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 }