1 /** 2 Contains routines for high level path handling. 3 4 Copyright: © 2012-2021 Sönke Ludwig 5 License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 6 Authors: Sönke Ludwig 7 */ 8 module dub.internal.vibecompat.inet.path; 9 10 import dub.internal.vibecompat.inet.path2; 11 import std.traits : isInstanceOf; 12 import std.range.primitives : ElementType, isInputRange; 13 14 /// Represents a path on Windows operating systems. 15 alias WindowsPath = Normalized!(GenericPath!WindowsPathFormat); 16 17 /// Represents a path on Unix/Posix systems. 18 alias PosixPath = Normalized!(GenericPath!PosixPathFormat); 19 20 /// Represents a path as part of an URI. 21 alias InetPath = GenericPath!InetPathFormat; // No need for normalization 22 23 /// The path type native to the target operating system. 24 version (Windows) alias NativePath = WindowsPath; 25 else alias NativePath = PosixPath; 26 27 // GenericPath no longer normalize on `opBinary!"~"` since v2. We relied on this 28 // behavior heavily and this clutch is used to avoid a breaking change. 29 private struct Normalized (PType) { 30 @safe: 31 public alias PathType = PType; 32 33 private PathType data; 34 35 public this (string data) scope @safe pure { 36 this.data = PathType(data); 37 } 38 39 public this (PathType data) scope @safe pure nothrow @nogc { 40 this.data = data; 41 } 42 43 this(Segment segment) { this(PathType(segment)); } 44 45 /** Constructs a path from an input range of `Segment`s. 46 47 Throws: 48 Since path segments are pre-validated, this constructor does not 49 throw an exception. 50 */ 51 this(R)(R segments) 52 if (isInputRange!R && is(ElementType!R : Segment)) 53 { 54 this(PathType(segments)); 55 } 56 57 /// Append a path to this 58 Normalized opBinary(string op : "~")(string subpath) const { 59 return this ~ Normalized(subpath); 60 } 61 /// ditto 62 Normalized opBinary(string op : "~")(Segment subpath) const { 63 return this ~ Normalized(PathType(subpath)); 64 } 65 /// ditto 66 Normalized opBinary(string op : "~", OtherType)(Normalized!OtherType subpath) const { 67 auto result = this.data.opBinary!"~"(subpath.data); 68 result.normalize(); 69 return Normalized(result); 70 } 71 /// ditto 72 Normalized opBinary(string op : "~")(InetPath subpath) const { 73 auto result = this.data.opBinary!"~"(subpath); 74 result.normalize(); 75 return Normalized(result); 76 } 77 /// Appends a relative path to this path. 78 void opOpAssign(string op : "~", T)(T op) { this = this ~ op; } 79 80 P opCast(P : Normalized!(GenericPath!(Format)), Format)() const { 81 return P(this.data.opCast!(P.PathType)); 82 } 83 P opCast(P : InetPath)() const { 84 return this.data.opCast!(P); 85 } 86 87 // Just forward, `alias this` is hopeless 88 public alias Segment = PathType.Segment; 89 90 /// Tests if the path is represented by an empty string. 91 @property bool empty() const nothrow @nogc { return this.data.empty(); } 92 93 /// Tests if the path is absolute. 94 @property bool absolute() const nothrow @nogc { return this.data.absolute(); } 95 96 /// Determines whether the path ends with a path separator (i.e. represents a folder specifically). 97 @property bool endsWithSlash() const nothrow @nogc { return this.data.endsWithSlash(); } 98 /// ditto 99 @property void endsWithSlash(bool v) nothrow { this.data.endsWithSlash(v); } 100 101 /** Iterates over the individual segments of the path. 102 103 Returns a forward range of `Segment`s. 104 */ 105 @property auto bySegment() const { return this.data.bySegment(); } 106 107 /// 108 string toString() const nothrow @nogc { return this.data.toString(); } 109 110 /// Computes a hash sum, enabling storage within associative arrays. 111 size_t toHash() const nothrow @trusted { return this.data.toHash(); } 112 113 /** Compares two path objects. 114 115 Note that the exact string representation of the two paths will be 116 compared. To get a basic semantic comparison, the paths must be 117 normalized first. 118 */ 119 bool opEquals(Normalized other) const @nogc { return this.data.opEquals(other.data); } 120 121 /// 122 @property Segment head() const @nogc { return this.data.head(); } 123 124 /// 125 @property bool hasParentPath() const @nogc { return this.data.hasParentPath(); } 126 127 /// 128 @property Normalized parentPath() const @nogc { return Normalized(this.data.parentPath()); } 129 130 /// 131 void normalize() { return this.data.normalize(); } 132 133 /// 134 Normalized normalized() const { return Normalized(this.data.normalized()); } 135 136 /// 137 bool startsWith(Normalized prefix) const nothrow { return this.data.startsWith(prefix.data); } 138 139 /// 140 static Normalized fromTrustedString(string p) nothrow @nogc { 141 return Normalized(PathType.fromTrustedString(p)); 142 } 143 } 144 145 Path relativeTo(Path)(in Path path, in Path base_path) @safe 146 if (isInstanceOf!(Normalized, Path)) 147 { 148 return Path(dub.internal.vibecompat.inet.path2.relativeTo(path.data, base_path.data)); 149 } 150 151 /** Converts a path to its system native string representation. 152 */ 153 string toNativeString(T)(Normalized!T path) 154 { 155 return (cast(NativePath)path).toString(); 156 }