Disc subpaths are a subset, basically

This commit is contained in:
Matt Nadareski
2026-06-14 18:49:24 -04:00
parent 4b48f52a91
commit dde1faf584
14 changed files with 242 additions and 402 deletions

View File

@@ -208,6 +208,113 @@ namespace SabreTools.RedumpLib.Test.Data
#endregion
#region Disc Subpath
/// <summary>
/// Check that every DiscSubpath has a long name provided
/// </summary>
/// <param name="discSubpath">DiscSubpath value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateDiscSubpathTestData))]
public void DiscSubpath_LongName(DiscSubpath? discSubpath, bool expectNull)
{
var actual = discSubpath.LongName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Check that every DiscSubpath has a short name provided
/// </summary>
/// <param name="discSubpath">DiscSubpath value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateDiscSubpathTestData))]
public void DiscSubpath_ShortName(DiscSubpath? discSubpath, bool expectNull)
{
var actual = discSubpath.ShortName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Ensure that every DiscSubpath that has a short name that is unique
/// </summary>
[Fact]
public void DiscSubpath_ShortName_NoDuplicates()
{
var fullDiscSubpaths = Enum.GetValues<DiscSubpath>().Cast<DiscSubpath>().ToList();
var filteredDiscSubpaths = new Dictionary<string, DiscSubpath?>();
int totalCount = 0;
foreach (DiscSubpath? discSubpath in fullDiscSubpaths)
{
var code = discSubpath.ShortName();
if (string.IsNullOrEmpty(code))
continue;
// Throw if the code already exists
if (filteredDiscSubpaths.ContainsKey(code))
throw new DuplicateNameException($"Code {code} already in dictionary");
filteredDiscSubpaths[code] = discSubpath;
totalCount++;
}
Assert.Equal(totalCount, filteredDiscSubpaths.Count);
}
/// <summary>
/// Check that every DiscSubpath can be mapped from a string
/// </summary>
/// <param name="discSubpath">DiscSubpath value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateDiscSubpathTestData))]
public void DiscSubpath_ToDiscSubpath(DiscSubpath? discSubpath, bool expectNull)
{
string? longName = discSubpath.LongName();
string? longNameSpaceless = longName?.Replace(" ", string.Empty);
var actualNormal = longName.ToDiscSubpath();
var actualSpaceless = longNameSpaceless.ToDiscSubpath();
if (expectNull)
{
Assert.Null(actualNormal);
Assert.Null(actualSpaceless);
}
else
{
Assert.Equal(discSubpath, actualNormal);
Assert.Equal(discSubpath, actualSpaceless);
}
}
/// <summary>
/// Generate a test set of DiscSubpath values
/// </summary>
/// <returns>MemberData-compatible list of DiscSubpath values</returns>
public static TheoryData<DiscSubpath?, bool> GenerateDiscSubpathTestData()
{
var testData = new TheoryData<DiscSubpath?, bool>() { { null, true } };
foreach (DiscSubpath? discSubpath in Enum.GetValues<DiscSubpath>().Cast<DiscSubpath?>())
{
testData.Add(discSubpath, false);
}
return testData;
}
#endregion
#region Language
/// <summary>

View File

@@ -10,113 +10,6 @@ namespace SabreTools.RedumpLib.Test.RedumpInfo
{
public class ExtensionsTests
{
#region Disc Subpath
/// <summary>
/// Check that every DiscSubpath has a long name provided
/// </summary>
/// <param name="discSubpath">DiscSubpath value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateDiscSubpathTestData))]
public void DiscSubpath_LongName(DiscSubpath? discSubpath, bool expectNull)
{
var actual = discSubpath.LongName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Check that every DiscSubpath has a short name provided
/// </summary>
/// <param name="discSubpath">DiscSubpath value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateDiscSubpathTestData))]
public void DiscSubpath_ShortName(DiscSubpath? discSubpath, bool expectNull)
{
var actual = discSubpath.ShortName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Ensure that every DiscSubpath that has a short name that is unique
/// </summary>
[Fact]
public void DiscSubpath_ShortName_NoDuplicates()
{
var fullDiscSubpaths = Enum.GetValues<DiscSubpath>().Cast<DiscSubpath>().ToList();
var filteredDiscSubpaths = new Dictionary<string, DiscSubpath?>();
int totalCount = 0;
foreach (DiscSubpath? discSubpath in fullDiscSubpaths)
{
var code = discSubpath.ShortName();
if (string.IsNullOrEmpty(code))
continue;
// Throw if the code already exists
if (filteredDiscSubpaths.ContainsKey(code))
throw new DuplicateNameException($"Code {code} already in dictionary");
filteredDiscSubpaths[code] = discSubpath;
totalCount++;
}
Assert.Equal(totalCount, filteredDiscSubpaths.Count);
}
/// <summary>
/// Check that every DiscSubpath can be mapped from a string
/// </summary>
/// <param name="discSubpath">DiscSubpath value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateDiscSubpathTestData))]
public void DiscSubpath_ToDiscSubpath(DiscSubpath? discSubpath, bool expectNull)
{
string? longName = discSubpath.LongName();
string? longNameSpaceless = longName?.Replace(" ", string.Empty);
var actualNormal = longName.ToDiscSubpath();
var actualSpaceless = longNameSpaceless.ToDiscSubpath();
if (expectNull)
{
Assert.Null(actualNormal);
Assert.Null(actualSpaceless);
}
else
{
Assert.Equal(discSubpath, actualNormal);
Assert.Equal(discSubpath, actualSpaceless);
}
}
/// <summary>
/// Generate a test set of DiscSubpath values
/// </summary>
/// <returns>MemberData-compatible list of DiscSubpath values</returns>
public static TheoryData<DiscSubpath?, bool> GenerateDiscSubpathTestData()
{
var testData = new TheoryData<DiscSubpath?, bool>() { { null, true } };
foreach (DiscSubpath? discSubpath in Enum.GetValues<DiscSubpath>().Cast<DiscSubpath?>())
{
testData.Add(discSubpath, false);
}
return testData;
}
#endregion
#region Dump Status
/// <summary>

View File

@@ -1,7 +1,6 @@
using System;
using SabreTools.RedumpLib.Data;
using SabreTools.RedumpLib.RedumpInfo;
using SabreTools.RedumpLib.RedumpInfo.Data;
using Xunit;
namespace SabreTools.RedumpLib.Test.RedumpInfo

View File

@@ -10,113 +10,6 @@ namespace SabreTools.RedumpLib.Test.RedumpOrg
{
public class ExtensionsTests
{
#region Disc Subpath
/// <summary>
/// Check that every DiscSubpath has a long name provided
/// </summary>
/// <param name="discSubpath">DiscSubpath value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateDiscSubpathTestData))]
public void DiscSubpath_LongName(DiscSubpath? discSubpath, bool expectNull)
{
var actual = discSubpath.LongName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Check that every DiscSubpath has a short name provided
/// </summary>
/// <param name="discSubpath">DiscSubpath value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateDiscSubpathTestData))]
public void DiscSubpath_ShortName(DiscSubpath? discSubpath, bool expectNull)
{
var actual = discSubpath.ShortName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Ensure that every DiscSubpath that has a short name that is unique
/// </summary>
[Fact]
public void DiscSubpath_ShortName_NoDuplicates()
{
var fullDiscSubpaths = Enum.GetValues<DiscSubpath>().Cast<DiscSubpath>().ToList();
var filteredDiscSubpaths = new Dictionary<string, DiscSubpath?>();
int totalCount = 0;
foreach (DiscSubpath? discSubpath in fullDiscSubpaths)
{
var code = discSubpath.ShortName();
if (string.IsNullOrEmpty(code))
continue;
// Throw if the code already exists
if (filteredDiscSubpaths.ContainsKey(code))
throw new DuplicateNameException($"Code {code} already in dictionary");
filteredDiscSubpaths[code] = discSubpath;
totalCount++;
}
Assert.Equal(totalCount, filteredDiscSubpaths.Count);
}
/// <summary>
/// Check that every DiscSubpath can be mapped from a string
/// </summary>
/// <param name="discSubpath">DiscSubpath value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateDiscSubpathTestData))]
public void DiscSubpath_ToDiscSubpath(DiscSubpath? discSubpath, bool expectNull)
{
string? longName = discSubpath.LongName();
string? longNameSpaceless = longName?.Replace(" ", string.Empty);
var actualNormal = longName.ToDiscSubpath();
var actualSpaceless = longNameSpaceless.ToDiscSubpath();
if (expectNull)
{
Assert.Null(actualNormal);
Assert.Null(actualSpaceless);
}
else
{
Assert.Equal(discSubpath, actualNormal);
Assert.Equal(discSubpath, actualSpaceless);
}
}
/// <summary>
/// Generate a test set of DiscSubpath values
/// </summary>
/// <returns>MemberData-compatible list of DiscSubpath values</returns>
public static TheoryData<DiscSubpath?, bool> GenerateDiscSubpathTestData()
{
var testData = new TheoryData<DiscSubpath?, bool>() { { null, true } };
foreach (DiscSubpath? discSubpath in Enum.GetValues<DiscSubpath>().Cast<DiscSubpath?>())
{
testData.Add(discSubpath, false);
}
return testData;
}
#endregion
#region Dump Status
/// <summary>

View File

@@ -1,7 +1,6 @@
using System;
using SabreTools.RedumpLib.Data;
using SabreTools.RedumpLib.RedumpOrg;
using SabreTools.RedumpLib.RedumpOrg.Data;
using Xunit;
namespace SabreTools.RedumpLib.Test.RedumpOrg

View File

@@ -41,6 +41,60 @@ namespace SabreTools.RedumpLib.Data
AddOns = 11,
}
/// <summary>
/// List of all disc subpaths
/// </summary>
public enum DiscSubpath
{
/// <remarks>Only in redump.org</remarks>
[HumanReadable(LongName = "Changes", ShortName = "changes")]
Changes,
[HumanReadable(LongName = "Cuesheet", ShortName = "cue")]
Cuesheet,
[HumanReadable(LongName = "Edit", ShortName = "edit")]
Edit,
/// <remarks>Only in redump.org</remarks>
[HumanReadable(LongName = "GDI", ShortName = "gdi")]
GDI,
/// <remarks>Only in redump.info</remarks>
// Placeholder for the linked queue history page, not an actual subpath
[HumanReadable(LongName = "History", ShortName = "history")]
History,
/// <remarks>Only in redump.org</remarks>
[HumanReadable(LongName = "Key", ShortName = "key")]
Key,
/// <remarks>Only in redump.org</remarks>
[HumanReadable(LongName = "LSD", ShortName = "lsd")]
LSD,
/// <remarks>Only in redump.org</remarks>
[HumanReadable(LongName = "MD5", ShortName = "md5")]
MD5,
/// <remarks>Only in redump.org</remarks>
[HumanReadable(LongName = "SBI", ShortName = "sbi")]
SBI,
/// <remarks>Only in redump.org</remarks>
[HumanReadable(LongName = "SFV", ShortName = "sfv")]
SFV,
/// <remarks>Only in redump.org</remarks>
[HumanReadable(LongName = "SHA-1", ShortName = "sha1")]
SHA1,
/// <remarks>Only in redump.org</remarks>
// Placeholder for the linked new disc page, not an actual subpath
[HumanReadable(Available = false, LongName = "WIP", ShortName = "wip")]
WIP,
}
/// <summary>
/// List of all disc langauges
/// </summary>

View File

@@ -934,6 +934,70 @@ namespace SabreTools.RedumpLib.Data
#endregion
#region Disc Subpath
/// <summary>
/// Get the human readable name for a DiscSubpath
/// </summary>
/// <param name="discSubpath"></param>
/// <returns></returns>
public static string? LongName(this DiscSubpath discSubpath)
=> AttributeHelper<DiscSubpath>.GetHumanReadableAttribute(discSubpath)?.LongName;
/// <summary>
/// Get the human readable name for a DiscSubpath
/// </summary>
/// <param name="discSubpath"></param>
/// <returns></returns>
public static string? LongName(this DiscSubpath? discSubpath)
=> AttributeHelper<DiscSubpath?>.GetHumanReadableAttribute(discSubpath)?.LongName;
/// <summary>
/// Get the URL path part for a DiscSubpath
/// </summary>
/// <param name="discSubpath"></param>
/// <returns></returns>
public static string? ShortName(this DiscSubpath discSubpath)
=> AttributeHelper<DiscSubpath>.GetHumanReadableAttribute(discSubpath)?.ShortName;
/// <summary>
/// Get the URL path part for a DiscSubpath
/// </summary>
/// <param name="discSubpath"></param>
/// <returns></returns>
public static string? ShortName(this DiscSubpath? discSubpath)
=> AttributeHelper<DiscSubpath?>.GetHumanReadableAttribute(discSubpath)?.ShortName;
/// <summary>
/// Get the Region enum value for a given string
/// </summary>
/// <param name="discSubpath">String value to convert</param>
/// <returns>Region represented by the string, if possible</returns>
public static DiscSubpath? ToDiscSubpath(this string? discSubpath)
{
// No value means no match
if (discSubpath is null || discSubpath.Length == 0)
return null;
discSubpath = discSubpath.ToLowerInvariant();
var discSubpaths = (DiscSubpath[])Enum.GetValues(typeof(DiscSubpath));
// Check short names
int index = Array.FindIndex(discSubpaths, s => discSubpath == s.ShortName()?.ToLowerInvariant());
if (index > -1)
return discSubpaths[index];
// Check long names
index = Array.FindIndex(discSubpaths, s => discSubpath == s.LongName()?.ToLowerInvariant()
|| discSubpath == s.LongName()?.Replace(" ", string.Empty)?.ToLowerInvariant());
if (index > -1)
return discSubpaths[index];
return null;
}
#endregion
#region Language
/// <summary>

View File

@@ -2,22 +2,6 @@ using SabreTools.RedumpLib.Attributes;
namespace SabreTools.RedumpLib.RedumpInfo.Data
{
/// <summary>
/// List of all disc subpaths
/// </summary>
public enum DiscSubpath
{
[HumanReadable(LongName = "Cuesheet", ShortName = "cue")]
Cuesheet,
[HumanReadable(LongName = "Edit", ShortName = "edit")]
Edit,
// Placeholder for the linked queue history page, not an actual subpath
[HumanReadable(LongName = "History", ShortName = "history")]
History,
}
/// <summary>
/// Dump status
/// </summary>

View File

@@ -8,70 +8,6 @@ namespace SabreTools.RedumpLib.RedumpInfo.Data
/// </summary>
public static class Extensions
{
#region Disc Subpath
/// <summary>
/// Get the human readable name for a DiscSubpath
/// </summary>
/// <param name="discSubpath"></param>
/// <returns></returns>
public static string? LongName(this DiscSubpath discSubpath)
=> AttributeHelper<DiscSubpath>.GetHumanReadableAttribute(discSubpath)?.LongName;
/// <summary>
/// Get the human readable name for a DiscSubpath
/// </summary>
/// <param name="discSubpath"></param>
/// <returns></returns>
public static string? LongName(this DiscSubpath? discSubpath)
=> AttributeHelper<DiscSubpath?>.GetHumanReadableAttribute(discSubpath)?.LongName;
/// <summary>
/// Get the URL path part for a DiscSubpath
/// </summary>
/// <param name="discSubpath"></param>
/// <returns></returns>
public static string? ShortName(this DiscSubpath discSubpath)
=> AttributeHelper<DiscSubpath>.GetHumanReadableAttribute(discSubpath)?.ShortName;
/// <summary>
/// Get the URL path part for a DiscSubpath
/// </summary>
/// <param name="discSubpath"></param>
/// <returns></returns>
public static string? ShortName(this DiscSubpath? discSubpath)
=> AttributeHelper<DiscSubpath?>.GetHumanReadableAttribute(discSubpath)?.ShortName;
/// <summary>
/// Get the Region enum value for a given string
/// </summary>
/// <param name="discSubpath">String value to convert</param>
/// <returns>Region represented by the string, if possible</returns>
public static DiscSubpath? ToDiscSubpath(this string? discSubpath)
{
// No value means no match
if (discSubpath is null || discSubpath.Length == 0)
return null;
discSubpath = discSubpath.ToLowerInvariant();
var discSubpaths = (DiscSubpath[])Enum.GetValues(typeof(DiscSubpath));
// Check short names
int index = Array.FindIndex(discSubpaths, s => discSubpath == s.ShortName()?.ToLowerInvariant());
if (index > -1)
return discSubpaths[index];
// Check long names
index = Array.FindIndex(discSubpaths, s => discSubpath == s.LongName()?.ToLowerInvariant()
|| discSubpath == s.LongName()?.Replace(" ", string.Empty)?.ToLowerInvariant());
if (index > -1)
return discSubpaths[index];
return null;
}
#endregion
#region Dump Status
/// <summary>

View File

@@ -114,6 +114,18 @@ namespace SabreTools.RedumpLib.RedumpInfo
sb.Append($"{subpath.ShortName()}/");
break;
// redump.org subpaths that don't have equivilent paths
case DiscSubpath.Changes:
case DiscSubpath.GDI:
case DiscSubpath.Key:
case DiscSubpath.LSD:
case DiscSubpath.MD5:
case DiscSubpath.SBI:
case DiscSubpath.SFV:
case DiscSubpath.SHA1:
case DiscSubpath.WIP:
break;
// History and null are invalid for a disc page
case DiscSubpath.History:
case null:

View File

@@ -1,4 +1,5 @@
using System.Text.RegularExpressions;
using SabreTools.RedumpLib.Data;
namespace SabreTools.RedumpLib.RedumpOrg.Data
{

View File

@@ -2,46 +2,6 @@ using SabreTools.RedumpLib.Attributes;
namespace SabreTools.RedumpLib.RedumpOrg.Data
{
/// <summary>
/// List of all disc subpaths
/// </summary>
public enum DiscSubpath
{
[HumanReadable(LongName = "Changes", ShortName = "changes")]
Changes,
[HumanReadable(LongName = "Cuesheet", ShortName = "cue")]
Cuesheet,
[HumanReadable(LongName = "Edit", ShortName = "edit")]
Edit,
[HumanReadable(LongName = "GDI", ShortName = "gdi")]
GDI,
[HumanReadable(LongName = "Key", ShortName = "key")]
Key,
[HumanReadable(LongName = "LSD", ShortName = "lsd")]
LSD,
[HumanReadable(LongName = "MD5", ShortName = "md5")]
MD5,
[HumanReadable(LongName = "SBI", ShortName = "sbi")]
SBI,
[HumanReadable(LongName = "SFV", ShortName = "sfv")]
SFV,
[HumanReadable(LongName = "SHA-1", ShortName = "sha1")]
SHA1,
// Placeholder for the linked new disc page, not an actual subpath
[HumanReadable(Available = false, LongName = "WIP", ShortName = "wip")]
WIP,
}
/// <summary>
/// Dump status
/// </summary>

View File

@@ -7,74 +7,9 @@ namespace SabreTools.RedumpLib.RedumpOrg.Data
/// <summary>
/// Information pertaining to Redump systems
/// </summary>
#pragma warning disable IDE0010
#pragma warning disable IDE0072
public static class Extensions
{
#region Disc Subpath
/// <summary>
/// Get the human readable name for a DiscSubpath
/// </summary>
/// <param name="discSubpath"></param>
/// <returns></returns>
public static string? LongName(this DiscSubpath discSubpath)
=> AttributeHelper<DiscSubpath>.GetHumanReadableAttribute(discSubpath)?.LongName;
/// <summary>
/// Get the human readable name for a DiscSubpath
/// </summary>
/// <param name="discSubpath"></param>
/// <returns></returns>
public static string? LongName(this DiscSubpath? discSubpath)
=> AttributeHelper<DiscSubpath?>.GetHumanReadableAttribute(discSubpath)?.LongName;
/// <summary>
/// Get the URL path part for a DiscSubpath
/// </summary>
/// <param name="discSubpath"></param>
/// <returns></returns>
public static string? ShortName(this DiscSubpath discSubpath)
=> AttributeHelper<DiscSubpath>.GetHumanReadableAttribute(discSubpath)?.ShortName;
/// <summary>
/// Get the URL path part for a DiscSubpath
/// </summary>
/// <param name="discSubpath"></param>
/// <returns></returns>
public static string? ShortName(this DiscSubpath? discSubpath)
=> AttributeHelper<DiscSubpath?>.GetHumanReadableAttribute(discSubpath)?.ShortName;
/// <summary>
/// Get the Region enum value for a given string
/// </summary>
/// <param name="discSubpath">String value to convert</param>
/// <returns>Region represented by the string, if possible</returns>
public static DiscSubpath? ToDiscSubpath(this string? discSubpath)
{
// No value means no match
if (discSubpath is null || discSubpath.Length == 0)
return null;
discSubpath = discSubpath.ToLowerInvariant();
var discSubpaths = (DiscSubpath[])Enum.GetValues(typeof(DiscSubpath));
// Check short names
int index = Array.FindIndex(discSubpaths, s => discSubpath == s.ShortName()?.ToLowerInvariant());
if (index > -1)
return discSubpaths[index];
// Check long names
index = Array.FindIndex(discSubpaths, s => discSubpath == s.LongName()?.ToLowerInvariant()
|| discSubpath == s.LongName()?.Replace(" ", string.Empty)?.ToLowerInvariant());
if (index > -1)
return discSubpaths[index];
return null;
}
#endregion
#region Dump Status
/// <summary>
@@ -500,6 +435,5 @@ namespace SabreTools.RedumpLib.RedumpOrg.Data
#endregion
}
#pragma warning restore IDE0010
#pragma warning restore IDE0072
}

View File

@@ -129,6 +129,10 @@ namespace SabreTools.RedumpLib.RedumpOrg
sb.Append($"{subpath.ShortName()}/");
break;
// redump.info subpaths that don't have equivilent paths
case DiscSubpath.History:
break;
// WIP and null are invalid for a disc page
case DiscSubpath.WIP:
case null: