Remove @dynamicMember generally

This commit is contained in:
Max Howell
2019-07-21 15:28:01 -04:00
parent d2bb2a1fdc
commit 621d1b0160
13 changed files with 198 additions and 131 deletions

View File

@@ -32,11 +32,15 @@ let _realpath = Glibc.realpath
let p1 = Path.root.usr.bin.ls // => /usr/bin/ls
However we only provide this support off of the static members like `root` due
to the anti-pattern where Path.swift suddenly feels like Javascript otherwise.
- Note: A `Path` does not necessarily represent an actual filesystem entry.
*/
public struct Path: Pathish {
@dynamicMemberLookup
public struct Path: Equatable, Hashable, Comparable {
/// The normalized string representation of the underlying filesystem path
public let string: String
init(string: String) {
assert(string.first == "/")
@@ -70,11 +74,11 @@ public struct Path: Equatable, Hashable, Comparable {
ifExists(withPrefix: "/var/automount", removeFirst: 2)
ifExists(withPrefix: "/private", removeFirst: 1)
#endif
self.string = join_(prefix: "/", pathComponents: pathComponents)
string = join_(prefix: "/", pathComponents: pathComponents)
case "~":
if description == "~" {
self = Path.home
string = Path.home.string
return
}
let tilded: String
@@ -96,7 +100,7 @@ public struct Path: Equatable, Hashable, Comparable {
#endif
}
pathComponents.remove(at: 0)
self.string = join_(prefix: tilded, pathComponents: pathComponents)
string = join_(prefix: tilded, pathComponents: pathComponents)
default:
return nil
@@ -124,20 +128,15 @@ public struct Path: Equatable, Hashable, Comparable {
// ^^ works even if the url is a file-reference url
}
/// :nodoc:
public subscript(dynamicMember addendum: String) -> Path {
//NOTE its possible for the string to be anything if we are invoked via
// explicit subscript thus we use our fully sanitized `join` function
return Path(string: join_(prefix: string, appending: addendum))
/// Converts anything that is `Pathish` to a `Path`
public init<P: Pathish>(_ path: P) {
string = path.string
}
}
//MARK: Properties
/// The underlying filesystem path
public let string: String
public extension Pathish {
/// Returns a `URL` representing this file path.
public var url: URL {
var url: URL {
return URL(fileURLWithPath: string)
}
@@ -147,7 +146,7 @@ public struct Path: Equatable, Hashable, Comparable {
- SeeAlso: https://developer.apple.com/documentation/foundation/nsurl/1408631-filereferenceurl
- Important: On Linux returns an file scheme NSURL for this path string.
*/
public var fileReferenceURL: NSURL? {
var fileReferenceURL: NSURL? {
#if !os(Linux)
// https://bugs.swift.org/browse/SR-2728
return (url as NSURL).perform(#selector(NSURL.fileReferenceURL))?.takeUnretainedValue() as? NSURL
@@ -164,7 +163,7 @@ public struct Path: Equatable, Hashable, Comparable {
- Note: always returns a valid path, `Path.root.parent` *is* `Path.root`.
*/
public var parent: Path {
var parent: Path {
let index = string.lastIndex(of: "/")!
let substr = string[string.indices.startIndex..<index]
return Path(string: String(substr))
@@ -177,7 +176,7 @@ public struct Path: Equatable, Hashable, Comparable {
- Note: We special case eg. `foo.tar.gz`.
*/
@inlinable
public var `extension`: String {
var `extension`: String {
//FIXME efficiency
switch true {
case string.hasSuffix(".tar.gz"):
@@ -204,7 +203,7 @@ public struct Path: Equatable, Hashable, Comparable {
- Important: The first element is always "/" to be consistent with `NSString.pathComponents`.
*/
@inlinable
public var components: [String] {
var components: [String] {
return ["/"] + string.split(separator: "/").map(String.init)
}
@@ -225,7 +224,7 @@ public struct Path: Equatable, Hashable, Comparable {
- Returns: A new joined path.
- SeeAlso: `Path./(_:_:)`
*/
public func join<S>(_ addendum: S) -> Path where S: StringProtocol {
func join<S>(_ addendum: S) -> Path where S: StringProtocol {
return Path(string: join_(prefix: string, appending: addendum))
}
@@ -246,7 +245,7 @@ public struct Path: Equatable, Hashable, Comparable {
- SeeAlso: `join(_:)`
*/
@inlinable
public static func /<S>(lhs: Path, rhs: S) -> Path where S: StringProtocol {
static func /<S>(lhs: Self, rhs: S) -> Path where S: StringProtocol {
return lhs.join(rhs)
}
@@ -257,7 +256,7 @@ public struct Path: Equatable, Hashable, Comparable {
- Parameter base: The base to which we calculate the relative path.
- ToDo: Another variant that returns `nil` if result would start with `..`
*/
public func relative(to base: Path) -> String {
func relative<P: Pathish>(to base: P) -> String {
// Split the two paths into their components.
// FIXME: The is needs to be optimized to avoid unncessary copying.
let pathComps = (string as NSString).pathComponents
@@ -296,7 +295,7 @@ public struct Path: Equatable, Hashable, Comparable {
- Returns: A string that is the filenames basename.
- Parameter dropExtension: If `true` returns the basename without its file extension.
*/
public func basename(dropExtension: Bool = false) -> String {
func basename(dropExtension: Bool = false) -> String {
var lastPathComponent: Substring {
let slash = string.lastIndex(of: "/")!
let index = string.index(after: slash)
@@ -321,27 +320,25 @@ public struct Path: Equatable, Hashable, Comparable {
If the path represents an actual entry that is a symlink, returns the symlinks
absolute destination.
- Important: This is not exhaustive, the resulting path may still contain
symlink.
- Important: The path will only be different if the last path component is a
symlink, any symlinks in prior components are not resolved.
- Important: This is not exhaustive, the resulting path may still contain a symlink.
- Important: The path will only be different if the last path component is a symlink, any symlinks in prior components are not resolved.
- Note: If file exists but isnt a symlink, returns `self`.
- Note: If symlink destination does not exist, is **not** an error.
*/
public func readlink() throws -> Path {
func readlink() throws -> Path {
do {
let rv = try FileManager.default.destinationOfSymbolicLink(atPath: string)
return Path(rv) ?? parent/rv
} catch CocoaError.fileReadUnknown {
// file is not symlink, return `self`
assert(exists)
return self
return Path(string: string)
} catch {
#if os(Linux)
// ugh: Swift on Linux
let nsError = error as NSError
if nsError.domain == NSCocoaErrorDomain, nsError.code == CocoaError.fileReadUnknown.rawValue, exists {
return self
return Path(self)
}
#endif
throw error
@@ -349,7 +346,7 @@ public struct Path: Equatable, Hashable, Comparable {
}
/// Recursively resolves symlinks in this path.
public func realpath() throws -> Path {
func realpath() throws -> Path {
guard let rv = _realpath(string, nil) else { throw CocoaError.error(.fileNoSuchFile) }
defer { free(rv) }
guard let rvv = String(validatingUTF8: rv) else { throw CocoaError.error(.fileReadUnknownStringEncoding) }
@@ -367,7 +364,7 @@ public struct Path: Equatable, Hashable, Comparable {
/// Returns the locale-aware sort order for the two paths.
/// :nodoc:
@inlinable
public static func <(lhs: Path, rhs: Path) -> Bool {
static func <(lhs: Self, rhs: Self) -> Bool {
return lhs.string.compare(rhs.string, locale: .current) == .orderedAscending
}
}
@@ -406,3 +403,27 @@ private func join_<S>(prefix: String, pathComponents: S) -> String where S: Sequ
}
return rv
}
/// A path that supports arbituary dot notation, eg. Path.root.usr.bin
@dynamicMemberLookup
public struct DynamicPath: Pathish {
/// The normalized string representation of the underlying filesystem path
public let string: String
init(string: String) {
assert(string.hasPrefix("/"))
self.string = string
}
/// Converts a `Path` to a `DynamicPath`
public init(_ path: Path) {
string = path.string
}
/// :nodoc:
public subscript(dynamicMember addendum: String) -> DynamicPath {
//NOTE its possible for the string to be anything if we are invoked via
// explicit subscript thus we use our fully sanitized `join` function
return DynamicPath(string: join_(prefix: string, appending: addendum))
}
}