From 8df9cc4df839c25cc06e938270ea5f7301fd4c72 Mon Sep 17 00:00:00 2001 From: Kyle McMahon Date: Fri, 5 Apr 2024 19:17:51 -0400 Subject: [PATCH] Implement Unicode scalars conversion --- .../Conversions/UnicodeScalarView.swift | 88 +++++++++++++++++ .../FromUnicodeScalarViewTests.swift | 94 +++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 Sources/Parsing/Conversions/UnicodeScalarView.swift create mode 100644 Tests/ParsingTests/FromUnicodeScalarViewTests.swift diff --git a/Sources/Parsing/Conversions/UnicodeScalarView.swift b/Sources/Parsing/Conversions/UnicodeScalarView.swift new file mode 100644 index 0000000000..10fbd595ec --- /dev/null +++ b/Sources/Parsing/Conversions/UnicodeScalarView.swift @@ -0,0 +1,88 @@ +extension Conversion where Self == Conversions.SubstringToUnicodeScalarView { + /// A conversion from `Substring` to `Substring.UnicodeScalarView`. + @inlinable + public static var unicodeScalars: Self { .init() } +} + +extension Conversion where Output == Substring { + /// Transforms this conversion to `Substring` into a conversion to `Substring.UnicodeScalarView`. + /// + /// A fluent version of ``Conversion/unicodeScalars-swift.type.property-7wivx``. + @inlinable + public var unicodeScalars: Conversions.Map { + self.map(.unicodeScalars) + } +} + +extension Conversion where Self == Conversions.BytesToUnicodeScalarView { + /// A conversion from `Substring.UTF8View` to `Substring.UnicodeScalarView`. + @inlinable + public static var unicodeScalars: Self { .init() } +} + +extension Conversion where Output == Substring.UTF8View { + /// Transforms this conversion to `Substring.UTF8View` into a conversion to `Substring.UnicodeScalarView`. + /// + /// A fluent version of ``Conversion/unicodeScalars-swift.type.property-1nefn``. + @inlinable + public var unicodeScalars: Conversions.Map> { + self.map(.unicodeScalars) + } +} + +extension Conversion where Self == Conversions.BytesToUnicodeScalarView> { + /// A conversion from `ArraySlice` to `Substring.UnicodeScalarView`. + @inlinable + public static var unicodeScalars: Self { .init() } +} + +extension Conversion where Output == ArraySlice { + /// Transforms this conversion to `ArraySlice` into a conversion to `Substring.UnicodeScalarView`. + /// + /// A fluent version of ``Conversion/unicodeScalars-swift.type.property-24r1i``. + @inlinable + public var unicodeScalars: Conversions.Map>> { + self.map(.unicodeScalars) + } +} + +extension Conversions { + /// A conversion from a `Substring` to `Substring.UnicodeScalarView`. + /// + /// You will not typically need to interact with this type directly. Instead you will usually use + /// the ``Conversion/unicodeScalars-swift.type.property-7wivx`` operation, which constructs this type. + public struct SubstringToUnicodeScalarView: Conversion { + @inlinable + public init() {} + + @inlinable + public func apply(_ input: Substring) throws -> Substring.UnicodeScalarView { + input.unicodeScalars + } + + @inlinable + public func unapply(_ output: Substring.UnicodeScalarView) throws -> Substring { + Substring(output) + } + } + + /// A conversion from a ``PrependableCollection`` of UTF-8 bytes to `Substring.UnicodeScalarView`. + /// + /// You will not typically need to interact with this type directly. Instead you will usually use + /// the ``Conversion/unicodeScalars-swift.type.property-1nefn`` and + /// ``Conversion/unicodeScalars-swift.type.property-24r1i`` operations, which construct this type. + public struct BytesToUnicodeScalarView: Conversion where Input.Element == UInt8 { + @inlinable + public init() {} + + @inlinable + public func apply(_ input: Input) throws -> Substring.UnicodeScalarView { + Substring(decoding: input, as: UTF8.self).unicodeScalars + } + + @inlinable + public func unapply(_ output: Substring.UnicodeScalarView) throws -> Input { + .init(Substring(output).utf8) + } + } +} diff --git a/Tests/ParsingTests/FromUnicodeScalarViewTests.swift b/Tests/ParsingTests/FromUnicodeScalarViewTests.swift new file mode 100644 index 0000000000..5c3ef64897 --- /dev/null +++ b/Tests/ParsingTests/FromUnicodeScalarViewTests.swift @@ -0,0 +1,94 @@ +import Parsing +import XCTest + +final class FromUnicodeScalarViewTests: XCTestCase { + func testSubstring() { + let p = Parse(input: Substring.self) { + "caf" + From(.unicodeScalars) { + "\u{00E9}".unicodeScalars + } + } + + var input = "caf\u{00E9}"[...] + XCTAssertNoThrow(try p.parse(&input)) + XCTAssert(input.isEmpty) + + XCTAssertThrowsError(try p.parse("cafe\u{0301}")) + } + + func testUTF8View() { + let p = Parse(input: Substring.UTF8View.self) { + "caf".utf8 + From(.unicodeScalars) { + "\u{00E9}".unicodeScalars + } + } + + var input = "caf\u{00E9}"[...].utf8 + XCTAssertNoThrow(try p.parse(&input)) + XCTAssert(input.isEmpty) + + XCTAssertThrowsError(try p.parse("cafe\u{0301}".utf8)) + } + + func testBytes() { + let p = Parse(input: ArraySlice.self) { + Array("caf".utf8) + From(.unicodeScalars) { + "\u{00E9}".unicodeScalars + } + } + + var input = Array("caf\u{00E9}".utf8)[...] + XCTAssertNoThrow(try p.parse(&input)) + XCTAssert(input.isEmpty) + + XCTAssertThrowsError(try p.parse(Array("cafe\u{0301}".utf8))) + } + + func testDeprecatedSubstring() { + let p = Parse(input: Substring.self) { + "caf" + FromUnicodeScalarView { + "\u{00E9}".unicodeScalars + } + } + + var input = "caf\u{00E9}"[...] + XCTAssertNoThrow(try p.parse(&input)) + XCTAssert(input.isEmpty) + + XCTAssertThrowsError(try p.parse("cafe\u{0301}")) + } + + func testDeprecatedUTF8View() { + let p = Parse(input: Substring.UTF8View.self) { + "caf".utf8 + FromUnicodeScalarView { + "\u{00E9}".unicodeScalars + } + } + + var input = "caf\u{00E9}"[...].utf8 + XCTAssertNoThrow(try p.parse(&input)) + XCTAssert(input.isEmpty) + + XCTAssertThrowsError(try p.parse("cafe\u{0301}".utf8)) + } + + func testDeprecatedBytes() { + let p = Parse(input: ArraySlice.self) { + Array("caf".utf8) + FromUnicodeScalarView { + "\u{00E9}".unicodeScalars + } + } + + var input = Array("caf\u{00E9}".utf8)[...] + XCTAssertNoThrow(try p.parse(&input)) + XCTAssert(input.isEmpty) + + XCTAssertThrowsError(try p.parse(Array("cafe\u{0301}".utf8))) + } +}