Skip to content
Merged
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
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: CI

on:
pull_request:
push:
branches:
- main

jobs:
test-feature-matrix:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- name: no-default-features
args: --no-default-features
- name: default-features
args: ""
- name: all-features
args: --all-features
- name: feature-hash
args: --no-default-features --features hash
- name: feature-stream
args: --no-default-features --features stream
- name: feature-serialize
args: --no-default-features --features serialize
- name: feature-uuid
args: --no-default-features --features uuid
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable

- name: Cache cargo registry + build
uses: Swatinem/rust-cache@v2

- name: Run unit tests (${{ matrix.name }})
run: cargo test ${{ matrix.args }} --verbose
12 changes: 8 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "pathlink"
version = "0.4.0"
version = "0.5.0"
authors = ["code@tinychain.net"]
edition = "2021"
edition = "2024"
license = "Apache-2.0"
description = "A URL type whose path can also be used as a filesystem path, for Rust"
repository = "https://github.com/TinyChain-Inc/pathlink.git"
Expand All @@ -21,11 +21,15 @@ uuid = ["hr-id/uuid"]
[dependencies]
async-hash = { version = "0.5", optional = true }
derive_more = { version = "1.0", features=["display"] }
destream = { path = "../destream", version = "0.10.1", optional = true }
destream = { version = "0.10", optional = true }
get-size = "0.1"
get-size-derive = "0.1"
hex = { version = "0.4", optional = true }
hr-id = { path = "../hr-id", version = "0.7.0" }
hr-id = "0.7"
safecast = "0.2"
serde = { version = "1.0", optional = true }
smallvec = "1.13"
url = "2.5"

[dev-dependencies]
serde_json = "1.0"
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,35 @@
# pathlink
A URL type whose path can also be used as a filesystem path, for Rust

A URL type whose path can also be used as a filesystem path, for Rust.

`pathlink` supports absolute HTTP and HTTPS links with IPv4, bracketed IPv6,
and domain-name hosts, plus absolute path-only links:

```rust
use pathlink::{Domain, Link};

let http: Link = "http://127.0.0.1:8702/api".parse().expect("HTTP link");
let https: Link = "https://example.com/api".parse().expect("HTTPS link");
let idn: Link = "https://bücher.example/api".parse().expect("IDN link");
let local: Link = "/api".parse().expect("path-only link");

assert_eq!(idn.to_string(), "https://xn--bcher-kva.example/api");

let domain = Domain::new("EXAMPLE.COM").expect("domain");
assert_eq!(domain.as_str(), "example.com");
```

Host parsing intentionally accepts only the URL components represented by
`Host`: a scheme, host address, and optional port. Userinfo, paths, queries,
and fragments are rejected when parsing a `Host`.

For HTTP and HTTPS links, an empty path is equivalent to the root path according
to IETF RFC 9110 Section 4.2.3, so parsing `https://example.com` canonicalizes and
displays it as `https://example.com/`.

Domain names are normalized to ASCII/Punycode through the `url` crate's IDNA
handling and then validated as DNS-style labels. Display, equality, ordering,
hashing, and serialization use that normalized form. `Address::Domain` stores a
validated `Domain` value backed by immutable shared string storage, so domain
addresses cannot be constructed from an arbitrary `String` and cloning them does
not clone the underlying domain string.
Loading
Loading