From 476af23c88033c6bacc21d8b35aeeedfc476a0ad Mon Sep 17 00:00:00 2001 From: Eric Rodrigues Pires Date: Wed, 22 Oct 2025 07:52:44 -0300 Subject: [PATCH] Update packages to 0.1.1 --- .../{duper-python.yml => duper-python.yaml} | 73 +++++++++++------- .github/workflows/zizmor.yaml | 25 ++++++ Cargo.lock | 8 +- README.md | 6 +- SPEC.md | 56 ++++++++------ axum_duper/CHANGELOG.md | 12 +++ axum_duper/Cargo.toml | 2 +- duper-python/CHANGELOG.md | 13 ++++ duper-python/Cargo.toml | 2 +- duper-vs-code/CHANGELOG.md | 13 ++++ duper-vs-code/README.md | 5 +- duper-vs-code/images/example-1.png | Bin 0 -> 34542 bytes duper-vs-code/{ => images}/logo.png | Bin duper-vs-code/package.json | 4 +- duper/CHANGELOG.md | 17 ++++ duper/Cargo.toml | 2 +- duper/README.md | 2 +- duper/src/grammar.pest | 23 +++++- duper/src/lib.rs | 4 +- duper/src/types.rs | 63 --------------- serde_duper/CHANGELOG.md | 12 +++ serde_duper/Cargo.toml | 2 +- 22 files changed, 213 insertions(+), 131 deletions(-) rename .github/workflows/{duper-python.yml => duper-python.yaml} (58%) create mode 100644 .github/workflows/zizmor.yaml create mode 100644 axum_duper/CHANGELOG.md create mode 100644 duper-python/CHANGELOG.md create mode 100644 duper-vs-code/CHANGELOG.md create mode 100644 duper-vs-code/images/example-1.png rename duper-vs-code/{ => images}/logo.png (100%) create mode 100644 duper/CHANGELOG.md delete mode 100644 duper/src/types.rs create mode 100644 serde_duper/CHANGELOG.md diff --git a/.github/workflows/duper-python.yml b/.github/workflows/duper-python.yaml similarity index 58% rename from .github/workflows/duper-python.yml rename to .github/workflows/duper-python.yaml index 250afe9..36407ad 100644 --- a/.github/workflows/duper-python.yml +++ b/.github/workflows/duper-python.yaml @@ -1,6 +1,9 @@ name: Build duper-python on: + push: + branches: ["main"] + tags: ["duper-python@*"] pull_request: workflow_dispatch: @@ -26,18 +29,22 @@ jobs: - runner: ubuntu-22.04 target: ppc64le steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - name: Checkout repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: true + - name: Setup Python + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: 3.x - name: Build wheels - uses: PyO3/maturin-action@v1 + uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4 with: target: ${{ matrix.platform.target }} args: --release --manifest-path duper-python/Cargo.toml --out dist --find-interpreter manylinux: auto - name: Upload wheels - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: wheels-linux-${{ matrix.platform.target }} path: dist @@ -56,18 +63,22 @@ jobs: - runner: ubuntu-22.04 target: armv7 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - name: Checkout repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: true + - name: Setup Python + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: 3.x - name: Build wheels - uses: PyO3/maturin-action@v1 + uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4 with: target: ${{ matrix.platform.target }} args: --release --manifest-path duper-python/Cargo.toml --out dist --find-interpreter manylinux: musllinux_1_2 - name: Upload wheels - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: wheels-musllinux-${{ matrix.platform.target }} path: dist @@ -82,18 +93,22 @@ jobs: - runner: windows-latest target: x86 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - name: Checkout repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: true + - name: Setup Python + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: 3.x architecture: ${{ matrix.platform.target }} - name: Build wheels - uses: PyO3/maturin-action@v1 + uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4 with: target: ${{ matrix.platform.target }} args: --release --manifest-path duper-python/Cargo.toml --out dist --find-interpreter - name: Upload wheels - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: wheels-windows-${{ matrix.platform.target }} path: dist @@ -108,17 +123,21 @@ jobs: - runner: macos-14 target: aarch64 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - name: Checkout repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: true + - name: Setup Python + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: 3.x - name: Build wheels - uses: PyO3/maturin-action@v1 + uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4 with: target: ${{ matrix.platform.target }} args: --release --manifest-path duper-python/Cargo.toml --out dist --find-interpreter - name: Upload wheels - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: wheels-macos-${{ matrix.platform.target }} path: dist @@ -126,14 +145,17 @@ jobs: sdist: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: true - name: Build sdist - uses: PyO3/maturin-action@v1 + uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4 with: command: sdist args: --manifest-path duper-python/Cargo.toml --out dist - name: Upload sdist - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: wheels-sdist path: dist @@ -141,23 +163,22 @@ jobs: release: name: Release runs-on: ubuntu-latest - if: ${{ github.event_name == 'workflow_dispatch' }} + if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }} needs: [linux, musllinux, windows, macos, sdist] permissions: - # Use to sign the release artifacts id-token: write - # Used to upload release artifacts contents: write - # Used to generate artifact attestation attestations: write steps: - - uses: actions/download-artifact@v4 + - name: Download artifact + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 - name: Generate artifact attestation - uses: actions/attest-build-provenance@v2 + uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0 with: subject-path: "wheels-*/*" - name: Publish to PyPI - uses: PyO3/maturin-action@v1 + if: ${{ startsWith(github.ref, 'refs/tags/') }} + uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4 env: MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }} with: diff --git a/.github/workflows/zizmor.yaml b/.github/workflows/zizmor.yaml new file mode 100644 index 0000000..c9adde5 --- /dev/null +++ b/.github/workflows/zizmor.yaml @@ -0,0 +1,25 @@ +name: GitHub Actions Security Analysis with zizmor + +on: + push: + branches: ["main"] + pull_request: + branches: ["**"] + +permissions: {} + +jobs: + zizmor: + name: Run zizmor + runs-on: ubuntu-latest + permissions: + security-events: write + contents: read + actions: read + steps: + - name: Checkout repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + - name: Run zizmor + uses: zizmorcore/zizmor-action@e673c3917a1aef3c65c972347ed84ccd013ecda4 # v0.2.0 diff --git a/Cargo.lock b/Cargo.lock index 215672c..e1544dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -103,7 +103,7 @@ dependencies = [ [[package]] name = "axum_duper" -version = "0.1.0" +version = "0.1.1" dependencies = [ "axum", "serde", @@ -308,7 +308,7 @@ dependencies = [ [[package]] name = "duper" -version = "0.1.0" +version = "0.1.1" dependencies = [ "insta", "miette", @@ -322,7 +322,7 @@ dependencies = [ [[package]] name = "duper-python" -version = "0.1.0" +version = "0.1.1" dependencies = [ "duper", "miette", @@ -1077,7 +1077,7 @@ dependencies = [ [[package]] name = "serde_duper" -version = "0.1.0" +version = "0.1.1" dependencies = [ "bytes", "chrono", diff --git a/README.md b/README.md index 06bb789..b0e7034 100644 --- a/README.md +++ b/README.md @@ -157,9 +157,9 @@ cargo add serde_duper Duper excels in a variety of use cases: -- In configuration files, its explicit types can better guide users. -- Thanks to its self-documenting identifiers, Duper seamlessly feels right at home in REST APIs. -- With its support for bytes, minimal syntax, and compatibility with JSON, Duper is also a great choice for data interchange. +- In configuration files where users are expected to swap out values, its explicit types can be a helpful guide. +- Thanks to its self-documenting identifiers, Duper feels right at home in REST APIs. +- With support for bytes, minimal syntax, and compatibility with JSON, Duper is also a great choice for data interchange. ## A formal specification for implementers diff --git a/SPEC.md b/SPEC.md index a84f3f6..8a75203 100644 --- a/SPEC.md +++ b/SPEC.md @@ -3,7 +3,10 @@

Duper: The format that's super!

-> Specification version 0.1.1 +

+ Specification version + GitHub license +

Duper aims to be a human-friendly extension of JSON with quality-of-life improvements, extra types, and semantic identifiers. @@ -12,7 +15,7 @@ Duper aims to be a human-friendly extension of JSON with quality-of-life improve - Duper is case-sensitive and must be a valid UTF-8 encoded Unicode document. - Whitespace means tab (U+0009), space (U+0020), line feed (U+000A), or carriage return (U+000D). - Newline means line feed (U+000A) or carriage return (U+000D). -- Files must have only one root value. Objects and arrays must be handled by a Duper parser, but other values may be allowed. +- Files must have only one root value. Objects and arrays as the root value must always be accepted by a Duper parser, but implementations may allow other values as the root value. ## Comments @@ -130,7 +133,7 @@ Defining a key multiple times is invalid. Note that plain keys, quoted keys, and { name: "Eric Rodrigues Pires", name: "Epic Eric", // INVALID - "name": "Eric", // INVALID + "n\x61me": "Eric", // INVALID r"name": "Eric", // INVALID } ``` @@ -149,22 +152,24 @@ A string may be either quoted or raw. For convenience, some popular characters have a compact escape sequence: -```plaintext -\b - backspace (U+0008) -\f - form feed (U+000C) -\n - line feed (U+000A) -\r - carriage return (U+000D) -\t - tab (U+0009) -\0 - null (U+0000) -\" - quote (U+0022) -\\ - backslash (U+005C) -\xHH - arbitrary byte (U+00HH) -\uHHHH - unicode (U+HHHH) +```duper +[ + r#" \b - backspace (U+0008) "#, + r#" \f - form feed (U+000C) "#, + r#" \n - line feed (U+000A) "#, + r#" \r - carriage return (U+000D) "#, + r#" \t - tab (U+0009) "#, + r#" \0 - null (U+0000) "#, + r#" \" - quote (U+0022) "#, + r#" \\ - backslash (U+005C) "#, + r#" \xHH - arbitrary byte (U+00HH) "#, + r#" \uHHHH - unicode (U+HHHH) "#, +] ``` Any Unicode character may be escaped with `\uHHHH` or a sequence of one or more `\xHH`. The escape codes must be Unicode [scalar values](https://unicode.org/glossary/#unicode_scalar_value). -Keep in mind that Duper strings are sequences of Unicode characters, _not_ byte sequences. For binary data, use [byte strings](#byte-strings). +Keep in mind that Duper strings are sequences of Unicode characters, _not_ byte sequences. Parsers should raise an error if a string contains invalid Unicode. For binary data, use [byte strings](#byte-strings). **Raw strings** start with the lowercase letter R, immediately followed by zero or more hash symbols `#`, immediately followed by a quotation mark `r"`. They end with a quotation mark `"`, followed by the same number of starting hash symbols `#`. They allow newlines and have no escaping whatsoever. @@ -227,6 +232,9 @@ For large numbers, you may use underscores between digits to enhance readability int6: 5_349_221, int7: 53_49_221, int8: 1_2_3_4_5, + // INVALID: 1__2, + // INVALID: _12, + // INVALID: 12_, } ``` @@ -250,7 +258,7 @@ Non-negative integer values may also be expressed in hexadecimal, octal, or bina } ``` -Implementations are free to support any integer size. It's recommended that at least 64-bit signed integers (from −2^63 to 2^63−1) are accepted and handled losslessly. If an integer cannot be represented losslessly, an error must be thrown. +Implementations are free to support any integer size. It's recommended that at least 64-bit signed integers (from −2^63 to 2^63−1) are accepted and handled losslessly. If an integer cannot be represented, it's recommended that implementations convert it into a float losslessly, or else convert it into a string, using an appropriate [identifier](#identifiers) in both cases. ## Floats @@ -279,7 +287,7 @@ An exponent part is an E (upper or lower case) followed by an integer part (whic The decimal point, if used, must be surrounded by at least one digit on each side. -``` +```duper { invalid_float_1: .7, // INVALID invalid_float_2: 7., // INVALID @@ -367,20 +375,22 @@ Tuples are similar to arrays, although parsers may choose to handle them differe } ``` +Any parenthesized expression must be interpreted as a tuple by parsers. + ## Identifiers -Identifiers are optional type-like annotations that wrap any kind of value, providing semantic meaning or hinting at special handling during parsing/validation. The first character must be an uppercase letter, followed by letters, numbers, underscores `_`, and hyphens `-`. They must start with an ASCII letter, or an underscore followed by a letter or digit. Sequences of underscores and hyphens are not allowed in the identifier. +Identifiers are optional type-like annotations that wrap any kind of value, providing semantic meaning or hinting at special handling during parsing/validation. Identified values are composed of the identifier name, followed by the value wrapped in parenthesis `(` and `)`. -The actual value must be wrapped in parenthesis `(` and `)`. +The first character must be an uppercase letter, followed by letters, numbers, underscores `_`, and hyphens `-`. They must start with an ASCII letter, or an underscore followed by a letter or digit. Sequences of underscores and hyphens are not allowed in the identifier. ```duper { user_id: Uuid("550e8400-e29b-41d4-a716-446655440000"), created: DateTime("2024-01-15T10:30:00Z"), - birthday: Date("1990-05-20"), + birthday: ISO-8601("1990-05-20"), price: Decimal("19.99"), weight: Kilograms(2.5), - color: RGB((255, 0, 128)), // Two sets of parenthesis for tuples + color: RGB((Red(255), Green(0), Blue(128))), // Two sets of parenthesis for tuples address: IPV4("192.168.1.1"), nested: Metadata({ version: Version("1.2.3"), @@ -406,7 +416,9 @@ Values may not contain more than one identifier. } ``` -Parsers should preserve identifier information. Deserializers may ignore identifiers, or use them for validation. Serializers may choose to output or omit identifiers. +Parsers should preserve identifier information. Deserializers may ignore identifiers, or use them for validation. Serializers may choose to output or omit identifiers by the user's request. + +Implementations are free to define their own identifiers with specific semantics. ## Filename Extension diff --git a/axum_duper/CHANGELOG.md b/axum_duper/CHANGELOG.md new file mode 100644 index 0000000..9ce2415 --- /dev/null +++ b/axum_duper/CHANGELOG.md @@ -0,0 +1,12 @@ +# Changelog + +## 0.1.1 (2025-10-22) + +### Changed + +- Update `serde_duper` to 0.1.1. +- Update README. + +## 0.1.0 (2025-10-20) + +Initial release. diff --git a/axum_duper/Cargo.toml b/axum_duper/Cargo.toml index 949feb2..07f6b32 100644 --- a/axum_duper/Cargo.toml +++ b/axum_duper/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "axum_duper" description = "Axum extractor/response for Duper." -version = "0.1.0" +version = "0.1.1" edition = "2024" license = "MIT" authors = ["Eric Rodrigues Pires "] diff --git a/duper-python/CHANGELOG.md b/duper-python/CHANGELOG.md new file mode 100644 index 0000000..d12bc56 --- /dev/null +++ b/duper-python/CHANGELOG.md @@ -0,0 +1,13 @@ +# Changelog + +## 0.1.1 (2025-10-22) + +### Changed + +- Update `duper` to 0.1.1. +- Update README. +- Update package information. + +## 0.1.0 (2025-10-20) + +Initial release. diff --git a/duper-python/Cargo.toml b/duper-python/Cargo.toml index 4d816e1..3bfff3d 100644 --- a/duper-python/Cargo.toml +++ b/duper-python/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "duper-python" description = "The format that's super!" -version = "0.1.0" +version = "0.1.1" edition = "2024" license = "MIT" authors = ["Eric Rodrigues Pires "] diff --git a/duper-vs-code/CHANGELOG.md b/duper-vs-code/CHANGELOG.md new file mode 100644 index 0000000..086b2ac --- /dev/null +++ b/duper-vs-code/CHANGELOG.md @@ -0,0 +1,13 @@ +# Changelog + +## 0.1.1 (2025-10-22) + +### Changed + +- Implement changes from specification 0.1.1. +- Update README. +- Update package information. + +## 0.1.0 (2025-10-20) + +Initial release. diff --git a/duper-vs-code/README.md b/duper-vs-code/README.md index 8157d3a..f9e8ff0 100644 --- a/duper-vs-code/README.md +++ b/duper-vs-code/README.md @@ -4,9 +4,12 @@

Duper for VS Code

+ Visual Studio Marketplace version GitHub license

-Duper highlighting support for VS Code. +Duper highlighting support for Visual Studio Code. + +![An example of Duper highlighting on Visual Studio Code.](images/example-1.png) [See the main project on GitHub.](https://github.com/EpicEric/duper) diff --git a/duper-vs-code/images/example-1.png b/duper-vs-code/images/example-1.png new file mode 100644 index 0000000000000000000000000000000000000000..bc7a0c2cf154fd6891a28183552162db6f48d3f6 GIT binary patch literal 34542 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 literal 0 HcmV?d00001 diff --git a/duper-vs-code/logo.png b/duper-vs-code/images/logo.png similarity index 100% rename from duper-vs-code/logo.png rename to duper-vs-code/images/logo.png diff --git a/duper-vs-code/package.json b/duper-vs-code/package.json index a057228..427375b 100644 --- a/duper-vs-code/package.json +++ b/duper-vs-code/package.json @@ -2,7 +2,7 @@ "name": "duper-vs-code", "displayName": "Duper", "description": "Duper support for Visual Studio Code", - "version": "0.1.0", + "version": "0.1.1", "engines": { "vscode": "^1.105.0" }, @@ -16,7 +16,7 @@ "categories": [ "Programming Languages" ], - "icon": "logo.png", + "icon": "images/logo.png", "contributes": { "languages": [ { diff --git a/duper/CHANGELOG.md b/duper/CHANGELOG.md new file mode 100644 index 0000000..311b4b8 --- /dev/null +++ b/duper/CHANGELOG.md @@ -0,0 +1,17 @@ +# Changelog + +## 0.1.1 (2025-10-22) + +### Changed + +- Implement changes from specification 0.1.1. +- Update README. +- Reuse buffers for visitors. + +### Removed + +- Unused types module. + +## 0.1.0 (2025-10-20) + +Initial release. diff --git a/duper/Cargo.toml b/duper/Cargo.toml index 3a920b0..939f70f 100644 --- a/duper/Cargo.toml +++ b/duper/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "duper" description = "The format that's super!" -version = "0.1.0" +version = "0.1.1" edition = "2024" license = "MIT" authors = ["Eric Rodrigues Pires "] diff --git a/duper/README.md b/duper/README.md index 9621d54..9960c51 100644 --- a/duper/README.md +++ b/duper/README.md @@ -4,8 +4,8 @@

Duper: The format that's super!

- GitHub stars Crates.io version + GitHub license

Duper aims to be a human-friendly extension of JSON with quality-of-life improvements, extra types, and semantic identifiers. diff --git a/duper/src/grammar.pest b/duper/src/grammar.pest index 1099d2c..d31c065 100644 --- a/duper/src/grammar.pest +++ b/duper/src/grammar.pest @@ -1,17 +1,23 @@ //! The parser rules used to build the Duper abstract syntax tree (AST). +// -- WHITESPACE -- + /// Matches whitespace, which is ignored. WHITESPACE = _{ " " | "\t" | "\r" | "\n" } /// Matches a comment, which is ignored. COMMENT = _{ "//" ~ (!"\n" ~ ANY)* | "/*" ~ (!"*/" ~ ANY)* ~ "*/" } +// -- ROOT -- + /// Matches a Duper trunk value, i.e. an object or array, at the root of the input. duper_trunk = { SOI ~ (identifier ~ "(" ~ (object | array) ~ ")" | object | array) ~ EOI } /// Matches any Duper value at the root of the input. duper_value = _{ SOI ~ described_value ~ EOI } +// -- VALUES -- + /// Matches an identifier for a Duper value. identifier = @{ UPPERCASE_LETTER ~ (("-" | "_")? ~ LETTER | NUMBER)* } @@ -21,6 +27,8 @@ described_value = { identifier ~ "(" ~ value ~ ")" | value } /// Matches any valid inner value. value = _{ object | array | tuple | quoted_string | raw_string | quoted_bytes | raw_bytes | float | integer | boolean | null } +// -- OBJECTS -- + /// Matches a Duper object. object = { "{" ~ ","? ~ "}" @@ -36,10 +44,11 @@ key = _{ quoted_string | raw_string | plain_key } /// Matches a Duper object's plain key. plain_key = @{ (ASCII_ALPHA | "_" ~ ASCII_ALPHANUMERIC) ~ (("_" | "-")? ~ ASCII_ALPHANUMERIC)* } +// -- TUPLES AND ARRAYS -- + /// Matches a Duper tuple. tuple = { - "()" - | "(" ~ "," ~ ")" + "(" ~ ","? ~ ")" | "(" ~ described_value ~ ("," ~ described_value)* ~ ","? ~ ")" } @@ -49,13 +58,15 @@ array = { | "[" ~ described_value ~ ("," ~ described_value)* ~ ","? ~ "]" } +// -- STRINGS AND BYTE STRINGS -- + /// Matches the characters inside a quoted string or a quoted byte string. quoted_inner = @{ char* } /// Matches the individual characters in a quoted string or a quoted byte string. char = { !("\"" | "\\") ~ ANY - | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") + | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t" | "0") | "\\" ~ ("x" ~ ASCII_HEX_DIGIT{2}) | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) } @@ -79,6 +90,8 @@ raw_bytes = ${ /// Matches the characters inside a raw string or a raw byte string. raw_inner = @{ (!("\"" ~ PEEK) ~ ANY)* } +// -- INTEGERS -- + /// Matches an integer. integer = { hex_integer | octal_integer | binary_integer | decimal_integer } @@ -106,6 +119,8 @@ binary_integer = @{ "0b" ~ binary_digits } /// Matches the digits of a binary integer. binary_digits = { ASCII_BIN_DIGIT ~ ("_"? ~ ASCII_BIN_DIGIT)* } +// -- FLOATING POINT NUMBERS -- + /// Matches a floating point number. float = @{ ("+" | "-")? ~ ("0" | ASCII_NONZERO_DIGIT ~ ("_"? ~ ASCII_DIGIT)*) ~ ((decimal ~ exponent?) | (decimal? ~ exponent)) @@ -117,6 +132,8 @@ decimal = { "." ~ ASCII_DIGIT ~ ("_"? ~ ASCII_DIGIT)* ~ (^"e" ~ ("+" | "-")? ~ A /// Matches the exponent of a floating point number. exponent = { ^"e" ~ ("+" | "-")? ~ ASCII_DIGIT ~ ("_"? ~ ASCII_DIGIT)* } +// -- BOOLEANS AND NULL -- + /// Matches a boolean. boolean = { "true" | "false" } diff --git a/duper/src/lib.rs b/duper/src/lib.rs index 14b4327..692c144 100644 --- a/duper/src/lib.rs +++ b/duper/src/lib.rs @@ -31,8 +31,8 @@ //! //! ## Other crates //! -//! - [`serde_duper`](https://docs.rs/serde_duper): Provides full serialization/ -//! deserialization support between Duper and native data types. +//! - [`serde_duper`](https://docs.rs/serde_duper): Provides full +//! serialization/deserialization support between Duper and native data types. //! - [`axum_duper`](https://docs.rs/axum_duper): Provides an extractor/response //! for use with [`axum`](https://docs.rs/axum). //! diff --git a/duper/src/types.rs b/duper/src/types.rs deleted file mode 100644 index 79d41ef..0000000 --- a/duper/src/types.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::ast::DuperIdentifier; - -#[non_exhaustive] -pub enum DuperTypes { - // Integer types - DecInteger, - HexInteger, - OctInteger, - BinInteger, - // String types - Uuid4, - Uuid, - Url, - IsoDatetime, - Datetime, - IsoDate, - Date, - IsoTime, - Time, - Duration, - Ip, - Ipv4, - Ipv6, - Cidr, - Cidrv4, - Cidrv6, - Regex, - Base64, - Jwt, -} - -impl TryFrom<&DuperIdentifier<'_>> for DuperTypes { - type Error = String; - - fn try_from(identifier: &DuperIdentifier<'_>) -> Result { - match identifier.0.as_ref() { - "DecInteger" => Ok(DuperTypes::DecInteger), - "HexInteger" => Ok(DuperTypes::HexInteger), - "OctInteger" => Ok(DuperTypes::OctInteger), - "BinInteger" => Ok(DuperTypes::BinInteger), - "Uuid" => Ok(DuperTypes::Uuid), - "Uuid4" => Ok(DuperTypes::Uuid4), - "Url" => Ok(DuperTypes::Url), - "Datetime" => Ok(DuperTypes::Datetime), - "IsoDatetime" => Ok(DuperTypes::IsoDatetime), - "Date" => Ok(DuperTypes::Date), - "IsoDate" => Ok(DuperTypes::IsoDate), - "Time" => Ok(DuperTypes::Time), - "IsoTime" => Ok(DuperTypes::IsoTime), - "Duration" => Ok(DuperTypes::Duration), - "Ip" => Ok(DuperTypes::Ip), - "Ipv4" => Ok(DuperTypes::Ipv4), - "Ipv6" => Ok(DuperTypes::Ipv6), - "Cidr" => Ok(DuperTypes::Cidr), - "Cidrv4" => Ok(DuperTypes::Cidrv4), - "Cidrv6" => Ok(DuperTypes::Cidrv6), - "Regex" => Ok(DuperTypes::Regex), - "Base64" => Ok(DuperTypes::Base64), - "Jwt" => Ok(DuperTypes::Jwt), - _ => Err(format!("Unsupported type {}", identifier.0.as_ref())), - } - } -} diff --git a/serde_duper/CHANGELOG.md b/serde_duper/CHANGELOG.md new file mode 100644 index 0000000..8b69bfd --- /dev/null +++ b/serde_duper/CHANGELOG.md @@ -0,0 +1,12 @@ +# Changelog + +## 0.1.1 (2025-10-22) + +### Changed + +- Update `duper` to 0.1.1. +- Update README. + +## 0.1.0 (2025-10-20) + +Initial release. diff --git a/serde_duper/Cargo.toml b/serde_duper/Cargo.toml index 483d16f..b419414 100644 --- a/serde_duper/Cargo.toml +++ b/serde_duper/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "serde_duper" description = "Serde support for Duper." -version = "0.1.0" +version = "0.1.1" edition = "2024" license = "MIT" authors = ["Eric Rodrigues Pires "] -- 2.51.2