fix case where prefix ends with a path separator
This commit is contained in:
parent
b99dbf53bd
commit
303b3924ae
|
@ -151,6 +151,12 @@ namespace SMAPI.Tests.Core
|
||||||
|
|
||||||
// with locale codes
|
// with locale codes
|
||||||
[TestCase("Data/Achievements.fr-FR", "Data/Achievements", ExpectedResult = true)]
|
[TestCase("Data/Achievements.fr-FR", "Data/Achievements", ExpectedResult = true)]
|
||||||
|
|
||||||
|
// prefix ends with path separator
|
||||||
|
[TestCase("Data/Events/Boop", "Data/Events/", ExpectedResult = true)]
|
||||||
|
[TestCase("Data/Events/Boop", "Data/Events\\", ExpectedResult = true)]
|
||||||
|
[TestCase("Data/Events", "Data/Events/", ExpectedResult = false)]
|
||||||
|
[TestCase("Data/Events", "Data/Events\\", ExpectedResult = false)]
|
||||||
public bool StartsWith_SimpleCases(string mainAssetName, string prefix)
|
public bool StartsWith_SimpleCases(string mainAssetName, string prefix)
|
||||||
{
|
{
|
||||||
// arrange
|
// arrange
|
||||||
|
@ -247,7 +253,7 @@ namespace SMAPI.Tests.Core
|
||||||
[TestCase("Mods/SomeMod/SomeSubdirectory", "Mods/Some", false, ExpectedResult = false)]
|
[TestCase("Mods/SomeMod/SomeSubdirectory", "Mods/Some", false, ExpectedResult = false)]
|
||||||
[TestCase("Mods/Jasper/Data", "Mods/Jas/Image", true, ExpectedResult = false)]
|
[TestCase("Mods/Jasper/Data", "Mods/Jas/Image", true, ExpectedResult = false)]
|
||||||
[TestCase("Mods/Jasper/Data", "Mods/Jas/Image", true, ExpectedResult = false)]
|
[TestCase("Mods/Jasper/Data", "Mods/Jas/Image", true, ExpectedResult = false)]
|
||||||
public bool StartsWith_SubfolderWithPartial(string mainAssetName, string otherAssetName, bool allowSubfolder)
|
public bool StartsWith_PartialMatchInPathSegment(string mainAssetName, string otherAssetName, bool allowSubfolder)
|
||||||
{
|
{
|
||||||
// arrange
|
// arrange
|
||||||
mainAssetName = PathUtilities.NormalizeAssetName(mainAssetName);
|
mainAssetName = PathUtilities.NormalizeAssetName(mainAssetName);
|
||||||
|
|
|
@ -138,37 +138,38 @@ namespace StardewModdingAPI.Framework.Content
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// get initial values
|
// get initial values
|
||||||
ReadOnlySpan<char> trimmed = prefix.AsSpan().Trim();
|
ReadOnlySpan<char> trimmedPrefix = prefix.AsSpan().Trim();
|
||||||
if (trimmed.Length == 0)
|
if (trimmedPrefix.Length == 0)
|
||||||
return true;
|
return true;
|
||||||
ReadOnlySpan<char> pathSeparators = new(ToolkitPathUtilities.PossiblePathSeparators); // just to simplify calling other span APIs
|
ReadOnlySpan<char> pathSeparators = new(ToolkitPathUtilities.PossiblePathSeparators); // just to simplify calling other span APIs
|
||||||
|
|
||||||
// asset keys can't have a leading slash, but AssetPathYielder will trim them
|
// asset keys can't have a leading slash, but AssetPathYielder will trim them
|
||||||
if (pathSeparators.Contains(trimmed[0]))
|
if (pathSeparators.Contains(trimmedPrefix[0]))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// compare segments
|
// compare segments
|
||||||
AssetNamePartEnumerator curParts = new(this.Name);
|
AssetNamePartEnumerator curParts = new(this.Name);
|
||||||
AssetNamePartEnumerator prefixParts = new(trimmed);
|
AssetNamePartEnumerator prefixParts = new(trimmedPrefix);
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
bool curHasMore = curParts.MoveNext();
|
bool curHasMore = curParts.MoveNext();
|
||||||
bool prefixHasMore = prefixParts.MoveNext();
|
bool prefixHasMore = prefixParts.MoveNext();
|
||||||
|
|
||||||
// reached end of prefix or asset name
|
// reached end for one side
|
||||||
if (prefixHasMore != curHasMore)
|
if (prefixHasMore != curHasMore)
|
||||||
{
|
{
|
||||||
// mismatch: prefix is longer
|
// mismatch: prefix is longer
|
||||||
if (prefixHasMore)
|
if (prefixHasMore)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// possible match: all prefix segments matched.
|
// match if subfolder paths are fine (e.g. prefix 'Data/Events' with target 'Data/Events/Beach')
|
||||||
return allowSubfolder || (pathSeparators.Contains(trimmed[^1]) ? curParts.Remainder.Length == 0 : curParts.Current.Length == 0);
|
return allowSubfolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
// match: previous segments matched exactly and both reached the end
|
// previous segments matched exactly and both reached the end
|
||||||
|
// match if prefix doesn't end with '/' (which should only match subfolders)
|
||||||
if (!prefixHasMore)
|
if (!prefixHasMore)
|
||||||
return true;
|
return !pathSeparators.Contains(trimmedPrefix[^1]);
|
||||||
|
|
||||||
// compare segment
|
// compare segment
|
||||||
if (curParts.Current.Length == prefixParts.Current.Length)
|
if (curParts.Current.Length == prefixParts.Current.Length)
|
||||||
|
@ -188,7 +189,7 @@ namespace StardewModdingAPI.Framework.Content
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// mismatch: something like "Maps/" would need an exact match
|
// mismatch: something like "Maps/" would need an exact match
|
||||||
if (pathSeparators.Contains(trimmed[^1]))
|
if (pathSeparators.Contains(trimmedPrefix[^1]))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// mismatch: partial word match not allowed, and the first or last letter of the suffix isn't a word separator
|
// mismatch: partial word match not allowed, and the first or last letter of the suffix isn't a word separator
|
||||||
|
@ -196,7 +197,7 @@ namespace StardewModdingAPI.Framework.Content
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// possible match
|
// possible match
|
||||||
return allowSubfolder || (pathSeparators.Contains(trimmed[^1]) ? curParts.Remainder.IndexOfAny(ToolkitPathUtilities.PossiblePathSeparators) < 0 : curParts.Remainder.Length == 0);
|
return allowSubfolder || (pathSeparators.Contains(trimmedPrefix[^1]) ? curParts.Remainder.IndexOfAny(ToolkitPathUtilities.PossiblePathSeparators) < 0 : curParts.Remainder.Length == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue