diff --git a/TruePath.SystemIo/PathIo.cs b/TruePath.SystemIo/PathIo.cs index 4796ec6..3a196a0 100644 --- a/TruePath.SystemIo/PathIo.cs +++ b/TruePath.SystemIo/PathIo.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2025 .NET Foundation and Contributors +// SPDX-FileCopyrightText: 2025-2026 .NET Foundation and Contributors // SPDX-FileCopyrightText: 2025-2026 TruePath contributors // // SPDX-License-Identifier: MIT @@ -154,6 +154,274 @@ public static IEnumerable EnumerateFiles(this AbsolutePath path, s .Select(filename => path / filename); } + /// + /// Returns an enumerable collection of the absolute paths of the directories in a specified path. + /// + /// The directory to search. + /// is invalid, such as referring to an unmapped drive. + /// is a file name. + /// The specified path, file name, or combined exceed the system-defined maximum length. + /// The caller does not have the required permission. + /// The caller does not have the required permission. + /// An enumerable collection of the absolute paths for the directories in the directory specified by . + public static IEnumerable EnumerateDirectories(this AbsolutePath path) + { + return Directory.EnumerateDirectories(path.Value) + .Select(directory => path / directory); + } + + /// + /// Returns an enumerable collection of the absolute paths of the directories that match a search pattern in a specified path. + /// + /// The directory to search. + /// + /// + /// The search string to match against the names of directories in . It may combine + /// literal characters with the * and ? wildcards, but it does not support regular + /// expressions. This overload matches with Win32 semantics, where * stands for zero or more + /// characters and ? for exactly one, except immediately before a period or at the end of the + /// name, where it may also match none: a?.d matches both ab.d and a.d, while + /// ?ello.d matches only hello.d. + /// + /// + /// When running on .NET Framework on Windows, the pattern is also matched against the short (8.3) name of + /// each directory, if it has one. Since a short name keeps at most three characters of the extension, + /// *.htm may also return site.html (whose short name is SITE~1.HTM), and a pattern + /// such as *1* may match many directories with no 1 in their long names, through the + /// ~1 suffix of their short names. Whether a directory has a short name depends on the volume + /// settings at the time the directory was created, so the results may differ between machines and even + /// between directories in the same parent directory. See + /// Raymond Chen's explanation + /// for details. + /// + /// + /// For the full pattern syntax and its caveats, see + /// . + /// + /// + /// is . + /// does not contain a valid pattern. + /// is invalid, such as referring to an unmapped drive. + /// is a file name. + /// The specified path, file name, or combined exceed the system-defined maximum length. + /// The caller does not have the required permission. + /// The caller does not have the required permission. + /// An enumerable collection of the absolute paths for the directories in the directory specified by and that match the specified search pattern. + public static IEnumerable EnumerateDirectories(this AbsolutePath path, string searchPattern) + { + return Directory.EnumerateDirectories(path.Value, searchPattern) + .Select(directory => path / directory); + } + +#if NET8_0_OR_GREATER + /// + /// Returns an enumerable collection of the absolute paths of the directories that match a search pattern and enumeration options in a specified path. + /// + /// The directory to search. + /// + /// The search string to match against the names of directories in . It may combine + /// literal characters with the * (zero or more characters) and ? wildcards, but it does not + /// support regular expressions. Under the default , ? matches exactly one + /// character; under , selected through , it + /// may also match none immediately before a period or at the end of the name. For the full pattern syntax + /// and its caveats, see + /// . + /// + /// An object that describes the search and enumeration configuration to use. + /// is . + /// does not contain a valid pattern. + /// is invalid, such as referring to an unmapped drive. + /// is a file name. + /// The specified path, file name, or combined exceed the system-defined maximum length. + /// The caller does not have the required permission. + /// The caller does not have the required permission. + /// An enumerable collection of the absolute paths for the directories in the directory specified by and that match the specified search pattern and enumeration options. + public static IEnumerable EnumerateDirectories(this AbsolutePath path, string searchPattern, EnumerationOptions enumerationOptions) + { + return Directory.EnumerateDirectories(path.Value, searchPattern, enumerationOptions) + .Select(directory => path / directory); + } +#endif + + /// + /// Returns an enumerable collection of the absolute paths of the directories that match a search pattern in a specified path, and optionally searches subdirectories. + /// + /// The directory to search. + /// + /// + /// The search string to match against the names of directories in . It may combine + /// literal characters with the * and ? wildcards, but it does not support regular + /// expressions. This overload matches with Win32 semantics, where * stands for zero or more + /// characters and ? for exactly one, except immediately before a period or at the end of the + /// name, where it may also match none: a?.d matches both ab.d and a.d, while + /// ?ello.d matches only hello.d. + /// + /// + /// When running on .NET Framework on Windows, the pattern is also matched against the short (8.3) name of + /// each directory, if it has one. Since a short name keeps at most three characters of the extension, + /// *.htm may also return site.html (whose short name is SITE~1.HTM), and a pattern + /// such as *1* may match many directories with no 1 in their long names, through the + /// ~1 suffix of their short names. Whether a directory has a short name depends on the volume + /// settings at the time the directory was created, so the results may differ between machines and even + /// between directories in the same parent directory. See + /// Raymond Chen's explanation + /// for details. + /// + /// + /// For the full pattern syntax and its caveats, see + /// . + /// + /// + /// One of the enumeration values that specifies whether the search operation should include only the current directory or should include all subdirectories. + /// is . + /// does not contain a valid pattern. + /// is not a valid value. + /// is invalid, such as referring to an unmapped drive. + /// is a file name. + /// The specified path, file name, or combined exceed the system-defined maximum length. + /// The caller does not have the required permission. + /// The caller does not have the required permission. + /// An enumerable collection of the absolute paths for the directories in the directory specified by and that match the specified search pattern and search option. + public static IEnumerable EnumerateDirectories(this AbsolutePath path, string searchPattern, SearchOption searchOption) + { + return Directory.EnumerateDirectories(path.Value, searchPattern, searchOption) + .Select(directory => path / directory); + } + + /// + /// Returns an enumerable collection of the absolute paths of the files and directories in a specified path. + /// + /// The directory to search. + /// is invalid, such as referring to an unmapped drive. + /// is a file name. + /// The specified path, file name, or combined exceed the system-defined maximum length. + /// The caller does not have the required permission. + /// The caller does not have the required permission. + /// An enumerable collection of the absolute paths of the file-system entries in the directory specified by . + public static IEnumerable EnumerateFileSystemEntries(this AbsolutePath path) + { + return Directory.EnumerateFileSystemEntries(path.Value) + .Select(entry => path / entry); + } + + /// + /// Returns an enumerable collection of the absolute paths of the files and directories that match a search pattern in a specified path. + /// + /// The directory to search. + /// + /// + /// The search string to match against the names of file-system entries in . It may + /// combine literal characters with the * and ? wildcards, but it does not support regular + /// expressions. This overload matches with Win32 semantics, where * stands for zero or more + /// characters and ? for exactly one, except immediately before a period or at the end of the + /// name, where it may also match none: a?.txt matches both ab.txt and a.txt, + /// while ?ello.txt matches only hello.txt. + /// + /// + /// When running on .NET Framework on Windows, the pattern is also matched against the short (8.3) name of + /// each file or directory, if it has one. Since a short name keeps at most three characters of the + /// extension, *.htm may also return index.html (whose short name is INDEX~1.HTM), and + /// a pattern such as *1* may match many entries with no 1 in their long names, through the + /// ~1 suffix of their short names. Whether an entry has a short name depends on the volume settings + /// at the time the entry was created, so the results may differ between machines and even between + /// entries in the same directory. See + /// Raymond Chen's explanation + /// for details. + /// + /// + /// For the full pattern syntax and its caveats, see + /// . + /// + /// + /// is . + /// does not contain a valid pattern. + /// is invalid, such as referring to an unmapped drive. + /// is a file name. + /// The specified path, file name, or combined exceed the system-defined maximum length. + /// The caller does not have the required permission. + /// The caller does not have the required permission. + /// An enumerable collection of the absolute paths of the file-system entries in the directory specified by and that match the specified search pattern. + public static IEnumerable EnumerateFileSystemEntries(this AbsolutePath path, string searchPattern) + { + return Directory.EnumerateFileSystemEntries(path.Value, searchPattern) + .Select(entry => path / entry); + } + +#if NET8_0_OR_GREATER + /// + /// Returns an enumerable collection of the absolute paths of the files and directories that match a search pattern and enumeration options in a specified path. + /// + /// The directory to search. + /// + /// The search string to match against the names of file-system entries in . It may + /// combine literal characters with the * (zero or more characters) and ? wildcards, but it does + /// not support regular expressions. Under the default , ? matches exactly + /// one character; under , selected through , + /// it may also match none immediately before a period or at the end of the name. For the full pattern syntax + /// and its caveats, see + /// . + /// + /// An object that describes the search and enumeration configuration to use. + /// is . + /// does not contain a valid pattern. + /// is invalid, such as referring to an unmapped drive. + /// is a file name. + /// The specified path, file name, or combined exceed the system-defined maximum length. + /// The caller does not have the required permission. + /// The caller does not have the required permission. + /// An enumerable collection of the absolute paths of the file-system entries in the directory specified by , that match the specified search pattern and the specified enumeration options. + public static IEnumerable EnumerateFileSystemEntries(this AbsolutePath path, string searchPattern, EnumerationOptions enumerationOptions) + { + return Directory.EnumerateFileSystemEntries(path.Value, searchPattern, enumerationOptions) + .Select(entry => path / entry); + } +#endif + + /// + /// Returns an enumerable collection of the absolute paths of the files and directories that match a search pattern in a specified path, and optionally searches subdirectories. + /// + /// The directory to search. + /// + /// + /// The search string to match against the names of file-system entries in . It may + /// combine literal characters with the * and ? wildcards, but it does not support regular + /// expressions. This overload matches with Win32 semantics, where * stands for zero or more + /// characters and ? for exactly one, except immediately before a period or at the end of the + /// name, where it may also match none: a?.txt matches both ab.txt and a.txt, + /// while ?ello.txt matches only hello.txt. + /// + /// + /// When running on .NET Framework on Windows, the pattern is also matched against the short (8.3) name of + /// each file or directory, if it has one. Since a short name keeps at most three characters of the + /// extension, *.htm may also return index.html (whose short name is INDEX~1.HTM), and + /// a pattern such as *1* may match many entries with no 1 in their long names, through the + /// ~1 suffix of their short names. Whether an entry has a short name depends on the volume settings + /// at the time the entry was created, so the results may differ between machines and even between + /// entries in the same directory. See + /// Raymond Chen's explanation + /// for details. + /// + /// + /// For the full pattern syntax and its caveats, see + /// . + /// + /// + /// One of the enumeration values that specifies whether the search operation should include only the current directory or should include all subdirectories. + /// is . + /// does not contain a valid pattern. + /// is not a valid value. + /// is invalid, such as referring to an unmapped drive. + /// is a file name. + /// The specified path, file name, or combined exceed the system-defined maximum length. + /// The caller does not have the required permission. + /// The caller does not have the required permission. + /// An enumerable collection of the absolute paths of the file-system entries in the directory specified by and that match the specified search pattern and option. + public static IEnumerable EnumerateFileSystemEntries(this AbsolutePath path, string searchPattern, SearchOption searchOption) + { + return Directory.EnumerateFileSystemEntries(path.Value, searchPattern, searchOption) + .Select(entry => path / entry); + } + /// /// Returns the names of files (including their paths) that match the specified search pattern in the specified directory. /// @@ -170,13 +438,6 @@ public static AbsolutePath[] GetFiles(this AbsolutePath path, LocalPathPattern s // before nullable reference types existed, so the generator sees every reference type as oblivious and renders it // as non-nullable. The annotations here have to come from source rather than from the generator. - /// - /// Retrieves the parent directory of the specified path, including both absolute and relative paths. - /// - /// The path for which to retrieve the parent directory. - /// The parent directory, or if is the root directory. - public static DirectoryInfo? GetParent(this AbsolutePath path) => Directory.GetParent(path.Value); - // The BCL accepts null here (it writes an empty file or nothing at all), and the released API records that as // `string?`. diff --git a/TruePath.SystemIo/PublicAPI/net8.0/PublicAPI.Unshipped.txt b/TruePath.SystemIo/PublicAPI/net8.0/PublicAPI.Unshipped.txt index a47bc0a..4f753eb 100644 --- a/TruePath.SystemIo/PublicAPI/net8.0/PublicAPI.Unshipped.txt +++ b/TruePath.SystemIo/PublicAPI/net8.0/PublicAPI.Unshipped.txt @@ -3,24 +3,17 @@ static TruePath.SystemIo.PathIo.CreateDirectory(this TruePath.AbsolutePath path, static TruePath.SystemIo.PathIo.CreateSymbolicLink(this TruePath.AbsolutePath path, TruePath.AbsolutePath pathToTarget) -> System.IO.FileSystemInfo! static TruePath.SystemIo.PathIo.Decrypt(this TruePath.AbsolutePath path) -> void static TruePath.SystemIo.PathIo.Encrypt(this TruePath.AbsolutePath path) -> void -static TruePath.SystemIo.PathIo.EnumerateDirectories(this TruePath.AbsolutePath path) -> System.Collections.Generic.IEnumerable! -static TruePath.SystemIo.PathIo.EnumerateDirectories(this TruePath.AbsolutePath path, string! searchPattern) -> System.Collections.Generic.IEnumerable! -static TruePath.SystemIo.PathIo.EnumerateDirectories(this TruePath.AbsolutePath path, string! searchPattern, System.IO.EnumerationOptions! enumerationOptions) -> System.Collections.Generic.IEnumerable! -static TruePath.SystemIo.PathIo.EnumerateDirectories(this TruePath.AbsolutePath path, string! searchPattern, System.IO.SearchOption searchOption) -> System.Collections.Generic.IEnumerable! -static TruePath.SystemIo.PathIo.EnumerateFileSystemEntries(this TruePath.AbsolutePath path) -> System.Collections.Generic.IEnumerable! -static TruePath.SystemIo.PathIo.EnumerateFileSystemEntries(this TruePath.AbsolutePath path, string! searchPattern) -> System.Collections.Generic.IEnumerable! -static TruePath.SystemIo.PathIo.EnumerateFileSystemEntries(this TruePath.AbsolutePath path, string! searchPattern, System.IO.EnumerationOptions! enumerationOptions) -> System.Collections.Generic.IEnumerable! -static TruePath.SystemIo.PathIo.EnumerateFileSystemEntries(this TruePath.AbsolutePath path, string! searchPattern, System.IO.SearchOption searchOption) -> System.Collections.Generic.IEnumerable! -static TruePath.SystemIo.PathIo.GetDirectoryRoot(this TruePath.AbsolutePath path) -> string! -static TruePath.SystemIo.PathIo.GetFileSystemEntries(this TruePath.AbsolutePath path) -> string![]! -static TruePath.SystemIo.PathIo.GetFileSystemEntries(this TruePath.AbsolutePath path, string! searchPattern) -> string![]! -static TruePath.SystemIo.PathIo.GetFileSystemEntries(this TruePath.AbsolutePath path, string! searchPattern, System.IO.EnumerationOptions! enumerationOptions) -> string![]! -static TruePath.SystemIo.PathIo.GetFileSystemEntries(this TruePath.AbsolutePath path, string! searchPattern, System.IO.SearchOption searchOption) -> string![]! -static TruePath.SystemIo.PathIo.GetParent(this TruePath.AbsolutePath path) -> System.IO.DirectoryInfo? +static TruePath.SystemIo.PathIo.EnumerateDirectories(this TruePath.AbsolutePath path) -> System.Collections.Generic.IEnumerable! +static TruePath.SystemIo.PathIo.EnumerateDirectories(this TruePath.AbsolutePath path, string! searchPattern) -> System.Collections.Generic.IEnumerable! +static TruePath.SystemIo.PathIo.EnumerateDirectories(this TruePath.AbsolutePath path, string! searchPattern, System.IO.EnumerationOptions! enumerationOptions) -> System.Collections.Generic.IEnumerable! +static TruePath.SystemIo.PathIo.EnumerateDirectories(this TruePath.AbsolutePath path, string! searchPattern, System.IO.SearchOption searchOption) -> System.Collections.Generic.IEnumerable! +static TruePath.SystemIo.PathIo.EnumerateFileSystemEntries(this TruePath.AbsolutePath path) -> System.Collections.Generic.IEnumerable! +static TruePath.SystemIo.PathIo.EnumerateFileSystemEntries(this TruePath.AbsolutePath path, string! searchPattern) -> System.Collections.Generic.IEnumerable! +static TruePath.SystemIo.PathIo.EnumerateFileSystemEntries(this TruePath.AbsolutePath path, string! searchPattern, System.IO.EnumerationOptions! enumerationOptions) -> System.Collections.Generic.IEnumerable! +static TruePath.SystemIo.PathIo.EnumerateFileSystemEntries(this TruePath.AbsolutePath path, string! searchPattern, System.IO.SearchOption searchOption) -> System.Collections.Generic.IEnumerable! static TruePath.SystemIo.PathIo.Open(this TruePath.AbsolutePath path, System.IO.FileStreamOptions! options) -> System.IO.FileStream! static TruePath.SystemIo.PathIo.OpenHandle(this TruePath.AbsolutePath path, System.IO.FileMode mode = System.IO.FileMode.Open, System.IO.FileAccess access = System.IO.FileAccess.Read, System.IO.FileShare share = System.IO.FileShare.Read, System.IO.FileOptions options = System.IO.FileOptions.None, long preallocationSize = 0) -> Microsoft.Win32.SafeHandles.SafeFileHandle! static TruePath.SystemIo.PathIo.Replace(this TruePath.AbsolutePath sourceFile, TruePath.AbsolutePath destinationFile, TruePath.AbsolutePath destinationBackupFile) -> void static TruePath.SystemIo.PathIo.Replace(this TruePath.AbsolutePath sourceFile, TruePath.AbsolutePath destinationFile, TruePath.AbsolutePath destinationBackupFile, bool ignoreMetadataErrors) -> void static TruePath.SystemIo.PathIo.ResolveLinkTarget(this TruePath.AbsolutePath linkPath, bool returnFinalTarget) -> System.IO.FileSystemInfo? -static TruePath.SystemIo.PathIo.SetCurrentDirectory(this TruePath.AbsolutePath path) -> void static TruePath.SystemIo.PathIo.SetUnixFileMode(this TruePath.AbsolutePath path, System.IO.UnixFileMode mode) -> void diff --git a/TruePath.SystemIo/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt b/TruePath.SystemIo/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt index ec4638e..0d9ca79 100644 --- a/TruePath.SystemIo/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt +++ b/TruePath.SystemIo/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt @@ -1,17 +1,11 @@ #nullable enable static TruePath.SystemIo.PathIo.Decrypt(this TruePath.AbsolutePath path) -> void static TruePath.SystemIo.PathIo.Encrypt(this TruePath.AbsolutePath path) -> void -static TruePath.SystemIo.PathIo.EnumerateDirectories(this TruePath.AbsolutePath path) -> System.Collections.Generic.IEnumerable! -static TruePath.SystemIo.PathIo.EnumerateDirectories(this TruePath.AbsolutePath path, string! searchPattern) -> System.Collections.Generic.IEnumerable! -static TruePath.SystemIo.PathIo.EnumerateDirectories(this TruePath.AbsolutePath path, string! searchPattern, System.IO.SearchOption searchOption) -> System.Collections.Generic.IEnumerable! -static TruePath.SystemIo.PathIo.EnumerateFileSystemEntries(this TruePath.AbsolutePath path) -> System.Collections.Generic.IEnumerable! -static TruePath.SystemIo.PathIo.EnumerateFileSystemEntries(this TruePath.AbsolutePath path, string! searchPattern) -> System.Collections.Generic.IEnumerable! -static TruePath.SystemIo.PathIo.EnumerateFileSystemEntries(this TruePath.AbsolutePath path, string! searchPattern, System.IO.SearchOption searchOption) -> System.Collections.Generic.IEnumerable! -static TruePath.SystemIo.PathIo.GetDirectoryRoot(this TruePath.AbsolutePath path) -> string! -static TruePath.SystemIo.PathIo.GetFileSystemEntries(this TruePath.AbsolutePath path) -> string![]! -static TruePath.SystemIo.PathIo.GetFileSystemEntries(this TruePath.AbsolutePath path, string! searchPattern) -> string![]! -static TruePath.SystemIo.PathIo.GetFileSystemEntries(this TruePath.AbsolutePath path, string! searchPattern, System.IO.SearchOption searchOption) -> string![]! -static TruePath.SystemIo.PathIo.GetParent(this TruePath.AbsolutePath path) -> System.IO.DirectoryInfo? +static TruePath.SystemIo.PathIo.EnumerateDirectories(this TruePath.AbsolutePath path) -> System.Collections.Generic.IEnumerable! +static TruePath.SystemIo.PathIo.EnumerateDirectories(this TruePath.AbsolutePath path, string! searchPattern) -> System.Collections.Generic.IEnumerable! +static TruePath.SystemIo.PathIo.EnumerateDirectories(this TruePath.AbsolutePath path, string! searchPattern, System.IO.SearchOption searchOption) -> System.Collections.Generic.IEnumerable! +static TruePath.SystemIo.PathIo.EnumerateFileSystemEntries(this TruePath.AbsolutePath path) -> System.Collections.Generic.IEnumerable! +static TruePath.SystemIo.PathIo.EnumerateFileSystemEntries(this TruePath.AbsolutePath path, string! searchPattern) -> System.Collections.Generic.IEnumerable! +static TruePath.SystemIo.PathIo.EnumerateFileSystemEntries(this TruePath.AbsolutePath path, string! searchPattern, System.IO.SearchOption searchOption) -> System.Collections.Generic.IEnumerable! static TruePath.SystemIo.PathIo.Replace(this TruePath.AbsolutePath sourceFile, TruePath.AbsolutePath destinationFile, TruePath.AbsolutePath destinationBackupFile) -> void static TruePath.SystemIo.PathIo.Replace(this TruePath.AbsolutePath sourceFile, TruePath.AbsolutePath destinationFile, TruePath.AbsolutePath destinationBackupFile, bool ignoreMetadataErrors) -> void -static TruePath.SystemIo.PathIo.SetCurrentDirectory(this TruePath.AbsolutePath path) -> void diff --git a/TruePath.SystemIo/path-io-exclusions.txt b/TruePath.SystemIo/path-io-exclusions.txt index dc82ab6..ef2bf1a 100644 --- a/TruePath.SystemIo/path-io-exclusions.txt +++ b/TruePath.SystemIo/path-io-exclusions.txt @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 TruePath contributors +# +# SPDX-License-Identifier: MIT +# # BCL methods that must not be exposed as PathIo extension methods. # # One XML documentation ID per line, e.g.: @@ -10,4 +14,16 @@ # argument (e.g. DeleteEmptyDirectory/DeleteDirectoryRecursively forwarding to # Directory.Delete(string, bool)). # -# (No entries yet.) + +# Covered by the EnumerateFileSystemEntries extensions. +M:System.IO.Directory.GetFileSystemEntries(System.String) +M:System.IO.Directory.GetFileSystemEntries(System.String,System.String) +M:System.IO.Directory.GetFileSystemEntries(System.String,System.String,System.IO.EnumerationOptions) +M:System.IO.Directory.GetFileSystemEntries(System.String,System.String,System.IO.SearchOption) + +# Covered by the AbsolutePath.Parent and AbsolutePath.PathRoot properties. +M:System.IO.Directory.GetParent(System.String) +M:System.IO.Directory.GetDirectoryRoot(System.String) + +# Covered by the AbsolutePath.CurrentWorkingDirectory property. +M:System.IO.Directory.SetCurrentDirectory(System.String) diff --git a/TruePath.SystemIo/path-io-exclusions.txt.license b/TruePath.SystemIo/path-io-exclusions.txt.license deleted file mode 100644 index 611889c..0000000 --- a/TruePath.SystemIo/path-io-exclusions.txt.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2026 TruePath contributors - -SPDX-License-Identifier: MIT diff --git a/TruePath.Tests/PathIoCompletenessTests.cs b/TruePath.Tests/PathIoCompletenessTests.cs index 8c95697..a3c3323 100644 --- a/TruePath.Tests/PathIoCompletenessTests.cs +++ b/TruePath.Tests/PathIoCompletenessTests.cs @@ -83,7 +83,7 @@ private static string PathIoParameterType(ParameterInfo parameter) private static HashSet LoadExclusions() { - return File.ReadAllLines(Path.Combine(RepositoryRoot(), "TruePath.SystemIo", "path-io-exclusions.txt")) + return (RepositoryRoot() / "TruePath.SystemIo" / "path-io-exclusions.txt").ReadAllLines() .Select(line => line.Trim()) .Where(line => line.Length > 0 && !line.StartsWith("#", StringComparison.Ordinal)) .ToHashSet(); @@ -95,7 +95,7 @@ private static HashSet LoadExclusions() /// private static HashSet<(string TypeName, string MethodName, int ArgumentCount)> LoadForwardedCalls() { - var source = File.ReadAllText(Path.Combine(RepositoryRoot(), "TruePath.SystemIo", "PathIo.cs")); + var source = (RepositoryRoot() / "TruePath.SystemIo" / "PathIo.cs").ReadAllText(); var calls = new HashSet<(string, string, int)>(); foreach (Match match in Regex.Matches(source, @"\b(?File|Directory)\.(?\w+)\((?[^()]*)\)")) { @@ -108,14 +108,14 @@ private static HashSet LoadExclusions() return calls; } - private static string RepositoryRoot() + private static AbsolutePath RepositoryRoot() { - var directory = new DirectoryInfo(AppContext.BaseDirectory); - while (directory != null && !File.Exists(Path.Combine(directory.FullName, "TruePath.sln"))) - directory = directory.Parent; + AbsolutePath? directory = new AbsolutePath(AppContext.BaseDirectory); + while (directory != null && !(directory.Value / "TruePath.slnx").ExistsFile()) + directory = directory.Value.Parent; - if (directory == null) Assert.Skip("Repository root with TruePath.sln not found."); - return directory.FullName; + if (directory == null) Assert.Fail("Repository root with TruePath.slnx not found."); + return directory.Value; } private static string DocId(MethodInfo method) diff --git a/TruePath.Tests/PathIoTests.cs b/TruePath.Tests/PathIoTests.cs index d85debd..0a6d9f1 100644 --- a/TruePath.Tests/PathIoTests.cs +++ b/TruePath.Tests/PathIoTests.cs @@ -55,6 +55,46 @@ public void GetFilesByLocalPathPatternTest() }); } + [Fact] + public void EnumerateDirectoriesTest() + { + DoWithTempFolder(tempFolder => + { + var alpha = tempFolder / "alpha"; + var beta = tempFolder / "beta"; + var nested = alpha / "nested"; + nested.CreateDirectory(); + beta.CreateDirectory(); + (tempFolder / "file.txt").WriteAllText("test"); + + Assert.Equal([alpha, beta], tempFolder.EnumerateDirectories().OrderBy(x => x.Value)); + Assert.Equal([alpha], tempFolder.EnumerateDirectories("a*")); + Assert.Equal( + [alpha, nested, beta], + tempFolder.EnumerateDirectories("*", SearchOption.AllDirectories).OrderBy(x => x.Value)); + }); + } + + [Fact] + public void EnumerateFileSystemEntriesTest() + { + DoWithTempFolder(tempFolder => + { + var alpha = tempFolder / "alpha"; + var nested = alpha / "nested.txt"; + var beta = tempFolder / "beta.txt"; + alpha.CreateDirectory(); + nested.WriteAllText("test"); + beta.WriteAllText("test"); + + Assert.Equal([alpha, beta], tempFolder.EnumerateFileSystemEntries().OrderBy(x => x.Value)); + Assert.Equal([alpha], tempFolder.EnumerateFileSystemEntries("a*")); + Assert.Equal( + [alpha, nested, beta], + tempFolder.EnumerateFileSystemEntries("*", SearchOption.AllDirectories).OrderBy(x => x.Value)); + }); + } + private static void DoWithTempFolder(Action test) { var tempFolder = Temporary.CreateTempFolder(); diff --git a/TruePath.sln b/TruePath.sln deleted file mode 100644 index d343c17..0000000 --- a/TruePath.sln +++ /dev/null @@ -1,163 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31903.59 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4393F95C-25AD-46F1-AEEF-85259893CF58}" - ProjectSection(SolutionItems) = preProject - README.md = README.md - .gitignore = .gitignore - CONTRIBUTING.md = CONTRIBUTING.md - TruePath.sln.license = TruePath.sln.license - .editorconfig = .editorconfig - Directory.Build.props = Directory.Build.props - CHANGELOG.md = CHANGELOG.md - MAINTAINING.md = MAINTAINING.md - LICENSE.txt = LICENSE.txt - TruePath.sln.DotSettings = TruePath.sln.DotSettings - REUSE.toml = REUSE.toml - renovate.json = renovate.json - renovate.json.license = renovate.json.license - global.json = global.json - global.json.license = global.json.license - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{8C064598-EE83-463D-9822-BAFA70B2C278}" - ProjectSection(SolutionItems) = preProject - scripts\github-actions.fsx = scripts\github-actions.fsx - scripts\Test-Encoding.ps1 = scripts\Test-Encoding.ps1 - scripts\Get-Version.ps1 = scripts\Get-Version.ps1 - scripts\Test-PublicApiShipped.ps1 = scripts\Test-PublicApiShipped.ps1 - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{10D9133A-772F-4364-929E-EEAE2E5DF04D}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{55C1E2A5-1630-4C22-A403-65DA2B1B969D}" - ProjectSection(SolutionItems) = preProject - .github\workflows\main.yml = .github\workflows\main.yml - .github\workflows\release.yml = .github\workflows\release.yml - .github\workflows\docs.yml = .github\workflows\docs.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LICENSES", "LICENSES", "{0E6F7E51-BAE3-459C-A0E3-8082FA094F62}" - ProjectSection(SolutionItems) = preProject - LICENSES\MIT.txt = LICENSES\MIT.txt - LICENSES\CC0-1.0.txt = LICENSES\CC0-1.0.txt - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TruePath", "TruePath\TruePath.csproj", "{DB806921-D806-452A-99FB-94FC725291DC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TruePath.Tests", "TruePath.Tests\TruePath.Tests.csproj", "{7E3E01D3-19A2-4583-9895-E2AEDC3ED07A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TruePath.Benchmarks", "TruePath.Benchmarks\TruePath.Benchmarks.csproj", "{4FB7CAE4-241F-4DA8-ABC7-699BEAFBC637}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".config", ".config", "{33FCBEF9-2172-4755-8792-AEAF646DE77D}" - ProjectSection(SolutionItems) = preProject - .config\dotnet-tools.json = .config\dotnet-tools.json - .config\dotnet-tools.json.license = .config\dotnet-tools.json.license - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{98C65796-8ACB-4CAF-A06B-6A179F338E9C}" - ProjectSection(SolutionItems) = preProject - docs\toc.yml = docs\toc.yml - docs\docfx.json = docs\docfx.json - docs\index.md = docs\index.md - docs\docfx.json.license = docs\docfx.json.license - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TruePath.SystemIo", "TruePath.SystemIo\TruePath.SystemIo.csproj", "{46252949-EA57-49F5-BE71-2F387DA173E8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TruePath.PathIoGenerator", "TruePath.PathIoGenerator\TruePath.PathIoGenerator.csproj", "{48FE6B83-4F3B-442E-A651-4901DE88E111}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TruePath.PathIoGenerator.Tests", "TruePath.PathIoGenerator.Tests\TruePath.PathIoGenerator.Tests.csproj", "{01937DD8-3DE0-4262-9E16-2740E95F75D6}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DB806921-D806-452A-99FB-94FC725291DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DB806921-D806-452A-99FB-94FC725291DC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DB806921-D806-452A-99FB-94FC725291DC}.Debug|x64.ActiveCfg = Debug|Any CPU - {DB806921-D806-452A-99FB-94FC725291DC}.Debug|x64.Build.0 = Debug|Any CPU - {DB806921-D806-452A-99FB-94FC725291DC}.Debug|x86.ActiveCfg = Debug|Any CPU - {DB806921-D806-452A-99FB-94FC725291DC}.Debug|x86.Build.0 = Debug|Any CPU - {DB806921-D806-452A-99FB-94FC725291DC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DB806921-D806-452A-99FB-94FC725291DC}.Release|Any CPU.Build.0 = Release|Any CPU - {DB806921-D806-452A-99FB-94FC725291DC}.Release|x64.ActiveCfg = Release|Any CPU - {DB806921-D806-452A-99FB-94FC725291DC}.Release|x64.Build.0 = Release|Any CPU - {DB806921-D806-452A-99FB-94FC725291DC}.Release|x86.ActiveCfg = Release|Any CPU - {DB806921-D806-452A-99FB-94FC725291DC}.Release|x86.Build.0 = Release|Any CPU - {7E3E01D3-19A2-4583-9895-E2AEDC3ED07A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7E3E01D3-19A2-4583-9895-E2AEDC3ED07A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7E3E01D3-19A2-4583-9895-E2AEDC3ED07A}.Debug|x64.ActiveCfg = Debug|Any CPU - {7E3E01D3-19A2-4583-9895-E2AEDC3ED07A}.Debug|x64.Build.0 = Debug|Any CPU - {7E3E01D3-19A2-4583-9895-E2AEDC3ED07A}.Debug|x86.ActiveCfg = Debug|Any CPU - {7E3E01D3-19A2-4583-9895-E2AEDC3ED07A}.Debug|x86.Build.0 = Debug|Any CPU - {7E3E01D3-19A2-4583-9895-E2AEDC3ED07A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7E3E01D3-19A2-4583-9895-E2AEDC3ED07A}.Release|Any CPU.Build.0 = Release|Any CPU - {7E3E01D3-19A2-4583-9895-E2AEDC3ED07A}.Release|x64.ActiveCfg = Release|Any CPU - {7E3E01D3-19A2-4583-9895-E2AEDC3ED07A}.Release|x64.Build.0 = Release|Any CPU - {7E3E01D3-19A2-4583-9895-E2AEDC3ED07A}.Release|x86.ActiveCfg = Release|Any CPU - {7E3E01D3-19A2-4583-9895-E2AEDC3ED07A}.Release|x86.Build.0 = Release|Any CPU - {4FB7CAE4-241F-4DA8-ABC7-699BEAFBC637}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4FB7CAE4-241F-4DA8-ABC7-699BEAFBC637}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4FB7CAE4-241F-4DA8-ABC7-699BEAFBC637}.Debug|x64.ActiveCfg = Debug|Any CPU - {4FB7CAE4-241F-4DA8-ABC7-699BEAFBC637}.Debug|x64.Build.0 = Debug|Any CPU - {4FB7CAE4-241F-4DA8-ABC7-699BEAFBC637}.Debug|x86.ActiveCfg = Debug|Any CPU - {4FB7CAE4-241F-4DA8-ABC7-699BEAFBC637}.Debug|x86.Build.0 = Debug|Any CPU - {4FB7CAE4-241F-4DA8-ABC7-699BEAFBC637}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4FB7CAE4-241F-4DA8-ABC7-699BEAFBC637}.Release|Any CPU.Build.0 = Release|Any CPU - {4FB7CAE4-241F-4DA8-ABC7-699BEAFBC637}.Release|x64.ActiveCfg = Release|Any CPU - {4FB7CAE4-241F-4DA8-ABC7-699BEAFBC637}.Release|x64.Build.0 = Release|Any CPU - {4FB7CAE4-241F-4DA8-ABC7-699BEAFBC637}.Release|x86.ActiveCfg = Release|Any CPU - {4FB7CAE4-241F-4DA8-ABC7-699BEAFBC637}.Release|x86.Build.0 = Release|Any CPU - {46252949-EA57-49F5-BE71-2F387DA173E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {46252949-EA57-49F5-BE71-2F387DA173E8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {46252949-EA57-49F5-BE71-2F387DA173E8}.Debug|x64.ActiveCfg = Debug|Any CPU - {46252949-EA57-49F5-BE71-2F387DA173E8}.Debug|x64.Build.0 = Debug|Any CPU - {46252949-EA57-49F5-BE71-2F387DA173E8}.Debug|x86.ActiveCfg = Debug|Any CPU - {46252949-EA57-49F5-BE71-2F387DA173E8}.Debug|x86.Build.0 = Debug|Any CPU - {46252949-EA57-49F5-BE71-2F387DA173E8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {46252949-EA57-49F5-BE71-2F387DA173E8}.Release|Any CPU.Build.0 = Release|Any CPU - {46252949-EA57-49F5-BE71-2F387DA173E8}.Release|x64.ActiveCfg = Release|Any CPU - {46252949-EA57-49F5-BE71-2F387DA173E8}.Release|x64.Build.0 = Release|Any CPU - {46252949-EA57-49F5-BE71-2F387DA173E8}.Release|x86.ActiveCfg = Release|Any CPU - {46252949-EA57-49F5-BE71-2F387DA173E8}.Release|x86.Build.0 = Release|Any CPU - {48FE6B83-4F3B-442E-A651-4901DE88E111}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {48FE6B83-4F3B-442E-A651-4901DE88E111}.Debug|Any CPU.Build.0 = Debug|Any CPU - {48FE6B83-4F3B-442E-A651-4901DE88E111}.Debug|x64.ActiveCfg = Debug|Any CPU - {48FE6B83-4F3B-442E-A651-4901DE88E111}.Debug|x64.Build.0 = Debug|Any CPU - {48FE6B83-4F3B-442E-A651-4901DE88E111}.Debug|x86.ActiveCfg = Debug|Any CPU - {48FE6B83-4F3B-442E-A651-4901DE88E111}.Debug|x86.Build.0 = Debug|Any CPU - {48FE6B83-4F3B-442E-A651-4901DE88E111}.Release|Any CPU.ActiveCfg = Release|Any CPU - {48FE6B83-4F3B-442E-A651-4901DE88E111}.Release|Any CPU.Build.0 = Release|Any CPU - {48FE6B83-4F3B-442E-A651-4901DE88E111}.Release|x64.ActiveCfg = Release|Any CPU - {48FE6B83-4F3B-442E-A651-4901DE88E111}.Release|x64.Build.0 = Release|Any CPU - {48FE6B83-4F3B-442E-A651-4901DE88E111}.Release|x86.ActiveCfg = Release|Any CPU - {48FE6B83-4F3B-442E-A651-4901DE88E111}.Release|x86.Build.0 = Release|Any CPU - {01937DD8-3DE0-4262-9E16-2740E95F75D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {01937DD8-3DE0-4262-9E16-2740E95F75D6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {01937DD8-3DE0-4262-9E16-2740E95F75D6}.Debug|x64.ActiveCfg = Debug|Any CPU - {01937DD8-3DE0-4262-9E16-2740E95F75D6}.Debug|x64.Build.0 = Debug|Any CPU - {01937DD8-3DE0-4262-9E16-2740E95F75D6}.Debug|x86.ActiveCfg = Debug|Any CPU - {01937DD8-3DE0-4262-9E16-2740E95F75D6}.Debug|x86.Build.0 = Debug|Any CPU - {01937DD8-3DE0-4262-9E16-2740E95F75D6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {01937DD8-3DE0-4262-9E16-2740E95F75D6}.Release|Any CPU.Build.0 = Release|Any CPU - {01937DD8-3DE0-4262-9E16-2740E95F75D6}.Release|x64.ActiveCfg = Release|Any CPU - {01937DD8-3DE0-4262-9E16-2740E95F75D6}.Release|x64.Build.0 = Release|Any CPU - {01937DD8-3DE0-4262-9E16-2740E95F75D6}.Release|x86.ActiveCfg = Release|Any CPU - {01937DD8-3DE0-4262-9E16-2740E95F75D6}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {55C1E2A5-1630-4C22-A403-65DA2B1B969D} = {10D9133A-772F-4364-929E-EEAE2E5DF04D} - EndGlobalSection -EndGlobal diff --git a/TruePath.sln.DotSettings b/TruePath.sln.DotSettings deleted file mode 100644 index e9e707c..0000000 --- a/TruePath.sln.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - ShowAndRun \ No newline at end of file diff --git a/TruePath.slnx b/TruePath.slnx new file mode 100644 index 0000000..3807221 --- /dev/null +++ b/TruePath.slnx @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TruePath.slnx.DotSettings b/TruePath.slnx.DotSettings new file mode 100644 index 0000000..9486956 --- /dev/null +++ b/TruePath.slnx.DotSettings @@ -0,0 +1,4 @@ + + ShowAndRun diff --git a/TruePath.sln.license b/TruePath.slnx.license similarity index 100% rename from TruePath.sln.license rename to TruePath.slnx.license