FilePath.swift 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. /*
  2. This source file is part of the Swift System open source project
  3. Copyright (c) 2020 Apple Inc. and the Swift System project authors
  4. Licensed under Apache License v2.0 with Runtime Library Exception
  5. See https://swift.org/LICENSE.txt for license information
  6. */
  7. /// Represents a location in the file system.
  8. ///
  9. /// This structure recognizes directory separators (e.g. `/`), roots, and
  10. /// requires that the content terminates in a NUL (`0x0`). Beyond that, it
  11. /// does not give any meaning to the bytes that it contains. The file system
  12. /// defines how the content is interpreted; for example, by its choice of string
  13. /// encoding.
  14. ///
  15. /// On construction, `FilePath` will normalize separators by removing
  16. /// redundant intermediary separators and stripping any trailing separators.
  17. /// On Windows, `FilePath` will also normalize forward slashes `/` into
  18. /// backslashes `\`, as preferred by the platform.
  19. ///
  20. /// The code below creates a file path from a string literal,
  21. /// and then uses it to open and append to a log file:
  22. ///
  23. /// let message: String = "This is a log message."
  24. /// let path: FilePath = "/tmp/log"
  25. /// let fd = try FileDescriptor.open(path, .writeOnly, options: .append)
  26. /// try fd.closeAfter { try fd.writeAll(message.utf8) }
  27. ///
  28. /// File paths conform to the
  29. /// <doc://com.apple.documentation/documentation/swift/equatable>
  30. /// and <doc://com.apple.documentation/documentation/swift/hashable> protocols
  31. /// by performing the protocols' operations on their raw byte contents.
  32. /// This conformance allows file paths to be used,
  33. /// for example, as keys in a dictionary.
  34. /// However, the rules for path equivalence
  35. /// are file-system–specific and have additional considerations
  36. /// like case insensitivity, Unicode normalization, and symbolic links.
  37. @available(System 0.0.1, *)
  38. public struct FilePath: Sendable {
  39. // TODO(docs): Section on all the new syntactic operations, lexical normalization, decomposition,
  40. // components, etc.
  41. internal var _storage: SystemString
  42. /// Creates an empty, null-terminated path.
  43. public init() {
  44. self._storage = SystemString()
  45. _invariantCheck()
  46. }
  47. // In addition to the empty init, this init will properly normalize
  48. // separators. All other initializers should be implemented by
  49. // ultimately deferring to a normalizing init.
  50. internal init(_ str: SystemString) {
  51. self._storage = str
  52. self._normalizeSeparators()
  53. _invariantCheck()
  54. }
  55. }
  56. @available(System 0.0.1, *)
  57. extension FilePath {
  58. /// The length of the file path, excluding the null terminator.
  59. public var length: Int { _storage.length }
  60. }
  61. @available(System 0.0.1, *)
  62. extension FilePath: Hashable {}
  63. @available(System 0.0.1, *)
  64. extension FilePath: Codable {
  65. // Encoder is synthesized; it probably should have been explicit and used
  66. // a single-value container, but making that change now is somewhat risky.
  67. // Decoder is written explicitly to ensure that we validate invariants on
  68. // untrusted input.
  69. public init(from decoder: any Decoder) throws {
  70. let container = try decoder.container(keyedBy: CodingKeys.self)
  71. self._storage = try container.decode(SystemString.self, forKey: ._storage)
  72. guard _invariantsSatisfied() else {
  73. throw DecodingError.dataCorruptedError(
  74. forKey: ._storage,
  75. in: container,
  76. debugDescription:
  77. "Encoding does not satisfy the invariants of FilePath"
  78. )
  79. }
  80. }
  81. }