referencing/
uri.rs

1//! URI handling utilities for JSON Schema references.
2use fluent_uri::{
3    encoding::{encoder::Fragment, EStr, Encoder},
4    Uri, UriRef,
5};
6use once_cell::sync::Lazy;
7
8use crate::Error;
9pub use fluent_uri::encoding::encoder::Path;
10
11/// Resolves the URI reference against the given base URI and returns the target URI.
12///
13/// # Errors
14///
15/// Returns an error if base has not schema or there is a fragment.
16pub fn resolve_against(base: &Uri<&str>, uri: &str) -> Result<Uri<String>, Error> {
17    if uri.starts_with('#') && base.as_str().ends_with(uri) {
18        return Ok(base.to_owned());
19    }
20    Ok(UriRef::parse(uri)
21        .map_err(|error| Error::uri_reference_parsing_error(uri, error))?
22        .resolve_against(base)
23        .map_err(|error| Error::uri_resolving_error(uri, *base, error))?
24        .normalize())
25}
26
27/// Parses a URI reference from a string into a [`crate::Uri`].
28///
29/// # Errors
30///
31/// Returns an error if the input string does not conform to URI-reference from RFC 3986.
32pub fn from_str(uri: &str) -> Result<Uri<String>, Error> {
33    let uriref = UriRef::parse(uri)
34        .map_err(|error| Error::uri_reference_parsing_error(uri, error))?
35        .normalize();
36    if uriref.has_scheme() {
37        Ok(Uri::try_from(uriref.as_str())
38            .map_err(|error| Error::uri_parsing_error(uriref.as_str(), error))?
39            .into())
40    } else {
41        Ok(uriref
42            .resolve_against(&DEFAULT_ROOT_URI.borrow())
43            .map_err(|error| Error::uri_resolving_error(uri, DEFAULT_ROOT_URI.borrow(), error))?)
44    }
45}
46
47pub(crate) static DEFAULT_ROOT_URI: Lazy<Uri<String>> =
48    Lazy::new(|| Uri::parse("json-schema:///".to_string()).expect("Invalid URI"));
49
50pub type EncodedString = EStr<Fragment>;
51
52// Adapted from `https://github.com/yescallop/fluent-uri-rs/blob/main/src/encoding/table.rs#L153`
53pub fn encode_to(input: &str, buffer: &mut String) {
54    const HEX_TABLE: [u8; 512] = {
55        const HEX_DIGITS: &[u8; 16] = b"0123456789ABCDEF";
56
57        let mut i = 0;
58        let mut table = [0; 512];
59        while i < 256 {
60            table[i * 2] = HEX_DIGITS[i >> 4];
61            table[i * 2 + 1] = HEX_DIGITS[i & 0b1111];
62            i += 1;
63        }
64        table
65    };
66
67    for ch in input.chars() {
68        if Path::TABLE.allows(ch) {
69            buffer.push(ch);
70        } else {
71            for x in ch.encode_utf8(&mut [0; 4]).bytes() {
72                buffer.push('%');
73                buffer.push(HEX_TABLE[x as usize * 2] as char);
74                buffer.push(HEX_TABLE[x as usize * 2 + 1] as char);
75            }
76        }
77    }
78}