@@ -90,7 +90,7 @@ public struct AbsolutePath: Hashable, Sendable {
9090
9191 self . init ( String ( decodingCString: pwszResult, as: UTF16 . self) )
9292#else
93- self . init ( basePath, RelativePath ( str) )
93+ try self . init ( basePath, RelativePath ( validating : str) )
9494#endif
9595 }
9696 }
@@ -102,8 +102,8 @@ public struct AbsolutePath: Hashable, Sendable {
102102 }
103103
104104 /// Convenience initializer that appends a string to a relative path.
105- public init ( _ absPath: AbsolutePath , _ relStr: String ) {
106- self . init ( absPath, RelativePath ( relStr) )
105+ public init ( _ absPath: AbsolutePath , validating relStr: String ) throws {
106+ try self . init ( absPath, RelativePath ( validating : relStr) )
107107 }
108108
109109 /// Initializes the AbsolutePath from `absStr`, which must be an absolute
@@ -240,23 +240,6 @@ public struct RelativePath: Hashable, Sendable {
240240 _impl = impl
241241 }
242242
243- /// Private initializer for constructing a relative path without performing
244- /// normalization or canonicalization. This will construct a path without
245- /// an anchor and thus may be invalid.
246- fileprivate init ( unsafeUncheckedPath string: String ) {
247- self . init ( PathImpl ( string: string) )
248- }
249-
250- /// Initializes the RelativePath from `str`, which must be a relative path
251- /// (which means that it must not begin with a path separator or a tilde).
252- /// An empty input path is allowed, but will be normalized to a single `.`
253- /// character. The input string will be normalized if needed, as described
254- /// in the documentation for RelativePath.
255- public init ( _ string: String ) {
256- // Normalize the relative string and store it as our Path.
257- self . init ( PathImpl ( normalizingRelativePath: string) )
258- }
259-
260243 /// Convenience initializer that verifies that the path is relative.
261244 public init ( validating path: String ) throws {
262245 try self . init ( PathImpl ( validatingRelativePath: path) )
@@ -429,12 +412,6 @@ protocol Path: Hashable {
429412 /// Creates a path from its normalized string representation.
430413 init ( string: String )
431414
432- /// Creates a path from an absolute string representation and normalizes it.
433- init ( normalizingAbsolutePath: String )
434-
435- /// Creates a path from an relative string representation and normalizes it.
436- init ( normalizingRelativePath: String )
437-
438415 /// Creates a path from a string representation, validates that it is a valid absolute path and normalizes it.
439416 init ( validatingAbsolutePath: String ) throws
440417
@@ -534,34 +511,12 @@ private struct WindowsPath: Path, Sendable {
534511 return String ( cString: representation)
535512 }
536513
537- init ( normalizingAbsolutePath path: String ) {
538- self . init ( string: Self . repr ( path) . withCString ( encodedAs: UTF16 . self) { pwszPath in
539- var canonical : PWSTR !
540- _ = PathAllocCanonicalize ( pwszPath,
541- ULONG ( PATHCCH_ALLOW_LONG_PATHS . rawValue) ,
542- & canonical)
543- return String ( decodingCString: canonical, as: UTF16 . self)
544- } )
545- }
546-
547514 init ( validatingAbsolutePath path: String ) throws {
548515 let realpath = Self . repr ( path)
549516 if !Self. isAbsolutePath ( realpath) {
550517 throw PathValidationError . invalidAbsolutePath ( path)
551518 }
552- self . init ( normalizingAbsolutePath: path)
553- }
554-
555- init ( normalizingRelativePath path: String ) {
556- if path. isEmpty || path == " . " {
557- self . init ( string: " . " )
558- } else {
559- var buffer : [ WCHAR ] = Array < WCHAR > ( repeating: 0 , count: Int ( MAX_PATH + 1 ) )
560- _ = path. replacingOccurrences ( of: " / " , with: " \\ " ) . withCString ( encodedAs: UTF16 . self) {
561- PathCanonicalizeW ( & buffer, $0)
562- }
563- self . init ( string: String ( decodingCString: buffer, as: UTF16 . self) )
564- }
519+ self . init ( string: realpath)
565520 }
566521
567522 init ( validatingRelativePath path: String ) throws {
@@ -570,11 +525,10 @@ private struct WindowsPath: Path, Sendable {
570525 } else {
571526 let realpath : String = Self . repr ( path)
572527 // Treat a relative path as an invalid relative path...
573- if Self . isAbsolutePath ( realpath) ||
574- realpath. first == " ~ " || realpath. first == " \\ " {
528+ if Self . isAbsolutePath ( realpath) || realpath. first == " \\ " {
575529 throw PathValidationError . invalidRelativePath ( path)
576530 }
577- self . init ( normalizingRelativePath : path )
531+ self . init ( string : realpath )
578532 }
579533 }
580534
@@ -597,7 +551,7 @@ private struct WindowsPath: Path, Sendable {
597551 }
598552 }
599553 defer { LocalFree ( result) }
600- return PathImpl ( string: String ( decodingCString: result!, as: UTF16 . self) )
554+ return Self ( string: String ( decodingCString: result!, as: UTF16 . self) )
601555 }
602556
603557 func appending( relativePath: Self ) -> Self {
@@ -608,7 +562,7 @@ private struct WindowsPath: Path, Sendable {
608562 }
609563 }
610564 defer { LocalFree ( result) }
611- return PathImpl ( string: String ( decodingCString: result!, as: UTF16 . self) )
565+ return Self ( string: String ( decodingCString: result!, as: UTF16 . self) )
612566 }
613567}
614568#else
@@ -830,7 +784,7 @@ private struct UNIXPath: Path, Sendable {
830784
831785 init ( validatingRelativePath path: String ) throws {
832786 switch path. first {
833- case " / " , " ~ " :
787+ case " / " :
834788 throw PathValidationError . invalidRelativePath ( path)
835789 default :
836790 self . init ( normalizingRelativePath: path)
@@ -875,9 +829,9 @@ private struct UNIXPath: Path, Sendable {
875829 }
876830
877831 if self == Self . root {
878- return PathImpl ( string: " / " + name)
832+ return Self ( string: " / " + name)
879833 } else {
880- return PathImpl ( string: string + " / " + name)
834+ return Self ( string: string + " / " + name)
881835 }
882836 }
883837
@@ -900,12 +854,12 @@ private struct UNIXPath: Path, Sendable {
900854 // the beginning of the path only.
901855 if relativePathString. hasPrefix ( " . " ) {
902856 if newPathString. hasPrefix ( " / " ) {
903- return PathImpl ( normalizingAbsolutePath: newPathString)
857+ return Self ( normalizingAbsolutePath: newPathString)
904858 } else {
905- return PathImpl ( normalizingRelativePath: newPathString)
859+ return Self ( normalizingRelativePath: newPathString)
906860 }
907861 } else {
908- return PathImpl ( string: newPathString)
862+ return Self ( string: newPathString)
909863 }
910864 }
911865}
@@ -926,7 +880,7 @@ extension PathValidationError: CustomStringConvertible {
926880 case . invalidAbsolutePath( let path) :
927881 return " invalid absolute path ' \( path) ' "
928882 case . invalidRelativePath( let path) :
929- return " invalid relative path ' \( path) '; relative path should not begin with ' \( AbsolutePath . root. pathString) ' or '~' "
883+ return " invalid relative path ' \( path) '; relative path should not begin with ' \( AbsolutePath . root. pathString) ' "
930884 }
931885 }
932886}
@@ -956,10 +910,16 @@ extension AbsolutePath {
956910 // might be an empty path (when self and the base are equal).
957911 let relComps = pathComps. dropFirst ( baseComps. count)
958912#if os(Windows)
959- result = RelativePath ( unsafeUncheckedPath : relComps. joined ( separator: " \\ " ) )
913+ let pathString = relComps. joined ( separator: " \\ " )
960914#else
961- result = RelativePath ( relComps. joined ( separator: " / " ) )
915+ let pathString = relComps. joined ( separator: " / " )
962916#endif
917+ do {
918+ result = try RelativePath ( validating: pathString)
919+ } catch {
920+ preconditionFailure ( " invalid relative path computed from \( pathString) " )
921+ }
922+
963923 } else {
964924 // General case, in which we might well need `..` components to go
965925 // "up" before we can go "down" the directory tree.
@@ -975,10 +935,15 @@ extension AbsolutePath {
975935 var relComps = Array ( repeating: " .. " , count: newBaseComps. count)
976936 relComps. append ( contentsOf: newPathComps)
977937#if os(Windows)
978- result = RelativePath ( unsafeUncheckedPath : relComps. joined ( separator: " \\ " ) )
938+ let pathString = relComps. joined ( separator: " \\ " )
979939#else
980- result = RelativePath ( relComps. joined ( separator: " / " ) )
940+ let pathString = relComps. joined ( separator: " / " )
981941#endif
942+ do {
943+ result = try RelativePath ( validating: pathString)
944+ } catch {
945+ preconditionFailure ( " invalid relative path computed from \( pathString) " )
946+ }
982947 }
983948
984949 assert ( AbsolutePath ( base, result) == self )
@@ -1065,13 +1030,31 @@ private func mayNeedNormalization(absolute string: String) -> Bool {
10651030// MARK: - `AbsolutePath` backwards compatibility, delete after deprecation period.
10661031
10671032extension AbsolutePath {
1033+ @_disfavoredOverload
10681034 @available ( * , deprecated, message: " use throwing `init(validating:)` variant instead " )
10691035 public init ( _ absStr: String ) {
10701036 try ! self . init ( validating: absStr)
10711037 }
10721038
1039+ @_disfavoredOverload
10731040 @available ( * , deprecated, message: " use throwing `init(validating:relativeTo:)` variant instead " )
10741041 public init ( _ str: String , relativeTo basePath: AbsolutePath ) {
10751042 try ! self . init ( validating: str, relativeTo: basePath)
10761043 }
1044+
1045+ @_disfavoredOverload
1046+ @available ( * , deprecated, message: " use throwing variant instead " )
1047+ public init ( _ absPath: AbsolutePath , _ relStr: String ) {
1048+ try ! self . init ( absPath, validating: relStr)
1049+ }
1050+ }
1051+
1052+ // MARK: - `AbsolutePath` backwards compatibility, delete after deprecation period.
1053+
1054+ extension RelativePath {
1055+ @_disfavoredOverload
1056+ @available ( * , deprecated, message: " use throwing variant instead " )
1057+ public init ( _ string: String ) {
1058+ try ! self . init ( validating: string)
1059+ }
10771060}
0 commit comments