File: FilePathExtras.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (108 lines) | stat: -rw-r--r-- 3,400 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108

#if SYSTEM_PACKAGE
@testable import SystemPackage
#else
@testable import System
#endif

// Why can't I write this extension on `FilePath.ComponentView.SubSequence`?
/*System 0.0.2, @available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)*/
extension Slice where Base == FilePath.ComponentView {
  internal var _storageSlice: SystemString.SubSequence {
    base._path._storage[self.startIndex._storage ..< self.endIndex._storage]
  }
}


// Proposed API that didn't make the cut, but we stil want to keep our testing for
/*System 0.0.2, @available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)*/
extension FilePath {
  /// Returns `self` relative to `base`.
  /// This does not cosult the file system or resolve symlinks.
  ///
  /// Returns `nil` if `self.root != base.root`.
  ///
  /// On Windows, if any component of either path could be interpreted as the root of
  /// a traditional DOS path (e.g. a directory named `C:`), returns `nil`.
  ///
  /// Example:
  ///
  ///     let path: FilePath = "/usr/local/bin"
  ///     path.lexicallyRelative(toBase: "/usr/local") == "bin"
  ///     path.lexicallyRelative(toBase: "/usr/local/bin/ls") == ".."
  ///     path.lexicallyRelative(toBase: "/tmp/foo.txt") == "../../usr/local/bin"
  ///     path.lexicallyRelative(toBase: "local/bin") == nil
  internal func lexicallyRelative(toBase base: FilePath) -> FilePath? {
    guard root == base.root else { return nil }

    // FIXME: On Windows, return nil if any component looks like a root

    let (tail, baseTail) = _dropCommonPrefix(components, base.components)

    var prefix = SystemString()
    for _ in 0..<baseTail.count {
      prefix.append(.dot)
      prefix.append(.dot)
      prefix.append(platformSeparator)
    }

    return FilePath(prefix + tail._storageSlice)
  }

  /// Whether a lexically-normalized `self` contains a lexically-normalized
  /// `other`.
  ///
  public func lexicallyContains(_ other: FilePath) -> Bool {
    guard !other.isEmpty else { return true }
    guard !isEmpty else { return false }

    let (selfLex, otherLex) =
      (self.lexicallyNormalized(), other.lexicallyNormalized())
    if otherLex.isAbsolute { return selfLex.starts(with: otherLex) }

    // FIXME: Windows semantics with relative roots?

    // TODO: better than this naive algorithm
    var slice = selfLex.components[...]
    while !slice.isEmpty {
      if slice.starts(with: otherLex.components) { return true }
      slice = slice.dropFirst()
    }
    return false
  }
}

extension Collection where Element: Equatable, SubSequence == Slice<Self> {
  // Mock up RangeSet functionality until it's real
  func indices(where p: (Element) throws -> Bool) rethrows -> [Range<Index>] {
    var result = Array<Range<Index>>()
    guard !isEmpty else { return result }

    var i = startIndex
    while i != endIndex {
      let next = index(after: i)
      if try p(self[i]) {
        result.append(i..<next)
      }
      i = next
    }

    return result
  }
}
extension RangeReplaceableCollection {
  mutating func removeSubranges(_ subranges: [Range<Index>]) {
    guard !subranges.isEmpty else { return }

    var result = Self()
    var idx = startIndex
    for range in subranges {
      result.append(contentsOf: self[idx..<range.lowerBound])
      idx = range.upperBound
    }
    if idx != endIndex {
      result.append(contentsOf: self[idx...])
    }
    self = result
  }
}