CommandLineToolkit

Форк
0
/
AbsolutePath.swift 
113 строк · 4.0 Кб
1
import Foundation
2

3
public final class AbsolutePath:
4
    Path,
5
    Codable,
6
    Hashable,
7
    Comparable,
8
    ExpressibleByStringLiteral,
9
    ExpressibleByArrayLiteral
10
{
11
    // MARK: - Static interface
12
    
13
    public static let root = AbsolutePath(components: [String]())
14
    public static let home = AbsolutePath(NSHomeDirectory())
15
    public static let temp = AbsolutePath(NSTemporaryDirectory())
16

17
    /// Returns an `AbsolutePath` only if `string` has a value that looks like an absolute path - begins with `/`.
18
    /// - Parameter string: String representation of absolute path.
19
    /// - Throws: Error when `string` doesn't seem to be an absolute path.
20
    /// - Returns:`AbsolutePath` with path parsed from `string`.
21
    public static func validating(string: String) throws -> AbsolutePath {
22
        struct ValidationError: Error, CustomStringConvertible {
23
            let string: String
24
            var description: String { "String '\(string)' does not appear to be an absolute path" }
25
        }
26
        guard isAbsolute(path: string) else { throw ValidationError(string: string) }
27
        return AbsolutePath(string)
28
    }
29
    
30
    public static func isAbsolute(path: String) -> Bool {
31
        path.hasPrefix("/")
32
    }
33
    
34
    // MARK: - Instance members
35
    
36
    public let components: [String] // source value
37
    public let pathString: String // precomputed value
38
    
39
    // MARK: - Initializers members
40
    
41
    public init<S: StringProtocol>(components: [S]) {
42
        self.components = StringPathParsing.slashSeparatedComponents(paths: components)
43
        self.pathString = Self.pathString(components: self.components)
44
    }
45
    
46
    // MARK: - Instance interface
47
    
48
    public var isRoot: Bool {
49
        components.isEmpty
50
    }
51
    
52
    public var fileUrl: URL {
53
        return URL(fileURLWithPath: pathString)
54
    }
55
    
56
    public var standardized: AbsolutePath {
57
        AbsolutePath(fileUrl.standardized)
58
    }
59
    
60
    /// Returns true if current path is a child of given anchor path.
61
    /// Examples:
62
    ///     `/path/to/something` is subpath of `/path/to`.
63
    ///     `/path/to/something` is NOT subpath of `/path/to/something`.
64
    ///     `/path/of/something` is NOT subpath of `/path/to/`.
65
    public func isSubpathOf(anchorPath: AbsolutePath) -> Bool {
66
        guard components.count > anchorPath.components.count else {
67
            return false
68
        }
69
        return zip(anchorPath.components, components.prefix(upTo: anchorPath.components.count)).allSatisfy { lhs, rhs in
70
            lhs == rhs
71
        }
72
    }
73
    
74
    /// Finds a `RelativePath` for this instance and a given anchor path.
75
    public func relativePath(anchorPath: AbsolutePath) -> RelativePath {
76
        let pathComponents = components
77
        let anchorComponents = anchorPath.components
78
        
79
        var componentsInCommon = 0
80
        for (pathComponent, anchorComponent) in zip(pathComponents, anchorComponents) {
81
            if pathComponent != anchorComponent {
82
                break
83
            }
84
            componentsInCommon += 1
85
        }
86
        
87
        let numberOfParentComponents = anchorComponents.count - componentsInCommon
88
        let numberOfPathComponents = pathComponents.count - componentsInCommon
89
        
90
        var relativeComponents = [String]()
91
        relativeComponents.reserveCapacity(numberOfParentComponents + numberOfPathComponents)
92
        for _ in 0..<numberOfParentComponents {
93
            relativeComponents.append("..")
94
        }
95
        relativeComponents.append(contentsOf: pathComponents[componentsInCommon..<pathComponents.count])
96
        
97
        return RelativePath(components: relativeComponents)
98
    }
99

100
    public static func +(lhs: AbsolutePath, rhs: RelativePath) -> AbsolutePath {
101
        return AbsolutePath(components: lhs.components + rhs.components)
102
    }
103

104
    // MARK: - Conformance to `ExpressibleByStringLiteral`
105
    
106
    public typealias StringLiteralType = String
107
    
108
    // MARK: - Private
109
    
110
    private static func pathString<S: StringProtocol>(components: [S]) -> String {
111
        "/" + components.joined(separator: "/")
112
    }
113
}
114

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.