A type safe swift ResultBuilder DSL for structured directories
0

Configure Feed

Select the types of activity you want to include in your feed.

Remove unused import of customDump

+1612 -84
-51
Package.resolved
··· 1 - { 2 - "originHash" : "8d8d86fb5c65d5509471dbe0c52ac821e1db4d31a2a7d1a275c978281b0f5847", 3 - "pins" : [ 4 - { 5 - "identity" : "swift-case-paths", 6 - "kind" : "remoteSourceControl", 7 - "location" : "https://github.com/pointfreeco/swift-case-paths", 8 - "state" : { 9 - "revision" : "19b7263bacb9751f151ec0c93ec816fe1ef67c7b", 10 - "version" : "1.6.1" 11 - } 12 - }, 13 - { 14 - "identity" : "swift-custom-dump", 15 - "kind" : "remoteSourceControl", 16 - "location" : "https://github.com/pointfreeco/swift-custom-dump", 17 - "state" : { 18 - "revision" : "82645ec760917961cfa08c9c0c7104a57a0fa4b1", 19 - "version" : "1.3.3" 20 - } 21 - }, 22 - { 23 - "identity" : "swift-parsing", 24 - "kind" : "remoteSourceControl", 25 - "location" : "https://github.com/woodymelling/swift-parsing", 26 - "state" : { 27 - "branch" : "android-support", 28 - "revision" : "1d74dcf9fe3b3918067b519447c66c0088b644b2" 29 - } 30 - }, 31 - { 32 - "identity" : "swift-syntax", 33 - "kind" : "remoteSourceControl", 34 - "location" : "https://github.com/swiftlang/swift-syntax", 35 - "state" : { 36 - "revision" : "0687f71944021d616d34d922343dcef086855920", 37 - "version" : "600.0.1" 38 - } 39 - }, 40 - { 41 - "identity" : "xctest-dynamic-overlay", 42 - "kind" : "remoteSourceControl", 43 - "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", 44 - "state" : { 45 - "revision" : "39de59b2d47f7ef3ca88a039dff3084688fe27f4", 46 - "version" : "1.5.2" 47 - } 48 - } 49 - ], 50 - "version" : 3 51 - }
+1 -8
Package.swift
··· 11 11 .library(name: "FileTree", targets: ["FileTree"]), 12 12 ], 13 13 dependencies: [ 14 - .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.3"), 15 - .package(url: "https://github.com/woodymelling/swift-parsing", branch: "android-support") 16 14 ], 17 15 targets: [ 18 16 // Targets are the basic building blocks of a package, defining a module or a test suite. 19 17 // Targets can depend on other targets in this package and products from dependencies. 20 - .target( 21 - name: "FileTree", 22 - dependencies: [ 23 - .product(name: "Conversions", package: "swift-parsing"), 24 - ] 25 - ), 18 + .target(name: "FileTree"), 26 19 .testTarget( 27 20 name: "FileTreeTests", 28 21 dependencies: [
-1
Sources/FileTree/Components/Directory.swift
··· 6 6 // 7 7 8 8 import Foundation 9 - import IssueReporting 10 9 11 10 public struct Directory<Component: FileTreeReader>: FileTreeReader { 12 11 let path: StaticString
+22 -23
Sources/FileTree/Components/File.swift
··· 6 6 // 7 7 8 8 import Foundation 9 - import IssueReporting 10 9 11 10 public struct File: FileTreeReader, Sendable { 12 11 let fileName: StaticString ··· 87 86 } 88 87 89 88 public func write(_ data: [FileContent<Data>], to url: URL) throws { 90 - guard writingToEmptyDirectory 91 - else { 92 - reportIssue(""" 93 - Writing an array of files to a directory that may already have contents currently unsupported. 94 - 95 - This is because of the circumstance where a file exists in the directory, but not in the array 96 - It is difficult to determine if the file should be deleted, or if it exists outside of the purview of the `Files` block and should be left alone. 97 - 98 - The semantics of Many may need to be tweaked to make this determination more clear. 99 - 100 - To allow writing to the directory, use: 101 - 102 - ``` 103 - $writingToEmptyDirectory.withValue(true) { 104 - Files(withExtension: .text).write([Data(), Data(), Data()])) 105 - } 106 - ``` 107 - 108 - which will naively write all the contents to the directory, and not delete anything that is already there. 109 - """) 110 - return 111 - } 89 + // guard writingToEmptyDirectory 90 + // else { 91 + // reportIssue(""" 92 + // Writing an array of files to a directory that may already have contents currently unsupported. 93 + // 94 + // This is because of the circumstance where a file exists in the directory, but not in the array 95 + // It is difficult to determine if the file should be deleted, or if it exists outside of the purview of the `Files` block and should be left alone. 96 + // 97 + // The semantics of Many may need to be tweaked to make this determination more clear. 98 + // 99 + // To allow writing to the directory, use: 100 + // 101 + // ``` 102 + // $writingToEmptyDirectory.withValue(true) { 103 + // Files(withExtension: .text).write([Data(), Data(), Data()])) 104 + // } 105 + // ``` 106 + // 107 + // which will naively write all the contents to the directory, and not delete anything that is already there. 108 + // """) 109 + // return 110 + // } 112 111 113 112 for fileContent in data { 114 113
-1
Sources/FileTree/Conversions.swift
··· 5 5 // Created by Woodrow Melling on 10/23/24. 6 6 // 7 7 8 - @preconcurrency import Conversions 9 8 import Foundation 10 9 11 10 // MARK: Converted
+62
Sources/FileTree/Conversions/BinaryFloatingPoint.swift
··· 1 + import Foundation 2 + 3 + extension Conversion where Self == Conversions.FixedWidthIntegerToBinaryFloatingPoint<Int, Double> { 4 + /// A conversion from an `Int` to a `Double`. 5 + /// 6 + /// This conversion can be used to transform a ``ParserPrinter``'s integer output into a double 7 + /// output: 8 + /// 9 + /// ```swift 10 + /// Digits().map(.double).parse("123") // ✅ 123.0 11 + /// 12 + /// Digits().map(.double).parse("123.0") // ❌ 13 + /// // error: unexpected input 14 + /// // --> input:1:4 15 + /// // 1 | 123.0 16 + /// // | ^ expected end of input 17 + /// ``` 18 + @inlinable 19 + public static var double: Self { .init() } 20 + } 21 + 22 + extension Conversion where Output == Int { 23 + /// Transforms this conversion to `Int` into a conversion to `Double`. 24 + /// 25 + /// A fluent version of ``Conversion/double-swift.type.property``. Equivalent to calling 26 + /// ``map(_:)`` with ``Conversion/double-swift.type.property``: 27 + /// 28 + /// ```swift 29 + /// intConversion.double 30 + /// // = 31 + /// intConversion.map(.double) 32 + /// ``` 33 + @inlinable 34 + public var double: 35 + Conversions.Map< 36 + Self, Conversions.FixedWidthIntegerToBinaryFloatingPoint<Int, Double> 37 + > 38 + { self.map(.double) } 39 + } 40 + 41 + extension Conversions { 42 + /// A conversion from an `Int` to a `Double`. 43 + /// 44 + /// You will not typically need to interact with this type directly. Instead you will usually use 45 + /// the ``Conversion/double-swift.type.property`` operation, which constructs this type. 46 + public struct FixedWidthIntegerToBinaryFloatingPoint< 47 + Input: FixedWidthInteger, Output: BinaryFloatingPoint 48 + >: Conversion { 49 + @usableFromInline 50 + init() {} 51 + 52 + @inlinable 53 + public func apply(_ input: Input) -> Output { 54 + .init(input) 55 + } 56 + 57 + @inlinable 58 + public func unapply(_ output: Output) -> Input { 59 + .init(output) 60 + } 61 + } 62 + }
+10
Sources/FileTree/Conversions/ConvertingError.swift
··· 1 + @usableFromInline 2 + struct ConvertingError: Error { 3 + @usableFromInline 4 + let message: String 5 + 6 + @usableFromInline 7 + init(_ message: String = "") { 8 + self.message = message 9 + } 10 + }
+177
Sources/FileTree/Conversions/Core/AnyConversion.swift
··· 1 + extension Conversion { 2 + /// A conversion that invokes the given apply and unapply functions. 3 + /// 4 + /// Useful for experimenting with conversions in a lightweight manner, without the ceremony of 5 + /// defining a dedicated type. 6 + /// 7 + /// ```swift 8 + /// struct Amount { 9 + /// var cents: Int 10 + /// } 11 + /// 12 + /// let amount = Parse( 13 + /// .convert( 14 + /// apply: { dollars, cents in Amount(cents: dollars * 100 + cents) }, 15 + /// unapply: { amount in amount.cents.quotientAndRemainder(dividingBy: 100) } 16 + /// ) 17 + /// ) { 18 + /// Digits() 19 + /// "." 20 + /// Digits(2) 21 + /// } 22 + /// ``` 23 + /// 24 + /// If performance is a concern, you should define a custom type that conforms to ``Conversion`` 25 + /// instead, which avoids the overhead of escaping closures, gives the compiler the ability to 26 + /// better optimize, and puts your in a better position to test the conversion. 27 + /// 28 + /// ```swift 29 + /// struct AmountConversion: Conversion { 30 + /// func apply(_ dollarsAndCents: (Int, Int)) -> Amount { 31 + /// return Amount(cents: dollarsAndCents.0 * 100 + dollarsAndCents.1) 32 + /// } 33 + /// 34 + /// func unapply(_ amount: Amount) -> (Int, Int) { 35 + /// amount.cents.quotientAndRemainder(dividingBy: 100) 36 + /// } 37 + /// } 38 + /// 39 + /// let amount = Parse(AmountConversion()) { 40 + /// Digits() 41 + /// "." 42 + /// Digits(2) 43 + /// } 44 + /// ``` 45 + /// 46 + /// - Parameters: 47 + /// - apply: A closure that attempts to convert an input into an output. `apply` is executed 48 + /// each time the ``apply(_:)`` method is called on the resulting conversion. If the closure 49 + /// returns `nil`, an error is thrown. Otherwise, the value is unwrapped. 50 + /// - unapply: A closure that attempts to convert an output into an input. `unapply` is executed 51 + /// each time the ``unapply(_:)`` method is called on the resulting conversion. If the closure 52 + /// returns `nil`, an error is thrown. Otherwise, the value is unwrapped. 53 + /// - Returns: A conversion that invokes the given apply and unapply functions. 54 + @inlinable 55 + public static func convert<Input, Output>( 56 + apply: @escaping @Sendable (Input) -> Output?, 57 + unapply: @escaping @Sendable (Output) -> Input? 58 + ) -> Self where Self == AnyConversion<Input, Output> { 59 + .init(apply: apply, unapply: unapply) 60 + } 61 + } 62 + 63 + /// A type-erased ``Conversion``. 64 + /// 65 + /// This conversion forwards its ``apply(_:)`` and ``unapply(_:)`` methods to an arbitrary 66 + /// underlying conversion having the same `Input` and `Output` types, hiding the specifics of the 67 + /// underlying ``Conversion``. 68 + /// 69 + /// Use `AnyConversion` to wrap a conversion whose type has details you don't want to expose across 70 + /// API boundaries, such as different modules. When you use type erasure this way, you can change 71 + /// the underlying conversion over time without affecting existing clients. 72 + /// 73 + /// `AnyConversion` can also be useful for experimenting with ad hoc conversions in a lightweight 74 + /// manner. One can avoid the upfront ceremony of defining a whole new type and instead create a 75 + /// "conformance" inline by specifying the `apply` and `unapply` functions directly 76 + /// 77 + /// ```swift 78 + /// Prefix { $0.isNumber } 79 + /// .map( 80 + /// AnyConversion( 81 + /// apply: { Int(String($0)) }, 82 + /// unapply: { String($0)[...] { 83 + /// ) 84 + /// ) 85 + /// 86 + /// // vs. 87 + /// 88 + /// struct SubstringToInt: Conversion { 89 + /// func apply(_ input: Substring) throws -> Int { 90 + /// guard let int = Int(String(input)) else { 91 + /// struct ConvertingError: Error {} 92 + /// throw ConvertingError() 93 + /// } 94 + /// return int 95 + /// } 96 + /// 97 + /// func unapply(_ output: Int) -> Substring { 98 + /// String(output)[...] 99 + /// } 100 + /// } 101 + /// 102 + /// Prefix { $0.isNumber } 103 + /// .map(SubstringToInt()) 104 + /// ``` 105 + /// 106 + /// If performance is a consideration of your parser-printer, you should avoid `AnyConversion` and 107 + /// instead create custom types that conform to the ``Conversion`` protocol. 108 + public struct AnyConversion<Input, Output>: Conversion { 109 + @usableFromInline 110 + let _apply: @Sendable (Input) throws -> Output 111 + 112 + @usableFromInline 113 + let _unapply: @Sendable (Output) throws -> Input 114 + 115 + /// Creates a type-erasing conversion to wrap the given conversion. 116 + /// 117 + /// - Parameter conversion: A conversion to wrap with a type eraser. 118 + @inlinable 119 + public init<C: Conversion>(_ conversion: C) where C.Input == Input, C.Output == Output { 120 + self._apply = conversion.apply 121 + self._unapply = conversion.unapply 122 + } 123 + 124 + /// Creates a conversion that wraps the given closures in its ``apply(_:)`` and ``unapply(_:)`` 125 + /// methods 126 + /// 127 + /// - Parameters: 128 + /// - apply: A closure that attempts to convert an input into an output. `apply` is executed 129 + /// each time the ``apply(_:)`` method is called on the resulting conversion 130 + /// - unapply: A closure that attempts to convert an output into an input. `unapply` is executed 131 + /// each time the ``unapply(_:)`` method is called on the resulting conversion. 132 + @inlinable 133 + public init( 134 + apply: @escaping @Sendable (Input) throws -> Output, 135 + unapply: @escaping @Sendable (Output) throws -> Input 136 + ) { 137 + self._apply = apply 138 + self._unapply = unapply 139 + } 140 + 141 + /// Creates a conversion that wraps the given closures in its ``apply(_:)`` and ``unapply(_:)`` 142 + /// methods, throwing an error when `nil` is returned. 143 + /// 144 + /// - Parameters: 145 + /// - apply: A closure that attempts to convert an input into an output. `apply` is executed 146 + /// each time the ``apply(_:)`` method is called on the resulting conversion. If the closure 147 + /// returns `nil`, an error is thrown. Otherwise, the value is unwrapped. 148 + /// - unapply: A closure that attempts to convert an output into an input. `unapply` is executed 149 + /// each time the ``unapply(_:)`` method is called on the resulting conversion. If the closure 150 + /// returns `nil`, an error is thrown. Otherwise, the value is unwrapped. 151 + @inlinable 152 + public init( 153 + apply: @escaping @Sendable (Input) -> Output?, 154 + unapply: @escaping @Sendable (Output) -> Input? 155 + ) { 156 + self._apply = { 157 + guard let value = apply($0) 158 + else { throw ConvertingError() } 159 + return value 160 + } 161 + self._unapply = { 162 + guard let value = unapply($0) 163 + else { throw ConvertingError() } 164 + return value 165 + } 166 + } 167 + 168 + @inlinable 169 + public func apply(_ input: Input) throws -> Output { 170 + try self._apply(input) 171 + } 172 + 173 + @inlinable 174 + public func unapply(_ output: Output) throws -> Input { 175 + try self._unapply(output) 176 + } 177 + }
+57
Sources/FileTree/Conversions/Core/Conversion.swift
··· 1 + /// Declares a type that can asynchronously transform an `Input` value into an `Output` value *and* transform an 2 + /// `Output` value back into an `Input` value. 3 + /// 4 + /// Useful in bidirectionally tranforming types, like when writing something to the disk. 5 + /// printability via ``Parser/map(_:)-18m9d``. 6 + @rethrows public protocol Conversion<Input, Output>: Sendable { 7 + // The type of values this conversion converts from. 8 + associatedtype Input 9 + 10 + // The type of values this conversion converts to. 11 + associatedtype Output 12 + 13 + associatedtype Body 14 + 15 + /// Attempts to asynchronously transform an input into an output. 16 + /// 17 + /// See ``Conversion/apply(_:)`` for the reverse process. 18 + /// 19 + /// - Parameter input: An input value. 20 + /// - Returns: A transformed output value. 21 + @Sendable func apply(_ input: Input) throws -> Output 22 + 23 + /// Attempts to asynchronously transform an output back into an input. 24 + /// 25 + /// The reverse process of ``Conversion/apply(_:)``. 26 + /// 27 + /// - Parameter output: An output value. 28 + /// - Returns: An "un"-transformed input value. 29 + @Sendable func unapply(_ input: Output) throws -> Input 30 + 31 + @ConversionBuilder 32 + var body: Body { get } 33 + } 34 + 35 + extension Conversion 36 + where Body: Conversion, Body.Input == Input, Body.Output == Output { 37 + public func apply(_ input: Input) throws -> Output { 38 + try self.body.apply(input) 39 + } 40 + 41 + public func unapply(_ output: Output) throws -> Input { 42 + try self.body.unapply(output) 43 + } 44 + } 45 + 46 + extension Conversion where Body == Never { 47 + public var body: Body { 48 + return fatalError("Body of \(Self.self) should never be called") 49 + } 50 + } 51 + 52 + /// A namespace for types that serve as conversions. 53 + /// 54 + /// The various operators defined as extensions on ``Conversion`` implement their functionality as 55 + /// classes or structures that extend this enumeration. For example, the ``Conversion/map(_:)`` 56 + /// operator returns a ``Map`` conversion. 57 + public enum Conversions {}
+26
Sources/FileTree/Conversions/Core/ConversionBuilder.swift
··· 1 + // 2 + // ConversionBuilder.swift 3 + // swift-parsing 4 + // 5 + // Created by Woodrow Melling on 10/24/24. 6 + // 7 + 8 + @resultBuilder 9 + public enum ConversionBuilder { 10 + public static func buildBlock<T>() -> Conversions.Identity<T> { 11 + Conversions.Identity() 12 + } 13 + public static func buildPartialBlock<C: Conversion>(first conversion: C) -> C { 14 + conversion 15 + } 16 + 17 + public static func buildPartialBlock< 18 + C0: Conversion, 19 + C1: Conversion 20 + >( 21 + accumulated c0: C0, 22 + next c1: C1 23 + ) -> Conversions.Map<C0, C1> where C0.Output == C1.Input { 24 + Conversions.Map(upstream: c0, downstream: c1) 25 + } 26 + }
+54
Sources/FileTree/Conversions/Core/ConversionMap.swift
··· 1 + extension Conversion { 2 + /// Returns a conversion that transforms the output of this conversion with a given downstream 3 + /// conversion. 4 + /// 5 + /// When provided with a conversion from this conversion's output type to some new output type, 6 + /// this method can return a new conversion from this conversion's input type to the given 7 + /// conversion's output type by calling their ``apply(_:)`` functions and ``unapply(_:)`` 8 + /// functions one after the other. 9 + /// 10 + /// This method is similar to `Sequence.map`, `Optional.map`, and `Result.map` in the Swift 11 + /// standard library, as well as `Publisher.map` in the Combine framework. This method is also 12 + /// similar to the `map` functions on ``Parser`` and ``ParserPrinter``, especially 13 + /// ``Parser/map(_:)-18m9d``, which takes a conversion. 14 + /// 15 + /// - Parameter downstream: A conversion that transforms the output of this conversion into some 16 + /// new output. 17 + /// - Returns: A conversion that transforms the input of this conversion into the output of the 18 + /// given conversion. 19 + @inlinable 20 + public func map<C>(_ downstream: C) -> Conversions.Map<Self, C> { 21 + .init(upstream: self, downstream: downstream) 22 + } 23 + } 24 + 25 + extension Conversions { 26 + /// A conversion that composes two conversions together by composing their 27 + /// ``Conversion/apply(_:)`` functions and ``Conversion/unapply(_:)`` functions together. 28 + /// 29 + /// You will not typically need to interact with this type directly. Instead you will usually use 30 + /// the ``Conversion/map(_:)`` operation, which constructs this type. 31 + public struct Map<Upstream: Conversion, Downstream: Conversion>: Conversion 32 + where Upstream.Output == Downstream.Input { 33 + public let upstream: Upstream 34 + public let downstream: Downstream 35 + 36 + @usableFromInline 37 + init(upstream: Upstream, downstream: Downstream) { 38 + self.upstream = upstream 39 + self.downstream = downstream 40 + } 41 + 42 + @inlinable 43 + @inline(__always) 44 + public func apply(_ input: Upstream.Input) rethrows -> Downstream.Output { 45 + try self.downstream.apply(self.upstream.apply(input)) 46 + } 47 + 48 + @inlinable 49 + @inline(__always) 50 + public func unapply(_ output: Downstream.Output) rethrows -> Upstream.Input { 51 + try self.upstream.unapply(self.downstream.unapply(output)) 52 + } 53 + } 54 + }
+37
Sources/FileTree/Conversions/Core/Inverted.swift
··· 1 + // 2 + // Inverted.swift 3 + // swift-parsing 4 + // 5 + // Created by Woodrow Melling on 10/28/24. 6 + // 7 + 8 + extension Conversions { 9 + public struct Inverted<C: Conversion>: Conversion { 10 + public var conversion: C 11 + 12 + @inlinable 13 + public init(_ conversion: C) { 14 + self.conversion = conversion 15 + } 16 + 17 + @inlinable 18 + @inline(__always) 19 + public func apply(_ input: C.Output) throws -> C.Input { 20 + try conversion.unapply(input) 21 + } 22 + 23 + @inlinable 24 + @inline(__always) 25 + public func unapply(_ output: C.Input) throws -> C.Output { 26 + try conversion.apply(output) 27 + } 28 + } 29 + } 30 + 31 + extension Conversion { 32 + @inlinable 33 + @inline(__always) 34 + public func inverted() -> Conversions.Inverted<Self> { 35 + Conversions.Inverted(self) 36 + } 37 + }
+55
Sources/FileTree/Conversions/Data.swift
··· 1 + import Foundation 2 + 3 + extension Conversion where Self == Conversions.BytesToData<Substring.UTF8View> { 4 + /// A conversion from `Substring.UTF8View` to `Data`. 5 + @inlinable 6 + public static var data: Self { .init() } 7 + } 8 + 9 + extension Conversion where Output == Substring.UTF8View { 10 + /// Transforms this conversion to `Substring.UTF8View` into a conversion to `Data`. 11 + /// 12 + /// A fluent version of ``Conversion/data-swift.type.property-8z7qz``. 13 + @inlinable 14 + public var data: Conversions.Map<Self, Conversions.BytesToData<Output>> { 15 + self.map(.data) 16 + } 17 + } 18 + 19 + extension Conversion where Self == Conversions.BytesToData<ArraySlice<UInt8>> { 20 + /// A conversion from `ArraySlice<UInt8>` to `Data`. 21 + @inlinable 22 + public static var data: Self { .init() } 23 + } 24 + 25 + extension Conversion where Output == ArraySlice<UInt8> { 26 + /// Transforms this conversion to `ArraySlice<UInt8>` into a conversion to `Data`. 27 + /// 28 + /// A fluent version of ``Conversion/data-swift.type.property-7g9sj``. 29 + @inlinable 30 + public var data: Conversions.Map<Self, Conversions.BytesToData<Output>> { 31 + self.map(.data) 32 + } 33 + } 34 + 35 + extension Conversions { 36 + /// A conversion from a ``PrependableCollection`` of UTF-8 bytes to `Data`. 37 + /// 38 + /// You will not typically need to interact with this type directly. Instead you will usually use 39 + /// the ``Conversion/data-swift.type.property-8z7qz`` and 40 + /// ``Conversion/data-swift.type.property-7g9sj`` operations, which constructs this type. 41 + public struct BytesToData<Input: PrependableCollection>: Conversion where Input.Element == UInt8 { 42 + @usableFromInline 43 + init() {} 44 + 45 + @inlinable 46 + public func apply(_ input: Input) -> Data { 47 + .init(input) 48 + } 49 + 50 + @inlinable 51 + public func unapply(_ output: Data) -> Input { 52 + .init(output) 53 + } 54 + } 55 + }
+24
Sources/FileTree/Conversions/DataString.swift
··· 1 + // 2 + // DataString.swift 3 + // OpenFestival 4 + // 5 + // Created by Woodrow Melling on 10/25/24. 6 + // 7 + 8 + import Foundation 9 + 10 + extension Conversions { 11 + public struct DataToString: Conversion { 12 + public typealias Input = Data 13 + public typealias Output = String 14 + public init() {} 15 + 16 + public func apply(_ input: Data) -> String { 17 + String(decoding: input, as: UTF8.self) 18 + } 19 + 20 + public func unapply(_ output: String) -> Data { 21 + Data(output.utf8) 22 + } 23 + } 24 + }
+3
Sources/FileTree/Conversions/EmptyInitializable.swift
··· 1 + public protocol _EmptyInitializable { 2 + init() 3 + }
+1
Sources/FileTree/Conversions/Enum.swift
··· 1 +
+54
Sources/FileTree/Conversions/FixedWidthInteger.swift
··· 1 + extension Conversion where Self == Conversions.BinaryFloatingPointToFixedWidthInteger<Double, Int> { 2 + /// A conversion from a `Double` to an `Int`. 3 + /// 4 + /// This conversion can be used to transform a ``ParserPrinter``'s double output into an integer 5 + /// output, rounding toward zero. 6 + /// 7 + /// ```swift 8 + /// Double.parser().map(.int).parse("123.45") // 123 9 + /// ``` 10 + @inlinable 11 + public static var int: Self { .init() } 12 + } 13 + 14 + extension Conversion where Output == Double { 15 + /// Transforms this conversion to `Double` into a conversion to `Int`. 16 + /// 17 + /// A fluent version of ``Conversion/int-swift.type.property``. Equivalent to calling ``map(_:)`` 18 + /// with ``Conversion/int-swift.type.property``: 19 + /// 20 + /// ```swift 21 + /// doubleConversion.int 22 + /// // = 23 + /// doubleConversion.map(.int) 24 + /// ``` 25 + @inlinable 26 + public var int: 27 + Conversions.Map< 28 + Self, Conversions.BinaryFloatingPointToFixedWidthInteger<Double, Int> 29 + > 30 + { self.map(.int) } 31 + } 32 + 33 + extension Conversions { 34 + /// A conversion from a `Double` to an `Int`. 35 + /// 36 + /// You will not typically need to interact with this type directly. Instead you will usually use 37 + /// the ``Conversion/int-swift.type.property`` operation, which constructs this type. 38 + public struct BinaryFloatingPointToFixedWidthInteger< 39 + Input: BinaryFloatingPoint, Output: FixedWidthInteger 40 + >: Conversion { 41 + @usableFromInline 42 + init() {} 43 + 44 + @inlinable 45 + public func apply(_ input: Input) -> Output { 46 + .init(input) 47 + } 48 + 49 + @inlinable 50 + public func unapply(_ output: Output) -> Input { 51 + .init(output) 52 + } 53 + } 54 + }
+20
Sources/FileTree/Conversions/Identity.swift
··· 1 + import Foundation 2 + 3 + extension Conversions { 4 + public struct Identity<Value>: Conversion { 5 + @inlinable 6 + public init() {} 7 + 8 + @inlinable 9 + @inline(__always) 10 + public func apply(_ input: Value) -> Value { 11 + input 12 + } 13 + 14 + @inlinable 15 + @inline(__always) 16 + public func unapply(_ output: Value) -> Value { 17 + output 18 + } 19 + } 20 + }
+82
Sources/FileTree/Conversions/JSON.swift
··· 1 + import Foundation 2 + 3 + extension Conversion { 4 + /// A conversion from `Data` to the given codable type. 5 + /// 6 + /// See ``json(_:decoder:encoder:)-swift.method`` for a fluent version of this interface that 7 + /// transforms an existing conversion. 8 + /// 9 + /// - Parameters: 10 + /// - type: A type that conforms to `Codable`. 11 + /// - decoder: An optional JSON decoder that handles decoding. 12 + /// - encoder: An optional JSON encoder that handles encoding. 13 + /// - Returns: A conversion from `Data` to the given codable type. 14 + @inlinable 15 + public static func json<Value>( 16 + _ type: Value.Type, 17 + decoder: JSONDecoder = .init(), 18 + encoder: JSONEncoder = .init() 19 + ) -> Self where Self == Conversions.JSON<Value> { 20 + .init(type, decoder: decoder, encoder: encoder) 21 + } 22 + 23 + /// Transforms this conversion to `Data` into a conversion to the given codable type. 24 + /// 25 + /// A fluent version of ``Conversion/json(_:decoder:encoder:)-swift.type.method``. Equivalent to 26 + /// calling ``map(_:)`` with ``Conversion/json(_:decoder:encoder:)-swift.type.method``: 27 + /// 28 + /// ```swift 29 + /// Parse(.data.json(User.self)) 30 + /// // = 31 + /// Parse(.data.map(.json(User.self))) 32 + /// ``` 33 + /// 34 + /// - Parameters: 35 + /// - type: A type that conforms to `Codable`. 36 + /// - decoder: An optional JSON decoder that handles decoding. 37 + /// - encoder: An optional JSON encoder that handles encoding. 38 + /// - Returns: A conversion from this conversion's input to the given codable type. 39 + @inlinable 40 + public func json<Value>( 41 + _ type: Value.Type, 42 + decoder: JSONDecoder = .init(), 43 + encoder: JSONEncoder = .init() 44 + ) -> Conversions.Map<Self, Conversions.JSON<Value>> { 45 + self.map(.json(type, decoder: decoder, encoder: encoder)) 46 + } 47 + } 48 + 49 + extension Conversions { 50 + /// A conversion from `Data` to some codable type. 51 + /// 52 + /// You will not typically need to interact with this type directly. Instead you will usually use 53 + /// the ``Conversion/json(_:decoder:encoder:)-swift.type.method`` operation, which constructs this 54 + /// type. 55 + public struct JSON<Value: Codable>: Conversion { 56 + @usableFromInline 57 + let decoder: JSONDecoder 58 + 59 + @usableFromInline 60 + let encoder: JSONEncoder 61 + 62 + @inlinable 63 + public init( 64 + _ type: Value.Type, 65 + decoder: JSONDecoder = .init(), 66 + encoder: JSONEncoder = .init() 67 + ) { 68 + self.decoder = decoder 69 + self.encoder = encoder 70 + } 71 + 72 + @inlinable 73 + public func apply(_ input: Data) throws -> Value { 74 + try self.decoder.decode(Value.self, from: input) 75 + } 76 + 77 + @inlinable 78 + public func unapply(_ output: Value) throws -> Data { 79 + try self.encoder.encode(output) 80 + } 81 + } 82 + }
+66
Sources/FileTree/Conversions/LosslessStringConvertible.swift
··· 1 + extension Conversion { 2 + /// A conversion from a string to a lossless string-convertible type. 3 + /// 4 + /// See ``lossless(_:)-swift.method`` for a fluent version of this interface that transforms an 5 + /// existing conversion. 6 + /// 7 + /// - Parameter type: A type that conforms to `LosslessStringConvertible`. 8 + /// - Returns: A conversion from a string to the given type. 9 + @inlinable 10 + public static func lossless<NewOutput>( 11 + _ type: NewOutput.Type 12 + ) -> Self where Self == Conversions.FromLosslessString<NewOutput> { 13 + .init() 14 + } 15 + 16 + /// Transforms this conversion to a string into a conversion to the given lossless 17 + /// string-convertible type. 18 + /// 19 + /// A fluent version of ``Conversion/lossless(_:)-swift.type.method``. Equivalent to calling 20 + /// ``map(_:)`` with ``Conversion/lossless(_:)-swift.type.method``: 21 + /// 22 + /// ```swift 23 + /// stringConversion.lossless(NewOutput.self) 24 + /// // = 25 + /// stringConversion.map(.lossless(NewOutput.self)) 26 + /// ``` 27 + /// 28 + /// - Parameter type: A type that conforms to `LosslessStringConvertible`. 29 + /// - Returns: A conversion from a string to the given type. 30 + @inlinable 31 + public func lossless<NewOutput>( 32 + _ type: NewOutput.Type 33 + ) -> Conversions.Map<Self, Conversions.FromLosslessString<NewOutput>> { 34 + self.map(.lossless(NewOutput.self)) 35 + } 36 + } 37 + 38 + extension Conversions { 39 + /// A conversion from a string to a lossless string-convertible type. 40 + /// 41 + /// You will not typically need to interact with this type directly. Instead you will usually use 42 + /// the ``Conversion/lossless(_:)-swift.type.method`` operation, which constructs this type. 43 + public struct FromLosslessString<Output: LosslessStringConvertible>: Conversion { 44 + @usableFromInline 45 + init() {} 46 + 47 + @inlinable 48 + public func apply(_ input: String) throws -> Output { 49 + guard let output = Output(input) 50 + else { 51 + throw ConvertingError( 52 + """ 53 + lossless: Failed to convert \(input.debugDescription) to \(Output.self). 54 + """ 55 + ) 56 + } 57 + 58 + return output 59 + } 60 + 61 + @inlinable 62 + public func unapply(_ output: Output) -> String { 63 + output.description 64 + } 65 + } 66 + }
+106
Sources/FileTree/Conversions/MapValues.swift
··· 1 + // 2 + // MapValues.swift 3 + // OpenFestival 4 + // 5 + // Created by Woodrow Melling on 10/31/24. 6 + // 7 + 8 + extension Conversions { 9 + public struct MapValues<C: Conversion>: Conversion { 10 + var transform: C 11 + 12 + public init(_ transform: C) { 13 + self.transform = transform 14 + } 15 + 16 + public init(@ConversionBuilder _ build: () -> C) { 17 + self.transform = build() 18 + } 19 + 20 + public func apply(_ input: [C.Input]) throws -> [C.Output] { 21 + try input.map(transform.apply) 22 + } 23 + 24 + public func unapply(_ output: [C.Output]) throws -> [C.Input] { 25 + try output.map(transform.unapply) 26 + } 27 + } 28 + 29 + } 30 + 31 + extension Conversions { 32 + public struct MapKVPairs<KeyConversion: Conversion, ValueConversion: Conversion>: Conversion 33 + where 34 + KeyConversion.Input: Hashable & Sendable, 35 + KeyConversion.Output: Hashable & Sendable, 36 + ValueConversion.Input: Sendable, 37 + ValueConversion.Output: Sendable 38 + { 39 + public typealias Input = [KeyConversion.Input: ValueConversion.Input] 40 + public typealias Output = [KeyConversion.Output: ValueConversion.Output] 41 + 42 + var keyConversion: KeyConversion 43 + var valueConversion: ValueConversion 44 + 45 + public init(keyConversion: KeyConversion, valueConversion: ValueConversion) { 46 + self.keyConversion = keyConversion 47 + self.valueConversion = valueConversion 48 + } 49 + 50 + public func apply(_ input: Input) throws -> Output { 51 + try input.mapKVPairs( 52 + keyConversion.apply, 53 + valueConversion.apply 54 + ) 55 + } 56 + 57 + public func unapply(_ output: Output) throws -> Input { 58 + try output.mapKVPairs( 59 + keyConversion.unapply, 60 + valueConversion.unapply 61 + ) 62 + } 63 + } 64 + } 65 + 66 + extension Dictionary { 67 + func mapKVPairs<NewKey, NewValue>( 68 + _ keyTransform: @escaping (Key) throws -> NewKey, 69 + _ valueTransform: @escaping (Value) throws -> NewValue 70 + ) rethrows -> [NewKey: NewValue] { 71 + return try [NewKey: NewValue]( 72 + uniqueKeysWithValues: self.map { 73 + return try (keyTransform($0.0), valueTransform($0.1)) 74 + }) 75 + } 76 + } 77 + 78 + extension Sequence { 79 + /// - Parameters: 80 + /// - closure: Transformation to apply to each element 81 + /// - Returns: Array of transformed elements in original order 82 + public func concurrentMap<T: Sendable>( 83 + _ transform: @escaping @Sendable (Element) async throws -> T 84 + ) async rethrows -> [T] where Element: Sendable { 85 + return try await withThrowingTaskGroup(of: (value: T, offset: Int).self) { group in 86 + for (id, element) in self.enumerated() { 87 + group.addTask { 88 + try await (value: transform(element), offset: id) 89 + } 90 + } 91 + 92 + var array: [(value: T, offset: Int)] = [] 93 + array.reserveCapacity(self.underestimatedCount) 94 + for try await result in group { 95 + array.append(result) 96 + } 97 + 98 + // Could this sort be avoided somehow? Maybe with an OrderedDictionary? 99 + return array.sorted { lhs, rhs in 100 + lhs.offset < rhs.offset 101 + } 102 + .map(\.value) 103 + } 104 + } 105 + 106 + }
+173
Sources/FileTree/Conversions/Memberwise.swift
··· 1 + extension Conversion { 2 + /// A conversion from a tuple of values into a struct and a struct into a tuple of values, using a 3 + /// memberwise initializer. 4 + /// 5 + /// Useful for transforming the output of a ``ParserPrinter`` into a struct. 6 + /// 7 + /// For example, given a simple `Coordinate` struct, we can build a parser-printer using 8 + /// ``memberwise(_:)``: 9 + /// 10 + /// ```swift 11 + /// struct Coordinate { 12 + /// var x: Double 13 + /// var y: Double 14 + /// } 15 + /// 16 + /// let coord = ParsePrint(.memberwise(Coordinate.init(x:y:))) { 17 + /// "(" 18 + /// Double.parser() 19 + /// "," 20 + /// Double.parser() 21 + /// ")" 22 + /// } 23 + /// 24 + /// try coord.parse("(1,-2)") // Coordinate(x: 1.0, y: -2.0) 25 + /// coord.print(.init(x: -5, y: 10)) // "(-5.0,10.0)" 26 + /// ``` 27 + /// 28 + /// To transform the output of a ``ParserPrinter`` into an enum, see ``Conversion/case(_:)-4j2n7``. 29 + /// 30 + /// ## Careful usage 31 + /// 32 + /// This conversion works by using the memberwise initializer you supply to ``memberwise(_:)`` in 33 + /// order to turn tuples into a struct, and it uses `unsafeBitcast` to turn the struct back into 34 + /// a tuple. Because of this, it is _not_ valid to use ``memberwise(_:)`` with anything other than 35 + /// the default synthesized memberwise initializer that structs are given for free by the compiler 36 + /// as that function most correctly maps the data inside a struct to its tuple representation, 37 + /// even enforcing the order of the fields. 38 + /// 39 + /// If you alter the initializer in any way you run the risk of introducing subtle bugs into 40 + /// your parser-printer and potentially causing crashes. 41 + /// 42 + /// For example, suppose we provided an alternative initializer to `Coordinate` above that 43 + /// allowed you to create a coordinate from a radius and angle measured in degrees: 44 + /// 45 + /// ```swift 46 + /// extension Coordinate { 47 + /// init(radius: Double, angle: Double) { 48 + /// self.x = radius * cos(angle * Double.pi / 180) 49 + /// self.y = radius * sin(angle * Double.pi / 180) 50 + /// } 51 + /// } 52 + /// ``` 53 + /// 54 + /// This may seem innocent enough, but it is _not_ safe to use this initializer with 55 + /// ``memberwise(_:)``. The following parser-printer will correctly parse a radius and angle into 56 + /// an x/y coordinate: 57 + /// 58 + /// ```swift 59 + /// let coord = ParserPrint(.memberwise(Coordinate.init(radius:angle:))) { 60 + /// Double.parser() 61 + /// " @ " 62 + /// Double.parser() 63 + /// "°" 64 + /// } 65 + /// 66 + /// try coord.parse("1 @ 90°") // (x: 0, y: 1) 67 + /// ``` 68 + /// 69 + /// However, printing a coordinate will _not_ convert it back into a radius and angle, and 70 + /// instead will erroneously use (0, 1) as the radius and angle: 71 + /// 72 + /// ```swift 73 + /// try coord.print(.init(x: 0, y: 1)) // "0 @ 1°" 74 + /// ``` 75 + /// 76 + /// This means this parser-printer does not round trip (see <doc:Roundtripping>), _i.e._ if we 77 + /// parse and input and then print that output we do not get back the original input we started 78 + /// with: 79 + /// 80 + /// ```swift 81 + /// try coord.print(try coord.parse("1 @ 90°")) == "1 @ 90°" // ❌ 82 + /// ``` 83 + /// 84 + /// Further, it is possible to provide a custom initializer for a type that either re-orders the 85 + /// fields or add/removes fields, both of which will cause the underlying `unsafeBitCast` to 86 + /// crash. For example, we could have a `User` struct that holds onto a string for the bio and an 87 + /// integer for the id, and provide a custom initializer so that the id is provided first: 88 + /// 89 + /// ```swift 90 + /// struct User { 91 + /// let bio: String 92 + /// let id: Int 93 + /// init(id: Int, bio: String) { 94 + /// self.bio = bio 95 + /// self.id = id 96 + /// } 97 + /// } 98 + /// ``` 99 + /// 100 + /// However, using this initializer with ``memberwise(_:)`` will cause printing to crash because 101 + /// it will try to bitcast a `(String, Int)` struct into a `(Int, String)` tuple: 102 + /// 103 + /// ```swift 104 + /// let user = ParsePrint(.memberwise(User.init(id:bio:))) { 105 + /// Int.parser() 106 + /// "," 107 + /// Rest() 108 + /// } 109 + /// 110 + /// try user.print(.init(id: 42, bio: "Hello world!")) // ❌ 111 + /// ``` 112 + /// 113 + /// - Parameter initializer: A memberwise initializer where `Values` directly maps to the memory 114 + /// layout of `Root`, for example the internal, default initializer that is automatically 115 + /// synthesized for structs. 116 + /// - Returns: A conversion that can embed a tuple of values into a struct, and destructure a 117 + /// struct back into a tuple of values. 118 + @inlinable 119 + public static func memberwise<Values, Struct>( 120 + _ initializer: @escaping @Sendable (Values) -> Struct 121 + ) -> Self where Self == Conversions.Memberwise<Values, Struct> { 122 + .init(initializer: initializer) 123 + } 124 + } 125 + 126 + extension Conversions { 127 + public struct Memberwise<Values, Struct>: Conversion { 128 + @usableFromInline 129 + let initializer: @Sendable (Values) -> Struct 130 + 131 + @usableFromInline 132 + init(initializer: @escaping @Sendable (Values) -> Struct) { 133 + self.initializer = initializer 134 + } 135 + 136 + @inlinable 137 + public func apply(_ input: Values) -> Struct { 138 + self.initializer(input) 139 + } 140 + 141 + @inlinable 142 + public func unapply(_ output: Struct) throws -> Values { 143 + let ptr = unsafeBitCast(Struct.self as Any.Type, to: UnsafeRawPointer.self) 144 + guard ptr.load(as: Int.self) == 512 145 + else { 146 + throw ConvertingError( 147 + """ 148 + memberwise: Can't convert \(Values.self) to non-struct type \(Struct.self). This \ 149 + conversion should only be used with a memberwise initializer matching the memory layout \ 150 + of the struct. The "memberwise" initializer is the internal, compiler-generated \ 151 + initializer that specifies its arguments in the same order as the struct specifies its \ 152 + properties. 153 + """ 154 + ) 155 + } 156 + guard 157 + MemoryLayout<Struct>.alignment == MemoryLayout<Values>.alignment, 158 + MemoryLayout<Struct>.size == MemoryLayout<Values>.size 159 + else { 160 + throw ConvertingError( 161 + """ 162 + memberwise: Can't convert \(Values.self) type \(Struct.self) as their memory layouts \ 163 + differ. This conversion should only be used with a memberwise initializer matching the \ 164 + memory layout of the struct. The "memberwise" initializer is the internal, \ 165 + compiler-generated initializer that specifies its arguments in the same order as the \ 166 + struct specifies its properties. 167 + """ 168 + ) 169 + } 170 + return unsafeBitCast(output, to: Values.self) 171 + } 172 + } 173 + }
+65
Sources/FileTree/Conversions/ParseableFormatStyleConversion.swift
··· 1 + //#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) 2 + // import Foundation 3 + // 4 + // @available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) 5 + // extension Conversion { 6 + // /// A conversion that wraps a parseable format style. 7 + // /// 8 + // /// This conversion forwards its ``apply(_:)`` and ``unapply(_:)`` methods to the underlying 9 + // /// `ParseableFormatStyle` by invoking its parse strategy's `parse` method and its `format` 10 + // /// method. 11 + // /// 12 + // /// See ``formatted(_:)-swift.method`` for a fluent version of this interface that transforms an 13 + // /// existing conversion. 14 + // /// 15 + // /// - Parameter style: A parseable format style. 16 + // /// - Returns: A conversion from a string to the given type. 17 + // public static func formatted<Style>( 18 + // _ style: Style 19 + // ) -> Self where Self == Conversions.ParseableFormat<Style> { 20 + // .init(style: style) 21 + // } 22 + // 23 + // /// Transforms this conversion to a parseable format style's parse input into a conversion to 24 + // /// the parseable format style's parse output. 25 + // /// 26 + // /// A fluent version of ``Conversion/formatted(_:)-swift.type.method``. Equivalent to calling 27 + // /// ``map(_:)`` with ``Conversion/formatted(_:)-swift.type.method``: 28 + // /// 29 + // /// ```swift 30 + // /// Parse(.string.formatted(.currency("USD"))) 31 + // /// // = 32 + // /// Parse(.string.map(.formatted(.currency("USD")))) 33 + // /// ``` 34 + // /// 35 + // /// - Parameter type: A type that conforms to `LosslessStringConvertible`. 36 + // /// - Returns: A conversion from a string to the given type. 37 + // public func formatted<Style>( 38 + // _ style: Style 39 + // ) -> Conversions.Map<Self, Conversions.ParseableFormat<Style>> { 40 + // self.map(.formatted(style)) 41 + // } 42 + // } 43 + // 44 + // extension Conversions { 45 + // /// A conversion for a parseable format style. 46 + // /// 47 + // /// You will not typically need to interact with this type directly. Instead you will usually 48 + // /// use the ``Conversion/formatted(_:)-swift.type.method`` operation, which constructs this 49 + // /// type. 50 + // @available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) 51 + // public struct ParseableFormat<Style>: Conversion where Style: ParseableFormatStyle { 52 + // public let style: Style 53 + // 54 + // @inlinable 55 + // public func apply(_ input: Style.Strategy.ParseInput) throws -> Style.Strategy.ParseOutput { 56 + // try style.parseStrategy.parse(input) 57 + // } 58 + // 59 + // @inlinable 60 + // public func unapply(_ output: Style.FormatInput) -> Style.FormatOutput { 61 + // self.style.format(output) 62 + // } 63 + // } 64 + // } 65 + //#endif
+175
Sources/FileTree/Conversions/PrependableCollection.swift
··· 1 + import Foundation 2 + 3 + /// A collection that supports empty initialization and the ability to prepend a sequence of 4 + /// elements of elements to itself. 5 + /// 6 + /// `PrependableCollection` is a specialized subset of `RangeReplaceableCollection` that is tuned 7 + /// to incremental printing. 8 + /// 9 + /// In fact, any `RangeReplaceableCollection` can get a conformance for free: 10 + /// 11 + /// ```swift 12 + /// extension MyRangeReplaceableCollection: PrependableCollection {} 13 + /// ``` 14 + /// 15 + /// Because it is also less strict than `RangeReplaceableCollection`, it is an appropriate 16 + /// protocol to conform to for types that cannot and should not conform to 17 + /// `RangeReplaceableCollection` directly. 18 + /// 19 + /// For example, `Substring.UTF8View` is a common input for string parsers to parse from, but it 20 + /// does not conform to `RangeReplaceableCollection`. It does, however, conform to 21 + /// `PrependableCollection` by validating and prepending the given UTF-8 bytes to its underlying 22 + /// substring. So in order to write a parser against generic sequences of UTF-8 bytes, you would 23 + /// constrain its input against `PrependableCollection`. 24 + /// 25 + /// For example the following `Digits` parser is generic over an `Collection` of bytes, and its 26 + /// printer conformance further constraints its input to be prependable. 27 + /// 28 + /// ```swift 29 + /// struct Digits<Input: Collection>: Parser 30 + /// where 31 + /// Input.Element == UTF8.CodeUnit, // Required for working with a collection of bytes 32 + /// Input.SubSequence == Input // Required for the parser to consume from input 33 + /// { 34 + /// func parse(_ input: inout Input) throws -> Int { 35 + /// // Collect all bytes between ASCII "0" and "9" 36 + /// let prefix = input.prefix(while: { $0 >= .init(ascii: "0") && $0 <= .init(ascii: "9") }) 37 + /// 38 + /// // Attempt to convert to an `Int` 39 + /// guard let int = Int(prefix) else { 40 + /// struct ParseError: Error {} 41 + /// throw ParseError() 42 + /// } 43 + /// 44 + /// // Incrementally consume bytes from input 45 + /// input.removeFirst(prefix.count) 46 + /// 47 + /// return int 48 + /// } 49 + /// } 50 + /// 51 + /// extension Digits: ParserPrinter where Input: PrependableCollection { 52 + /// func print(_ output: Int, into input: inout Input) { 53 + /// // Convert `Int` to string's underlying bytes 54 + /// let bytes = String(output).utf8 55 + /// 56 + /// // Prepend bytes using `PrependableCollection` conformance. 57 + /// input.prepend(contentsOf: bytes) 58 + /// } 59 + /// } 60 + /// ``` 61 + /// 62 + /// The `Digits` parser-printer now works on any collection of UTF-8 code units, including 63 + /// `UTF8View` and `ArraySlice<UInt8>`: 64 + /// 65 + /// ```swift 66 + /// var input = "123"[...].utf8 67 + /// try Digits().parse(&input) // 123 68 + /// try Digits().print(123, into: &input) 69 + /// Substring(input) // "123" 70 + /// ``` 71 + /// 72 + /// ```swift 73 + /// var input = ArraySlice("123"[...].utf8) 74 + /// try Digits().parse(&input) // 123 75 + /// try Digits().print(123, into: &input) 76 + /// Substring(decoding: input, as: UTF8.self) // "123" 77 + /// ``` 78 + public protocol PrependableCollection<Element>: Collection, _EmptyInitializable { 79 + /// Inserts the elements of a sequence or collection to the beginning of this collection. 80 + /// 81 + /// The collection being prepended to allocates any additional necessary storage to hold the new 82 + /// elements. 83 + /// 84 + /// - Parameter newElements: The elements to append to the collection. 85 + mutating func prepend<S: Sequence>(contentsOf newElements: S) where S.Element == Element 86 + } 87 + 88 + extension PrependableCollection { 89 + /// Creates a new instance of a collection containing the elements of a sequence. 90 + /// 91 + /// - Parameter elements: The sequence of elements for the new collection. `elements` must be 92 + /// finite. 93 + @inlinable 94 + public init<S: Collection>(_ elements: S) where S.Element == Element { 95 + var collection = Self() 96 + collection.prepend(contentsOf: elements) 97 + self = collection 98 + } 99 + 100 + /// Adds an element to the beginning of the collection. 101 + /// 102 + /// - Parameter newElement: The element to prepend to the collection. 103 + @inlinable 104 + public mutating func prepend(_ newElement: Element) { 105 + self.prepend(contentsOf: CollectionOfOne(newElement)) 106 + } 107 + } 108 + 109 + extension RangeReplaceableCollection { 110 + @inlinable 111 + public mutating func prepend<S: Sequence>(contentsOf newElements: S) 112 + where S.Element == Element { 113 + var newSelf = Self() 114 + newSelf.append(contentsOf: newElements) 115 + newSelf.append(contentsOf: self) 116 + self = newSelf 117 + } 118 + } 119 + 120 + extension Array: PrependableCollection {} 121 + extension ArraySlice: PrependableCollection {} 122 + extension ContiguousArray: PrependableCollection {} 123 + extension Data: PrependableCollection {} 124 + extension Slice: PrependableCollection, _EmptyInitializable 125 + where Base: RangeReplaceableCollection {} 126 + extension String: PrependableCollection {} 127 + extension String.UnicodeScalarView: PrependableCollection {} 128 + extension Substring: PrependableCollection {} 129 + extension Substring.UnicodeScalarView: PrependableCollection {} 130 + 131 + extension String.UTF8View: PrependableCollection { 132 + @inlinable 133 + public init() { 134 + self = "".utf8 135 + } 136 + 137 + @inlinable 138 + public mutating func prepend<S: Sequence>(contentsOf elements: S) 139 + where S.Element == Element { 140 + var str = String(self) 141 + defer { self = str.utf8 } 142 + 143 + switch elements { 144 + case let elements as String.UTF8View: 145 + str.prepend(contentsOf: String(elements)) 146 + case let elements as Substring.UTF8View: 147 + str.prepend(contentsOf: Substring(elements)) 148 + default: 149 + str.prepend(contentsOf: Substring(decoding: Array(elements), as: UTF8.self)) 150 + } 151 + } 152 + } 153 + 154 + extension Substring.UTF8View: PrependableCollection { 155 + @inlinable 156 + public init() { 157 + self = ""[...].utf8 158 + } 159 + 160 + @inlinable 161 + public mutating func prepend<S: Sequence>(contentsOf elements: S) 162 + where S.Element == Element { 163 + var str = Substring(self) 164 + defer { self = str.utf8 } 165 + 166 + switch elements { 167 + case let elements as String.UTF8View: 168 + str.prepend(contentsOf: String(elements)) 169 + case let elements as Substring.UTF8View: 170 + str.prepend(contentsOf: Substring(elements)) 171 + default: 172 + str.prepend(contentsOf: Substring(decoding: Array(elements), as: UTF8.self)) 173 + } 174 + } 175 + }
+28
Sources/FileTree/Conversions/PrintChanges.swift
··· 1 + //// 2 + //// PrintChanges.swift 3 + //// OpenFestival 4 + //// 5 + //// Created by Woodrow Melling on 10/31/24. 6 + //// 7 + // 8 + //import CustomDump 9 + // 10 + //extension Conversion { 11 + // func _printChanges() -> some Conversion<Input, Output> { 12 + // AnyConversion( 13 + // apply: { 14 + // customDump($0, name: "Before \(Self.self).apply") 15 + // let result = try self.apply($0) 16 + // customDump(result, name: "After \(Self.self).apply") 17 + // 18 + // return result 19 + // }, 20 + // unapply: { 21 + // customDump($0, name: "Before \(Self.self).unapply") 22 + // let result = try self.unapply($0) 23 + // customDump(result, name: "After \(Self.self) unapply") 24 + // return result 25 + // } 26 + // ) 27 + // } 28 + //}
+104
Sources/FileTree/Conversions/RawRepresentable.swift
··· 1 + extension Conversion { 2 + /// A conversion from a given raw representable type's raw value to itself. 3 + /// 4 + /// This conversion is useful for mapping the output of a more primitive parser-printer into a 5 + /// raw representable value. 6 + /// 7 + /// For example, you may have a raw representable type that wraps a more primitive type for the 8 + /// purpose of strengthening type requirements in your APIs. One example is an identifier type 9 + /// that wraps an integer: 10 + /// 11 + /// ```swift 12 + /// struct UserID: RawRepresentable { 13 + /// var rawValue: Int 14 + /// } 15 + /// ``` 16 + /// 17 + /// You can transform an `Int` parser into a `UserID` parser by invoking ``Parser/map(_:)-18m9d`` 18 + /// with this conversion: 19 + /// 20 + /// ```swift 21 + /// let userID = Int.parser().map(.representing(UserID.self)) 22 + /// ``` 23 + /// 24 + /// See ``representing(_:)-swift.method`` for a fluent version of this interface that transforms 25 + /// an existing conversion. This fluent API is particularly useful when mapping string raw values 26 + /// directly from parsers of substrings and UTF-8 views, which require first transforming the 27 + /// parsed substring or UTF-8 view into a string via the ``string-swift.type.property-9owth`` and 28 + /// ``string-swift.type.property-3u2b5`` conversions. 29 + /// 30 + /// ```swift 31 + /// struct EmailAddress: RawRepresentable { 32 + /// var rawValue: String 33 + /// } 34 + /// 35 + /// let emailAddress = Parse(.string.representing(EmailAddress.self)) { 36 + /// Consumed { 37 + /// PrefixUpTo("@") 38 + /// "@" 39 + /// Rest() 40 + /// } 41 + /// } 42 + /// ``` 43 + /// 44 + /// - Parameter type: A type that conforms to `RawRepresentable`. 45 + /// - Returns: A conversion from a raw value to the given type. 46 + @inlinable 47 + public static func representing<NewOutput>( 48 + _ type: NewOutput.Type 49 + ) -> Self where Self == Conversions.FromRawValue<NewOutput> { 50 + .init() 51 + } 52 + 53 + /// Transforms this conversion to a raw value into a conversion to the given raw representable 54 + /// type. 55 + /// 56 + /// A fluent version of ``Conversion/representing(_:)-swift.type.method``. Equivalent to calling 57 + /// ``map(_:)`` with ``Conversion/representing(_:)-swift.type.method``: 58 + /// 59 + /// ```swift 60 + /// stringConversion.representing(EmailAddress.self) 61 + /// // = 62 + /// stringConversion.map(.representing(EmailAddress.self) 63 + /// ``` 64 + /// 65 + /// - Parameter type: A type that conforms to `RawRepresentable`. 66 + /// - Returns: A conversion from a raw value to the given type. 67 + @inlinable 68 + public func representing<NewOutput>( 69 + _ type: NewOutput.Type 70 + ) -> Conversions.Map<Self, Conversions.FromRawValue<NewOutput>> { 71 + self.map(.representing(NewOutput.self)) 72 + } 73 + } 74 + 75 + extension Conversions { 76 + /// A conversion from a raw value to a raw representable type. 77 + /// 78 + /// You will not typically need to interact with this type directly. Instead you will usually use 79 + /// the ``Conversion/representing(_:)-swift.type.method`` operation, which constructs this type. 80 + public struct FromRawValue<Output: RawRepresentable>: Conversion { 81 + @inlinable 82 + public init() {} 83 + 84 + @inlinable 85 + public func apply(_ input: Output.RawValue) throws -> Output { 86 + guard let output = Output(rawValue: input) 87 + else { 88 + var debugDescription = "" 89 + debugPrint(input, to: &debugDescription) 90 + throw ConvertingError( 91 + """ 92 + representing: Failed to convert \(debugDescription) to \(Output.self). 93 + """ 94 + ) 95 + } 96 + return output 97 + } 98 + 99 + @inlinable 100 + public func unapply(_ output: Output) -> Output.RawValue { 101 + output.rawValue 102 + } 103 + } 104 + }
+84
Sources/FileTree/Conversions/String.swift
··· 1 + extension Conversion where Self == Conversions.SubstringToString { 2 + /// A conversion from `Substring` to `String`. 3 + /// 4 + /// Useful for transforming a ``ParserPrinter``'s substring output into a more general-purpose 5 + /// string. 6 + /// 7 + /// ```swift 8 + /// let line = Prefix { $0 != "\n" }.map(.string) 9 + /// ``` 10 + @inlinable 11 + public static var string: Self { .init() } 12 + } 13 + 14 + extension Conversion where Output == Substring { 15 + /// Transforms this conversion to `Substring` into a conversion to `String`. 16 + /// 17 + /// A fluent version of ``Conversion/string-swift.type.property-3u2b5``. 18 + @inlinable 19 + public var string: Conversions.Map<Self, Conversions.SubstringToString> { self.map(.string) } 20 + } 21 + 22 + extension Conversion where Self == Conversions.BytesToString<Substring.UTF8View> { 23 + /// A conversion from `Substring.UTF8View` to `String`. 24 + /// 25 + /// Useful for transforming a ``ParserPrinter``'s UTF-8 output into a more general-purpose string. 26 + /// 27 + /// ```swift 28 + /// let line = Prefix { $0 != .init(ascii: "\n") }.map(.string) 29 + /// ``` 30 + @inlinable 31 + public static var string: Self { .init() } 32 + } 33 + 34 + extension Conversion where Output == Substring.UTF8View { 35 + /// Transforms this conversion to `Substring.UTF8View` into a conversion to `String`. 36 + /// 37 + /// A fluent version of ``Conversion/string-swift.type.property-9owth``. 38 + @inlinable 39 + public var string: Conversions.Map<Self, Conversions.BytesToString<Output>> { self.map(.string) } 40 + } 41 + 42 + extension Conversions { 43 + /// A conversion from a substring to a string. 44 + /// 45 + /// You will not typically need to interact with this type directly. Instead you will usually use 46 + /// the ``Conversion/string-swift.type.property-3u2b5`` operation, which constructs this type. 47 + public struct SubstringToString: Conversion { 48 + @inlinable 49 + public init() {} 50 + 51 + @inlinable 52 + public func apply(_ input: Substring) -> String { 53 + String(input) 54 + } 55 + 56 + @inlinable 57 + public func unapply(_ output: String) -> Substring { 58 + Substring(output) 59 + } 60 + } 61 + 62 + /// A conversion from a ``PrependableCollection`` of UTF-8 bytes to a string. 63 + /// 64 + /// You will not typically need to interact with this type directly. Instead you will usually use 65 + /// the ``Conversion/string-swift.type.property-9owth`` operation, which constructs this type. 66 + public struct BytesToString<Input: PrependableCollection>: Conversion 67 + where 68 + Input.SubSequence == Input, 69 + Input.Element == UTF8.CodeUnit 70 + { 71 + @inlinable 72 + public init() {} 73 + 74 + @inlinable 75 + public func apply(_ input: Input) -> String { 76 + String(decoding: input, as: UTF8.self) 77 + } 78 + 79 + @inlinable 80 + public func unapply(_ output: String) -> Input { 81 + .init(output.utf8) 82 + } 83 + } 84 + }
+89
Sources/FileTree/Conversions/Substring.swift
··· 1 + extension Conversion where Self == Conversions.UnicodeScalarViewToSubstring { 2 + /// A conversion from `Substring.UnicodeScalarView` to `Substring`. 3 + /// 4 + /// Useful when used with the ``From`` parser-printer to integrate a substring parser into a 5 + /// parser on unicode scalars. 6 + @inlinable 7 + public static var substring: Self { .init() } 8 + } 9 + 10 + extension Conversion where Output == Substring.UnicodeScalarView { 11 + /// Transforms this conversion to `Substring.UnicodeScalarView` into a conversion to `Substring`. 12 + /// 13 + /// A fluent version of ``Conversion/substring-swift.type.property-4r1aj``. 14 + @inlinable 15 + public var substring: Conversions.Map<Self, Conversions.UnicodeScalarViewToSubstring> { 16 + self.map(.substring) 17 + } 18 + } 19 + 20 + extension Conversion where Self == Conversions.UTF8ViewToSubstring { 21 + /// A conversion from `Substring.UTF8View` to `Substring`. 22 + /// 23 + /// Useful when used with the ``From`` parser-printer to integrate a substring parser into a 24 + /// parser on UTF-8 bytes. 25 + /// 26 + /// For example: 27 + /// 28 + /// ```swift 29 + /// Parse { 30 + /// "caf".utf8 31 + /// From(.substring) { 32 + /// "é" 33 + /// } 34 + /// } 35 + /// ``` 36 + @inlinable 37 + public static var substring: Self { .init() } 38 + } 39 + 40 + extension Conversion where Output == Substring.UTF8View { 41 + /// Transforms this conversion to `Substring.UTF8View` into a conversion to `Substring`. 42 + /// 43 + /// A fluent version of ``Conversion/substring-swift.type.property-1y3u3``. 44 + @inlinable 45 + public var substring: Conversions.Map<Self, Conversions.UTF8ViewToSubstring> { 46 + self.map(.substring) 47 + } 48 + } 49 + 50 + extension Conversions { 51 + /// A conversion from a unicode scalar view to its substring. 52 + /// 53 + /// You will not typically need to interact with this type directly. Instead you will usually use 54 + /// the ``Conversion/substring-swift.type.property-1y3u3`` and operation, which constructs this 55 + /// type under the hood. 56 + public struct UnicodeScalarViewToSubstring: Conversion { 57 + @inlinable 58 + public init() {} 59 + 60 + @inlinable 61 + public func apply(_ input: Substring.UnicodeScalarView) -> Substring { 62 + Substring(input) 63 + } 64 + 65 + @inlinable 66 + public func unapply(_ output: Substring) -> Substring.UnicodeScalarView { 67 + output.unicodeScalars 68 + } 69 + } 70 + 71 + /// A conversion from a UTF-8 view to its substring. 72 + /// 73 + /// You will not typically need to interact with this type directly. Instead you will usually use 74 + /// the ``Conversion/substring-swift.type.property-4r1aj`` operation, which constructs this type. 75 + public struct UTF8ViewToSubstring: Conversion { 76 + @inlinable 77 + public init() {} 78 + 79 + @inlinable 80 + public func apply(_ input: Substring.UTF8View) -> Substring { 81 + Substring(input) 82 + } 83 + 84 + @inlinable 85 + public func unapply(_ output: Substring) -> Substring.UTF8View { 86 + output.utf8 87 + } 88 + } 89 + }
+37
Sources/FileTree/Conversions/UTF8View.swift
··· 1 + extension Conversion where Self == Conversions.SubstringToUTF8View { 2 + /// A conversion from `Substring` to `Substring.UTF8View`. 3 + /// 4 + /// Useful when used with the ``From`` parser-printer to integrate a UTF-8 parser into a substring 5 + /// parser. 6 + @inlinable 7 + public static var utf8: Self { .init() } 8 + } 9 + 10 + extension Conversion where Output == Substring { 11 + /// Transforms this conversion to `Substring` into a conversion to `Substring.UTF8View`. 12 + /// 13 + /// A fluent version of ``Conversion/utf8-swift.type.property``. 14 + @inlinable 15 + public var utf8: Conversions.Map<Self, Conversions.SubstringToUTF8View> { self.map(.utf8) } 16 + } 17 + 18 + extension Conversions { 19 + /// A conversion from a substring to its UTF-8 view. 20 + /// 21 + /// You will not typically need to interact with this type directly. Instead you will usually use 22 + /// the ``Conversion/utf8-swift.type.property`` operation, which constructs this type. 23 + public struct SubstringToUTF8View: Conversion { 24 + @inlinable 25 + public init() {} 26 + 27 + @inlinable 28 + public func apply(_ input: Substring) -> Substring.UTF8View { 29 + input.utf8 30 + } 31 + 32 + @inlinable 33 + public func unapply(_ output: Substring.UTF8View) -> Substring { 34 + Substring(output) 35 + } 36 + } 37 + }