TruePath 2.0.0
dotnet add package TruePath --version 2.0.0
NuGet\Install-Package TruePath -Version 2.0.0
<PackageReference Include="TruePath" Version="2.0.0" />
<PackageVersion Include="TruePath" Version="2.0.0" />
<PackageReference Include="TruePath" />
paket add TruePath --version 2.0.0
#r "nuget: TruePath, 2.0.0"
#:package TruePath@2.0.0
#addin nuget:?package=TruePath&version=2.0.0
#tool nuget:?package=TruePath&version=2.0.0
TruePath 
This is a library containing a set of types to work with file system paths in .NET.
Motivation
Historically, .NET has been lacking a good set of types to work with file system paths. The System.IO.Path class has a variety of methods that operate on path strings, but it doesn't provide any types to represent paths themselves. It's impossible to tell whether a method accepts an absolute path, a relative path, a file name, or something file-related at all, only looking at its signature: all these types are represented by plain strings. Also, comparing different paths is not straightforward.
This library aims to fill this gap by providing a set of types that represent paths in a strongly-typed way. Now, you can require a path in a method's parameters, and it is guaranteed that the passed path will be well-formed and will have certain properties.
Also, the methods in the library provide some qualities that are missing from the System.IO.Path: say, we aim to provide several ways of path normalization and comparison, the ones that will and will not perform disk IO to resolve paths on case-insensitive file systems.
The library is inspired by the path libraries used in other ecosystems, in no particular order:
- Java's java.nio.file.Path and Kotlin's extensions for general API shape;
- [fs.path.append] from the C++ standard for path concatenation algorithms.
Read more on the documentation site.
If you miss some feature or have questions, do not hesitate to open an issue or go to the discussions section.
Packages
| Name | NuGet Package | Documentation |
|---|---|---|
| TruePath | API Reference | |
| TruePath.SystemIo | API Reference | |
| TruePath.TestableIO.System.IO (third-party) | Documentation |
TruePath provides two NuGet packages:
- TruePath for the main path abstractions,
- TruePath.SystemIo for the
System.IOintegration.
A third-party package, TruePath.TestableIO.System.IO, adds TestableIO.System.IO.Abstractions integration on top of TruePath.
Versioning
This project follows Semantic Versioning, with one exception: the behavior of the types and members marked as [System.Diagnostics.CodeAnalysis.ExperimentalAttribute] may change without a major version bump. Using them causes the compiler to report diagnostic TRUEPATH001; suppress it (e.g. by adding <NoWarn>$(NoWarn);TRUEPATH001</NoWarn> into your project file, or with #pragma warning disable TRUEPATH001) to acknowledge that.
Documentation
License
The project is distributed under the terms of the MIT license.
The license indication in the project's sources is compliant with the REUSE specification v3.3.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.Bcl.Memory (>= 10.0.13)
-
net8.0
- No dependencies.
NuGet packages (13)
Showing the top 5 NuGet packages that depend on TruePath:
| Package | Downloads |
|---|---|
|
TruePath.SystemIo
Adapters to use System.IO.File and System.IO.Directory APIs together with TruePath. |
|
|
Generaptor
Package Description |
|
|
FileBasedApp.Toolkit
Opiniated helpers for a FileBaseApp. Based on TruePath, SimpleExec and Spectre.Console.Cli |
|
|
TruePath.TestableIO.System.IO
Bridges TruePath and TestableIO.System.IO Presents extension methods for the Testable.System.IO mapping to AbsoutePath (TruePath) |
|
|
Fenrir.Git
Fenrir is a .NET library to work with Git repositories. It provides functions to read Git objects, traverse the commit graph, extract trees and files from any commit, etc. Essentially, it provides tools to create your own Git client, or transform a repository in any way. |
GitHub repositories (1)
Showing the top 1 popular GitHub repositories that depend on TruePath:
| Repository | Stars |
|---|---|
|
ForNeVeR/Cesium
C compiler for the CLI platform
|
| Version | Downloads | Last Updated |
|---|---|---|
| 2.0.0 | 827 | 10/25/2026 |
| 1.12.0 | 33,836 | 3/14/2026 |
| 1.11.0 | 12,554 | 11/25/2025 |
| 1.10.0 | 7,197 | 8/4/2025 |
| 1.9.0 | 9,295 | 5/31/2025 |
| 1.8.0 | 390 | 5/31/2025 |
| 1.7.0 | 1,815 | 4/18/2025 |
| 1.6.0 | 3,859 | 10/6/2024 |
| 1.5.0 | 1,115 | 9/22/2024 |
| 1.4.0 | 918 | 8/11/2024 |
| 1.3.0 | 805 | 6/21/2024 |
| 1.2.1 | 465 | 5/25/2024 |
| 1.2.0 | 397 | 5/5/2024 |
| 1.1.0 | 470 | 4/27/2024 |
| 1.0.0 | 287 | 4/21/2024 |
| 0.0.0 | 256 | 4/20/2024 |
This is a major release with certain breaking changes. No public APIs have been broken, but the amount of minor behavior changes required us to bump the major version.
The main change (outlined below) is the introduction of the relative path kinds (most important for Windows). TruePath's path resolve APIs now follow the new rules regarding the relative path resolve (especially regarding more exotic relative path kinds on Windows); check the relevant documentation.
Another notable change is that, while LocalPath still considers an empty path to designate the current directory, current directory now always gets normalized as "." and not an empty string.
There were a bunch of unrelated important (sometimes breaking) changes to other APIs, read the details below.
[Changed]
- Breaking: (#224 (https://github.com/ForNeVeR/TruePath/issues/224)) on Windows, LocalPath.IsAbsolute now only returns true for fully qualified paths, such as C:\Windows. Paths rooted without a drive letter (\Windows), and paths relative to the current directory of a drive (C:Windows, C:) are not absolute anymore, and AbsolutePath now rejects them. Previously, this was following the Path.IsPathRooted API.
- Breaking: (#224 (https://github.com/ForNeVeR/TruePath/issues/224)) on Windows, the / operator of LocalPath and AbsolutePath no longer delegates to Path.Combine, and instead follows the algorithm of C++ std::filesystem::path::operator/ (fs.path.append (https://eel.is/c++draft/fs.path.append)) with case-insensitive drive letters. In particular, C:\base / \x is now C:\x (was \x), C:\base / C:x is now C:\base\x (was C:x), and C: / x is now C:x (was C:\x). On Unix, the behavior is unchanged.
- Breaking: path normalization now represents the current directory as . instead of an empty string: PathStrings.Normalize returns "." for "", "." and "a/..", and so do LocalPath.Value and ToString() (and LocalPath.Parent of a single-segment relative path is .). As a consequence, LocalPath.FileName of such a path is ., the same way it is .. for .., and GetExtensionWithDot returns . for it.
- Breaking: LocalPath.IsPrefixOf and StartsWith now consider paths of different PathKind unrelated: an absolute path is never a prefix of a relative one, nor the other way round, and on Windows \Windows, C:Windows and Windows are all unrelated to each other.
- Breaking: PathExtensions.WithExtension now throws an ArgumentException unless the path ends with a file name and the result of the change is a file name in its place: an extension change may no longer alter the number or the kind of any path's segments. It used to do so, e.g.:
- new LocalPath(@"C:\foo\.gitignore").WithExtension(null) returned C:\foo,
- new LocalPath(".gitignore").WithExtension(null) returned the empty path,
- and new LocalPath(@"C:\").WithExtension("bar") returned C:\.bar.
None of the examples above take place anymore.
- #224 (https://github.com/ForNeVeR/TruePath/issues/224): on Windows, AbsolutePath / LocalPath now resolves a path relative to the current directory of another drive against the current directory of that drive, so the result is always absolute: e.g. C:\base / D:x is D:\x if the current directory of drive D: is its root. Previously, the result was D:x, which is not absolute.
- #224 (https://github.com/ForNeVeR/TruePath/issues/224): on Windows, LocalPath.ResolveToCurrentDirectory now resolves \x against the drive of the current directory, and D:x against the current directory of drive D:.
- #240 (https://github.com/ForNeVeR/TruePath/issues/240): more performant AbsolutePath.ReadKind: it now queries the entry attributes only once, and on Windows it no longer allocates or performs additional IO in cases not concerning reparse points. Thanks to @FalconTechDev for help!
- AbsolutePath.ReadKind now only returns null if the entry doesn't exist, and throws an exception if its attributes cannot be read for another reason: e.g. an UnauthorizedAccessException if the access is denied, or an IOException if the path contains a name rejected by the file system (on .NET Framework, also ArgumentException or PathTooLongException). Previously, it returned null in these cases.
- The platform-default path comparers (LocalPath.PlatformDefaultComparer, AbsolutePath.PlatformDefaultComparer) are now case-insensitive on iOS and tvOS as well, matching the .NET runtime.
[Added]
- #215 (https://github.com/ForNeVeR/TruePath/issues/215): AbsolutePath.PathRoot property. Thanks to @fskalkan!
- LocalPath.PathRoot property, similar to the previous entry, returning the root of an absolute path, or the drive root of a drive-relative path (e.g. C:foo, which only exists on Windows, will return C:), or null when the root can't be determined.
- #202: path.GetFiles(LocalPathPattern) (https://github.com/ForNeVeR/TruePath/issues/202). Thanks to @hamdiakin!
- #224 (https://github.com/ForNeVeR/TruePath/issues/224): PathKind enumeration and LocalPath.Kind property, telling an absolute path from a relative one, and on Windows also from a path rooted without a drive letter (\Windows) and from a path relative to the current directory of a drive (C:Windows). These are experimental (diagnostic TRUEPATH001): more kinds may be added later, e.g. for UNC paths.
[Fixed]
- #239 (https://github.com/ForNeVeR/TruePath/issues/239): on Unix, AbsolutePath.Canonicalize() no longer closes the file descriptor 0 when the path doesn't exist. This used to close the standard input of the process, or any file or socket that later took this descriptor number. Also, it no longer leaks the memory allocated for the resolved path.
- #217 (https://github.com/ForNeVeR/TruePath/issues/217): the IsPrefixOf and StartsWith methods for both LocalPath and AbsolutePath are now exact opposites (use same code), and always operate on whole path segments (file and directory names) instead of raw strings. For example, new AbsolutePath("/foo1").StartsWith(new AbsolutePath("/foo")) is now false, where it used to be true. Thanks to @berkcantmr for help on this fix!
- #221 (https://github.com/ForNeVeR/TruePath/issues/221): IsPrefixOf now handles the root folders correctly as prefixes of all the absolute paths under the same root. Thanks to @berkcantmr and @t957095!
- IsPrefixOf and StartsWith now compare path strings using ordinal string comparison.
- #225 (https://github.com/ForNeVeR/TruePath/issues/225): IsPrefixOf and StartsWith now compare path strings using the same platform-default case sensitivity as path equality. Thanks to @amirani8137!
- #224 (https://github.com/ForNeVeR/TruePath/issues/224): path normalization no longer drops the root separator when a rooted path ends with ..: C:\base\.. is now normalized to C:\ (was C:, a path relative to the current directory of drive C:), and /base/.. to / (was an empty path, i.e. the current directory).
- #219 (https://github.com/ForNeVeR/TruePath/issues/219): path normalization no longer applies Windows drive-specific logic to all paths. Thanks to @hamdiakin!
- #95 (https://github.com/ForNeVeR/TruePath/issues/95): Path normalization now drops a .. directly after the root, since the root has no parent: /.. is normalized to /, and C:\..\x to C:\x, the same as Path.GetFullPath does. Thanks to @sinthu-14 for help!
- #226 (https://github.com/ForNeVeR/TruePath/issues/226): LocalPath.IsPrefixOf and StartsWith now take leading .. references of relative paths into account instead of comparing them as strings. For example, new LocalPath("..").IsPrefixOf(new LocalPath("foo")) is now true, and new LocalPath("..").IsPrefixOf(new LocalPath("../..")) is now false. Thanks to @rghvgrv!
- In .NET Standard 2.0's version of TruePath (e.g., the one targeting .NET Framework), LocalPath.RelativeTo and AbsolutePath.RelativeTo now use a port of the .NET runtime's Path.GetRelativePath, and return the same results as on .NET 8+ (except for the stream syntax, see #235 (https://github.com/ForNeVeR/TruePath/issues/235)). Previously, the result was wrong for a destination equal to or above the base path, and for names containing %XX sequences.
- AbsolutePath.ReadKind no longer throws an exception if the entry gets deleted during the call.
- LocalPath.RelativeTo no longer throws an exception when either path designates the current directory.