Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
rust: [1.63.0, 1.82.0, stable, beta, nightly]
rust: [1.63.0, 1.83.0, stable, beta, nightly]
exclude:
- os: macos-latest
rust: 1.82.0
rust: 1.83.0
- os: windows-latest
rust: 1.82.0
rust: 1.83.0
- os: macos-latest
rust: beta
- os: windows-latest
Expand Down Expand Up @@ -49,9 +49,9 @@ jobs:
cp ci-lockfiles/1.63.Cargo.lock Cargo.lock
# Older Unicode versions have a different test file
cp idna/tests/IdnaTestV2-Unicode16.txt idna/tests/IdnaTestV2.txt
- name: Downgrade deps on Rust 1.82.0
- name: Downgrade deps on Rust 1.83.0
if: |
matrix.rust == '1.82.0'
matrix.rust == '1.83.0'
run: |
cargo update -p icu_normalizer --precise 2.0.1
cargo update -p icu_provider --precise 2.0.0
Expand All @@ -67,6 +67,8 @@ jobs:
# Run tests
- name: Run tests
run: cargo test
- name: Run percent-encoding-iri (iri) tests
run: cargo test -p percent-encoding-iri --features iri
# Run tests enabling the serde feature
- name: Run tests with the serde feature
run: cargo test --features "url/serde,url/expose_internals"
Expand All @@ -76,7 +78,7 @@ jobs:
if: |
matrix.os == 'windows-latest' &&
matrix.rust != '1.63.0' &&
matrix.rust != '1.82.0'
matrix.rust != '1.83.0'
run: cargo test --test debugger_visualizer --features "url/debugger_visualizer,url_debug_tests/debugger_visualizer" -- --test-threads=1 || echo "debugger test failed"
continue-on-error: true # Fails on GH actions, but not locally.
- name: Test `no_std` support
Expand Down
66 changes: 66 additions & 0 deletions .github/workflows/publish-percent-encoding-iri.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Publish https://crates.io/crates/percent-encoding-iri
#
# Prerequisites:
# - GitHub secret CARGO_REGISTRY_TOKEN (https://crates.io/settings/tokens) with publish scope.
# - Optional: create Environment "crates-io" (Settings → Environments), restrict to `main`,
# and add required reviewers so only the real publish job waits for approval (not dry-run).
#
# Usage: Actions → "Publish percent-encoding-iri" → Run workflow (only from `main`).
# Leave dry_run enabled to run tests + cargo publish --dry-run only.

name: Publish percent-encoding-iri

on:
workflow_dispatch:
branches:
- main
inputs:
dry_run:
description: "If true, only tests + cargo publish --dry-run (no upload)"
type: boolean
default: true

concurrency:
group: publish-percent-encoding-iri
cancel-in-progress: false

env:
CARGO_NET_GIT_FETCH_WITH_CLI: true

jobs:
validate:
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable

- name: Run tests (default features)
run: cargo test -p percent-encoding-iri

- name: Run tests with IRI feature
run: cargo test -p percent-encoding-iri --features iri

- name: Package validation (dry-run)
run: cargo publish -p percent-encoding-iri --dry-run

publish:
needs: validate
if: ${{ !inputs.dry_run }}
runs-on: ubuntu-latest
timeout-minutes: 15
environment: crates-io
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to confirm with @andrewsanchez if the release environment should be used instead.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, probably it is!
However, it should be released separately from Anki. Because this library needs to exist, be updated in Anki, and then Anki needs to be published.

permissions:
contents: read
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable

- name: Publish to crates.io
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we use Trusted Publishing instead? https://crates.io/docs/trusted-publishing

Copy link
Copy Markdown
Author

@fernandolins fernandolins Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we can do it, but we have to set it up on crates.io

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if I'm forgetting discussion about this, but do we really need to publish to crates.io? Why not something like this?

some-crate = { git = "https://github.com/your-username/forked-crate", rev = "a1b2c3d4" }

- name: Cache Cargo registry + git deps
  uses: actions/cache@v3
  with:
    path: |
      ~/.cargo/registry
      ~/.cargo/git        # <-- this caches your git dependencies
      target/
    key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since our main goal is to publish Anki, then yes, we will need to publish this fork that is used as a dependency in Anki project.

See more: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#local-paths-in-published-crates

run: cargo publish -p percent-encoding-iri
6 changes: 3 additions & 3 deletions ci-lockfiles/1.63.Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion form_urlencoded/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ std = ["alloc", "percent-encoding/std"]
alloc = ["percent-encoding/alloc"]

[dependencies]
percent-encoding = { version = "2.3.2", default-features = false, path = "../percent_encoding" }
percent-encoding = { package = "percent-encoding-iri", version = "2.3.2", default-features = false, path = "../percent_encoding" }

[package.metadata.docs.rs]
rustdoc-args = ["--generate-link-to-definition"]
21 changes: 16 additions & 5 deletions percent_encoding/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
[package]
name = "percent-encoding"
name = "percent-encoding-iri"
version = "2.3.2"
authors = ["The rust-url developers"]
description = "Percent encoding and decoding"
categories = ["no-std"]
repository = "https://github.com/servo/rust-url/"
authors = ["The rust-url developers", "Ankitects contributors"]
description = "Percent encoding with optional IRI-style UTF-8 (enable the `iri` feature). API-compatible with the `percent-encoding` crate name in Rust via `percent_encoding`."
documentation = "https://docs.rs/percent-encoding-iri"
repository = "https://github.com/ankitects/rust-url"
readme = "README.md"
license = "MIT OR Apache-2.0"
edition = "2018"
rust-version = "1.51"
keywords = ["percent-encoding", "iri", "url", "encoding", "no-std"]
categories = ["encoding", "no-std", "web-programming"]
publish = true
include = ["Cargo.toml", "README.md", "LICENSE-*", "src/**/*"]

[lib]
name = "percent_encoding"

[features]
default = ["std"]
std = ["alloc"]
alloc = []
# Encode only ASCII code units in `AsciiSet`; leave UTF-8 non-ASCII bytes literal (IRI-style).
iri = []

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--generate-link-to-definition"]
17 changes: 17 additions & 0 deletions percent_encoding/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# percent-encoding-iri

Same API as [`percent-encoding`](https://crates.io/crates/percent-encoding) from [rust-url](https://github.com/servo/rust-url), published on crates.io under this name so projects can depend on a **distinct package** while optionally enabling **IRI-style** behaviour (UTF-8 non-ASCII octets are not forced to percent-encode when the `iri` feature is on).

```toml
percent-encoding-iri = { version = "2.3.2", features = ["iri"] }
```

In Rust, import as **`percent_encoding`** (library name), for example:

```rust
use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
```

Without the `iri` feature, encoding matches the stock WHATWG-style rule.

Source: <https://github.com/ankitects/rust-url>
34 changes: 33 additions & 1 deletion percent_encoding/src/ascii_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,14 @@ impl AsciiSet {
}

pub(crate) fn should_percent_encode(&self, byte: u8) -> bool {
!byte.is_ascii() || self.contains(byte)
#[cfg(feature = "iri")]
{
byte.is_ascii() && self.contains(byte)
}
#[cfg(not(feature = "iri"))]
{
!byte.is_ascii() || self.contains(byte)
}
}

pub const fn add(&self, byte: u8) -> Self {
Expand Down Expand Up @@ -211,3 +218,28 @@ mod tests {
assert!(COMPLEMENT.contains(b'C'));
}
}

#[cfg(all(test, feature = "iri"))]
mod iri_tests {
use super::*;

#[test]
fn should_percent_encode_leaves_non_ascii_utf8_unencoded() {
let set = AsciiSet::EMPTY.add(b'/').add(b'%');
for &byte in "日本語.mp3".as_bytes() {
assert!(
!set.should_percent_encode(byte),
"byte {:#x} should not be percent-encoded",
byte
);
}
}

#[test]
fn should_percent_encode_still_encodes_ascii_in_set() {
let set = AsciiSet::EMPTY.add(b' ').add(b'?');
assert!(set.should_percent_encode(b' '));
assert!(set.should_percent_encode(b'?'));
assert!(!set.should_percent_encode(b'a'));
}
}
13 changes: 12 additions & 1 deletion percent_encoding/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ pub fn percent_encode_byte(byte: u8) -> &'static str {

/// Percent-encode the given bytes with the given set.
///
/// Non-ASCII bytes and bytes in `ascii_set` are encoded.
/// Bytes in `ascii_set` are encoded. Non-ASCII bytes are also encoded unless the crate
/// feature `iri` is enabled (IRI-style: UTF-8 non-ASCII octets pass through).
///
/// The return type:
///
Expand Down Expand Up @@ -418,6 +419,16 @@ mod tests {
);
}

#[cfg(feature = "iri")]
#[test]
fn utf8_percent_encode_unicode_filename_unchanged_for_path_set() {
const PATHISH: &AsciiSet = &CONTROLS.add(b'#').add(b'?').add(b'{').add(b'}');
assert_eq!(
super::utf8_percent_encode("日本語.mp3", PATHISH).collect::<String>(),
"日本語.mp3"
);
}

#[test]
fn percent_decode() {
assert_eq!(
Expand Down
2 changes: 1 addition & 1 deletion url/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ wasm-bindgen-test = "0.3"
[dependencies]
form_urlencoded = { version = "1.2.2", path = "../form_urlencoded", default-features = false, features = ["alloc"] }
idna = { version = "1.1.0", path = "../idna", default-features = false, features = ["alloc", "compiled_data"] }
percent-encoding = { version = "2.3.2", path = "../percent_encoding", default-features = false, features = ["alloc"] }
percent-encoding = { package = "percent-encoding-iri", version = "2.3.2", path = "../percent_encoding", default-features = false, features = ["alloc"] }
serde = { version = "1.0", optional = true, default-features = false }
serde_derive = { version = "1.0", optional = true, default-features = false }

Expand Down
Loading