Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for no_std #1

Merged
merged 11 commits into from
Apr 20, 2023
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
5 changes: 5 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ jobs:
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
# Add toolchain for no_std tests
- run: rustup toolchain install nightly
- run: rustup target add aarch64-unknown-none
- run: cargo build --all-targets
# Run tests
- name: Run tests
Expand All @@ -49,6 +52,8 @@ jobs:
run: cargo test --test debugger_visualizer --features "url/serde,url/debugger_visualizer" -- --test-threads=1
- name: Test `no_std` support
run: cargo test --no-default-features --features=alloc
- name: Build `aarch64-unknown-none` with `no_std`
run: cargo +nightly build -Zbuild-std=core,alloc --target aarch64-unknown-none -v --release --no-default-features --features=alloc

WASM:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion data-url/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ alloc = []

[dev-dependencies]
tester = "0.9"
serde = {version = "1.0", features = ["derive"]}
serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] }
serde_json = "1.0"

[lib]
Expand Down
2 changes: 1 addition & 1 deletion idna/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ name = "unit"
assert_matches = "1.3"
bencher = "0.1"
tester = "0.9"
serde_json = "1.0"
serde_json = { version = "1.0" }

[dependencies]
unicode-bidi = { version = "0.3.10", default-features = false, features = ["hardcoded-data"] }
Expand Down
2 changes: 1 addition & 1 deletion idna/src/uts46.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ impl Idna {
return Errors::default();
}
let mut errors = processing(domain, self.config, &mut self.normalized, out);
self.output = std::mem::replace(out, String::with_capacity(out.len()));
self.output = core::mem::replace(out, String::with_capacity(out.len()));
let mut first = true;
for label in self.output.split('.') {
if !first {
Expand Down
3 changes: 1 addition & 2 deletions idna/tests/punycode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@

use crate::test::TestFn;
use idna::punycode::{decode, encode_str};
use serde_json::map::Map;
use serde_json::Value;
use serde_json::{map::Map, Value};
use std::str::FromStr;

fn one_test(decoded: &str, encoded: &str) {
Expand Down
15 changes: 10 additions & 5 deletions url/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,23 @@ debugger_test = "0.1"
debugger_test_parser = "0.1"

[dependencies]
form_urlencoded = { version = "1.1.0", path = "../form_urlencoded" }
idna = { version = "0.3.0", path = "../idna" }
percent-encoding = { version = "2.2.0", path = "../percent_encoding" }
serde = {version = "1.0", optional = true, features = ["derive"]}
form_urlencoded = { version = "1.1.0", path = "../form_urlencoded", default-features = false, features = ["alloc"] }
idna = { version = "0.3.0", path = "../idna", default-features = false, features = ["alloc"] }
percent-encoding = { version = "2.2.0", path = "../percent_encoding", default-features = false, features = ["alloc"] }
data-url = { version = "0.2.0", path = "../data-url", default-features = false, features = ["alloc"] }
serde = {version = "1.0", optional = true, default-features = false, features = ["alloc", "derive"]}
no-std-net = { version = "0.6.0", default-features = false }

[features]
default = []
default = ["std"]
std = ["idna/std", "percent-encoding/std", "form_urlencoded/std", "no-std-net/std", "alloc"]
alloc = []
# UNSTABLE FEATURES (requires Rust nightly)
# Enable to use the #[debugger_visualizer] attribute.
debugger_visualizer = []
# Expose internal offsets of the URL.
expose_internals = []
serde = ["dep:serde", "no-std-net/serde"]

[[bench]]
name = "parse_url"
Expand Down
13 changes: 10 additions & 3 deletions url/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,16 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::cmp;
use std::fmt::{self, Formatter};
use std::net::{Ipv4Addr, Ipv6Addr};
use alloc::{
borrow::ToOwned,
string::{String, ToString},
vec::Vec,
};
use core::{
cmp,
fmt::{self, Formatter},
};
use no_std_net::{Ipv4Addr, Ipv6Addr};

use percent_encoding::{percent_decode, utf8_percent_encode, CONTROLS};
#[cfg(feature = "serde")]
Expand Down
139 changes: 111 additions & 28 deletions url/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,27 +127,44 @@ url = { version = "2", features = ["serde"] }
feature(debugger_visualizer),
debugger_visualizer(natvis_file = "../../debug_metadata/url.natvis")
)]
#![no_std]

pub use form_urlencoded;

// For forwards compatibility
#[cfg(feature = "std")]
extern crate std;

#[macro_use]
extern crate alloc;

#[cfg(not(feature = "alloc"))]
compile_error!("the `alloc` feature must be enabled");

#[cfg(feature = "serde")]
extern crate serde;

use crate::host::HostInternal;
use crate::parser::{to_u32, Context, Parser, SchemeType, PATH_SEGMENT, USERINFO};
use percent_encoding::{percent_decode, percent_encode, utf8_percent_encode};
use std::borrow::Borrow;
use std::cmp;
use std::fmt::{self, Write};
use std::hash;
use std::io;
use std::mem;
use std::net::{IpAddr, SocketAddr, ToSocketAddrs};
use std::ops::{Range, RangeFrom, RangeTo};
use std::path::{Path, PathBuf};
use std::str;

use std::convert::TryFrom;
use crate::parser::{to_u32, Context, Parser, SchemeType, USERINFO};
use alloc::borrow::ToOwned;
use alloc::string::{String, ToString};
use core::borrow::Borrow;
use core::cmp;
use core::convert::TryFrom;
use core::fmt::{self, Write};
use core::hash;
use core::mem;
use core::ops::{Range, RangeFrom, RangeTo};
use core::str;
use no_std_net::IpAddr;
use percent_encoding::utf8_percent_encode;

#[cfg(feature = "std")]
use std::{
io,
net::{SocketAddr, ToSocketAddrs},
path::{Path, PathBuf},
};

pub use crate::host::Host;
pub use crate::origin::{OpaqueOrigin, Origin};
Expand Down Expand Up @@ -1237,10 +1254,11 @@ impl Url {
/// })
/// }
/// ```
#[cfg(feature = "std")]
pub fn socket_addrs(
&self,
default_port_number: impl Fn() -> Option<u16>,
) -> io::Result<Vec<SocketAddr>> {
) -> io::Result<alloc::vec::Vec<SocketAddr>> {
// Note: trying to avoid the Vec allocation by returning `impl AsRef<[SocketAddr]>`
// causes borrowck issues because the return value borrows `default_port_number`:
//
Expand All @@ -1249,6 +1267,7 @@ impl Url {
// > This RFC proposes that *all* type parameters are considered in scope
// > for `impl Trait` in return position

// TODO: Return custom error type to support no_std
fn io_result<T>(opt: Option<T>, message: &str) -> io::Result<T> {
opt.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, message))
}
Expand Down Expand Up @@ -1310,9 +1329,23 @@ impl Url {
///
/// ```
/// use url::Url;
/// # use std::error::Error;
///
/// # fn run() -> Result<(), Box<dyn Error>> {
/// # use url::ParseError;
/// # #[derive(Debug)]
/// # /// A simple wrapper error struct for `no_std` support
/// # struct TestError;
/// # impl From<ParseError> for TestError {
/// # fn from(value: ParseError) -> Self {
/// # TestError {}
/// # }
/// # }
/// # impl From<&str> for TestError {
/// # fn from(value: &str) -> Self {
/// # TestError {}
/// # }
/// # }
///
/// # fn run() -> Result<(), TestError> {
/// let url = Url::parse("https://example.com/foo/bar")?;
/// let mut path_segments = url.path_segments().ok_or_else(|| "cannot be base")?;
/// assert_eq!(path_segments.next(), Some("foo"));
Expand Down Expand Up @@ -1717,9 +1750,22 @@ impl Url {
///
/// ```
/// use url::Url;
/// # use std::error::Error;
/// # use url::ParseError;
/// # #[derive(Debug)]
/// # /// A simple wrapper error struct for `no_std` support
/// # struct TestError;
/// # impl From<ParseError> for TestError {
/// # fn from(value: ParseError) -> Self {
/// # TestError {}
/// # }
/// # }
/// # impl From<&str> for TestError {
/// # fn from(value: &str) -> Self {
/// # TestError {}
/// # }
/// # }
///
/// # fn run() -> Result<(), Box<dyn Error>> {
/// # fn run() -> Result<(), TestError> {
/// let mut url = Url::parse("ssh://example.net:2048/")?;
///
/// url.set_port(Some(4096)).map_err(|_| "cannot be base")?;
Expand All @@ -1736,9 +1782,22 @@ impl Url {
///
/// ```rust
/// use url::Url;
/// # use std::error::Error;
/// # use url::ParseError;
/// # #[derive(Debug)]
/// # /// A simple wrapper error struct for `no_std` support
/// # struct TestError;
/// # impl From<ParseError> for TestError {
/// # fn from(value: ParseError) -> Self {
/// # TestError {}
/// # }
/// # }
/// # impl From<&str> for TestError {
/// # fn from(value: &str) -> Self {
/// # TestError {}
/// # }
/// # }
///
/// # fn run() -> Result<(), Box<dyn Error>> {
/// # fn run() -> Result<(), TestError> {
/// let mut url = Url::parse("https://example.org/")?;
///
/// url.set_port(Some(443)).map_err(|_| "cannot be base")?;
Expand Down Expand Up @@ -2419,7 +2478,12 @@ impl Url {
/// # run().unwrap();
/// # }
/// ```
#[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))]
///
/// This method is only available if the `std` Cargo feature is enabled.
#[cfg(all(
feature = "std",
any(unix, windows, target_os = "redox", target_os = "wasi")
))]
#[allow(clippy::result_unit_err)]
pub fn from_file_path<P: AsRef<Path>>(path: P) -> Result<Url, ()> {
let mut serialization = "file://".to_owned();
Expand Down Expand Up @@ -2456,7 +2520,12 @@ impl Url {
///
/// Note that `std::path` does not consider trailing slashes significant
/// and usually does not include them (e.g. in `Path::parent()`).
#[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))]
///
/// This method is only available if the `std` Cargo feature is enabled.
#[cfg(all(
feature = "std",
any(unix, windows, target_os = "redox", target_os = "wasi")
))]
#[allow(clippy::result_unit_err)]
pub fn from_directory_path<P: AsRef<Path>>(path: P) -> Result<Url, ()> {
let mut url = Url::from_file_path(path)?;
Expand Down Expand Up @@ -2572,8 +2641,13 @@ impl Url {
/// or if `Path::new_opt()` returns `None`.
/// (That is, if the percent-decoded path contains a NUL byte or,
/// for a Windows path, is not UTF-8.)
///
/// This method is only available if the `std` Cargo feature is enabled.
#[inline]
#[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))]
#[cfg(all(
feature = "std",
any(unix, windows, target_os = "redox", target_os = "wasi")
))]
#[allow(clippy::result_unit_err)]
pub fn to_file_path(&self) -> Result<PathBuf, ()> {
if let Some(segments) = self.path_segments() {
Expand Down Expand Up @@ -2777,11 +2851,13 @@ impl<'de> serde::Deserialize<'de> for Url {
}
}

#[cfg(any(unix, target_os = "redox", target_os = "wasi"))]
#[cfg(all(feature = "std", any(unix, target_os = "redox", target_os = "wasi")))]
fn path_to_file_url_segments(
path: &Path,
serialization: &mut String,
) -> Result<(u32, HostInternal), ()> {
use crate::parser::PATH_SEGMENT;
use percent_encoding::percent_encode;
#[cfg(any(unix, target_os = "redox"))]
use std::os::unix::prelude::OsStrExt;
#[cfg(target_os = "wasi")]
Expand All @@ -2807,20 +2883,23 @@ fn path_to_file_url_segments(
Ok((host_end, HostInternal::None))
}

#[cfg(windows)]
#[cfg(all(feature = "std", windows))]
fn path_to_file_url_segments(
path: &Path,
serialization: &mut String,
) -> Result<(u32, HostInternal), ()> {
path_to_file_url_segments_windows(path, serialization)
}

#[cfg(feature = "std")]
// Build this unconditionally to alleviate https://github.com/servo/rust-url/issues/102
#[cfg_attr(not(windows), allow(dead_code))]
fn path_to_file_url_segments_windows(
path: &Path,
serialization: &mut String,
) -> Result<(u32, HostInternal), ()> {
use crate::parser::PATH_SEGMENT;
use percent_encoding::percent_encode;
use std::path::{Component, Prefix};
if !path.is_absolute() {
return Err(());
Expand Down Expand Up @@ -2879,11 +2958,13 @@ fn path_to_file_url_segments_windows(
Ok((host_end, host_internal))
}

#[cfg(any(unix, target_os = "redox", target_os = "wasi"))]
#[cfg(all(feature = "std", any(unix, target_os = "redox", target_os = "wasi")))]
fn file_url_segments_to_pathbuf(
host: Option<&str>,
segments: str::Split<'_, char>,
) -> Result<PathBuf, ()> {
use alloc::vec::Vec;
use percent_encoding::percent_decode;
use std::ffi::OsStr;
#[cfg(any(unix, target_os = "redox"))]
use std::os::unix::prelude::OsStrExt;
Expand Down Expand Up @@ -2924,20 +3005,22 @@ fn file_url_segments_to_pathbuf(
Ok(path)
}

#[cfg(windows)]
#[cfg(all(feature = "std", windows))]
fn file_url_segments_to_pathbuf(
host: Option<&str>,
segments: str::Split<char>,
) -> Result<PathBuf, ()> {
file_url_segments_to_pathbuf_windows(host, segments)
}

#[cfg(feature = "std")]
// Build this unconditionally to alleviate https://github.com/servo/rust-url/issues/102
#[cfg_attr(not(windows), allow(dead_code))]
fn file_url_segments_to_pathbuf_windows(
host: Option<&str>,
mut segments: str::Split<'_, char>,
) -> Result<PathBuf, ()> {
use percent_encoding::percent_decode;
let mut string = if let Some(host) = host {
r"\\".to_owned() + host
} else {
Expand Down
3 changes: 2 additions & 1 deletion url/src/origin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
use crate::host::Host;
use crate::parser::default_port;
use crate::Url;
use std::sync::atomic::{AtomicUsize, Ordering};
use alloc::{borrow::ToOwned, string::String};
use core::sync::atomic::{AtomicUsize, Ordering};

pub fn url_origin(url: &Url) -> Origin {
let scheme = url.scheme();
Expand Down
Loading