From 35653b04c9d6838f596c55356aab1ee9cd36702f Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 17 Feb 2022 13:58:07 +0100 Subject: [PATCH 01/10] Make form_urlencoded no_std compatible --- form_urlencoded/src/lib.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/form_urlencoded/src/lib.rs b/form_urlencoded/src/lib.rs index b0916e42b..a952b7007 100644 --- a/form_urlencoded/src/lib.rs +++ b/form_urlencoded/src/lib.rs @@ -12,10 +12,14 @@ //! //! Converts between a string (such as an URL’s query string) //! and a sequence of (name, value) pairs. +#![no_std] +extern crate alloc; + +use alloc::borrow::{Borrow, Cow, ToOwned}; +use alloc::string::String; +use core::str; use percent_encoding::{percent_decode, percent_encode_byte}; -use std::borrow::{Borrow, Cow}; -use std::str; /// Convert a byte string in the `application/x-www-form-urlencoded` syntax /// into a iterator of (name, value) pairs. From 194122c1dbbd31bfb560ec3aa33ed2925d310b8a Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 12 Aug 2021 19:08:53 +0200 Subject: [PATCH 02/10] Make data-url no_std compatible --- data-url/src/forgiving_base64.rs | 2 ++ data-url/src/lib.rs | 6 ++++++ data-url/src/mime.rs | 5 +++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/data-url/src/forgiving_base64.rs b/data-url/src/forgiving_base64.rs index 4f8550ed2..390d88bc4 100644 --- a/data-url/src/forgiving_base64.rs +++ b/data-url/src/forgiving_base64.rs @@ -1,5 +1,7 @@ //! +use alloc::vec::Vec; + #[derive(Debug)] pub struct InvalidBase64(InvalidBase64Details); diff --git a/data-url/src/lib.rs b/data-url/src/lib.rs index 4a4f376f4..32a14c442 100644 --- a/data-url/src/lib.rs +++ b/data-url/src/lib.rs @@ -14,6 +14,12 @@ //! assert_eq!(body, b"Hello World!"); //! assert!(fragment.is_none()); //! ``` +#![no_std] + +#[macro_use] +extern crate alloc; + +use alloc::{string::String, vec::Vec}; macro_rules! require { ($condition: expr) => { diff --git a/data-url/src/mime.rs b/data-url/src/mime.rs index a4c4f5aa4..baca4582f 100644 --- a/data-url/src/mime.rs +++ b/data-url/src/mime.rs @@ -1,5 +1,6 @@ -use std::fmt::{self, Write}; -use std::str::FromStr; +use alloc::{borrow::ToOwned, string::String, vec::Vec}; +use core::fmt::{self, Write}; +use core::str::FromStr; /// #[derive(Debug, PartialEq, Eq)] From 422a260f3806d827bc1d41880e4e8784c8bb4a45 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 17 Feb 2022 14:10:28 +0100 Subject: [PATCH 03/10] Make idna no_std compatible Add default feature flag "std" that enables a `std::error::Error` impl for `Errors`. --- idna/Cargo.toml | 8 ++++++-- idna/src/lib.rs | 8 ++++++++ idna/src/punycode.rs | 7 ++++--- idna/src/uts46.rs | 11 +++++++---- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/idna/Cargo.toml b/idna/Cargo.toml index 9566cc72e..7caa44595 100644 --- a/idna/Cargo.toml +++ b/idna/Cargo.toml @@ -12,6 +12,10 @@ rust-version = "1.45" [lib] doctest = false +[features] +default = ["std"] +std = [] + [[test]] name = "tests" harness = false @@ -26,8 +30,8 @@ tester = "0.9" serde_json = "1.0" [dependencies] -unicode-bidi = "0.3" -unicode-normalization = "0.1.17" +unicode-bidi = { version = "0.3.7", default-features = false } +unicode-normalization = { version = "0.1.17", default-features = false } [[bench]] name = "all" diff --git a/idna/src/lib.rs b/idna/src/lib.rs index 37d638741..a4d03c08a 100644 --- a/idna/src/lib.rs +++ b/idna/src/lib.rs @@ -31,11 +31,19 @@ //! > This document specifies a mechanism //! > that minimizes the impact of this transition for client software, //! > allowing client software to access domains that are valid under either system. +#![no_std] + +extern crate alloc; + +#[cfg(feature = "std")] +extern crate std; #[cfg(test)] #[macro_use] extern crate assert_matches; +use alloc::string::String; + pub mod punycode; mod uts46; diff --git a/idna/src/punycode.rs b/idna/src/punycode.rs index 21955f359..faef379ca 100644 --- a/idna/src/punycode.rs +++ b/idna/src/punycode.rs @@ -13,8 +13,9 @@ //! `encode_str` and `decode_to_string` provide convenience wrappers //! that convert from and to Rust’s UTF-8 based `str` and `String` types. -use std::char; -use std::u32; +use alloc::{string::String, vec::Vec}; +use core::char; +use core::u32; // Bootstring parameters for Punycode static BASE: u32 = 36; @@ -168,7 +169,7 @@ impl Decoder { } pub(crate) struct Decode<'a> { - base: std::str::Chars<'a>, + base: core::str::Chars<'a>, pub(crate) insertions: &'a [(usize, char)], inserted: usize, position: usize, diff --git a/idna/src/uts46.rs b/idna/src/uts46.rs index 9ee83bc88..6c7d82cd4 100644 --- a/idna/src/uts46.rs +++ b/idna/src/uts46.rs @@ -11,7 +11,9 @@ use self::Mapping::*; use crate::punycode; -use std::{error::Error as StdError, fmt}; + +use alloc::string::String; +use core::fmt; use unicode_bidi::{bidi_class, BidiClass}; use unicode_normalization::char::is_combining_mark; use unicode_normalization::{is_nfc, UnicodeNormalization}; @@ -70,10 +72,10 @@ fn find_char(codepoint: char) -> &'static Mapping { } struct Mapper<'a> { - chars: std::str::Chars<'a>, + chars: core::str::Chars<'a>, config: Config, errors: &'a mut Errors, - slice: Option>, + slice: Option>, } impl<'a> Iterator for Mapper<'a> { @@ -697,7 +699,8 @@ impl From for Result<(), Errors> { } } -impl StdError for Errors {} +#[cfg(feature = "std")] +impl std::error::Error for Errors {} impl fmt::Display for Errors { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { From 81ee0741aad668d4028521225f75e3a2de5f7c72 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 5 Oct 2021 14:36:13 +0200 Subject: [PATCH 04/10] Test no_std support --- .github/workflows/main.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 74b4c1610..e6cff540c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -42,6 +42,12 @@ jobs: with: command: test args: --all-features + - uses: actions-rs/cargo@v1 + # --no-default-features only available since 1.51.0 + if: matrix.rust != '1.45.0' + with: + command: test + args: --no-default-features WASM: runs-on: ubuntu-latest From 67d2a4c49d2f1414fc2aa614e29c9f7e2b4cb5f8 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 12 Aug 2021 19:12:51 +0200 Subject: [PATCH 05/10] Initial work at making url no_std compatible --- url/Cargo.toml | 7 ++-- url/src/host.rs | 7 ++-- url/src/lib.rs | 70 +++++++++++++++++++++++++++------------- url/src/origin.rs | 5 ++- url/src/parser.rs | 14 ++++---- url/src/path_segments.rs | 3 +- url/src/quirks.rs | 2 ++ url/src/slicing.rs | 3 +- 8 files changed, 75 insertions(+), 36 deletions(-) diff --git a/url/Cargo.toml b/url/Cargo.toml index 3955078b5..e49f42fce 100644 --- a/url/Cargo.toml +++ b/url/Cargo.toml @@ -26,12 +26,13 @@ bencher = "0.1" [dependencies] form_urlencoded = { version = "1.0.0", path = "../form_urlencoded" } -idna = { version = "0.2.0", path = "../idna", optional = true } +idna = { version = "0.2.0", default-features = false, path = "../idna", optional = true } percent-encoding = { version = "2.1.0", path = "../percent_encoding" } -serde = {version = "1.0", optional = true, features = ["derive"]} +serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] } [features] -default = ["idna"] +default = ["std", "idna"] +std = ["idna/std"] [[bench]] name = "parse_url" diff --git a/url/src/host.rs b/url/src/host.rs index 4678cb8a1..df0890fec 100644 --- a/url/src/host.rs +++ b/url/src/host.rs @@ -6,8 +6,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::cmp; -use std::fmt::{self, Formatter}; +use alloc::borrow::ToOwned; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::cmp; +use core::fmt::{self, Formatter}; use std::net::{Ipv4Addr, Ipv6Addr}; use percent_encoding::{percent_decode, utf8_percent_encode, CONTROLS}; diff --git a/url/src/lib.rs b/url/src/lib.rs index 8bdd378b7..b0555f430 100644 --- a/url/src/lib.rs +++ b/url/src/lib.rs @@ -131,6 +131,10 @@ url = { version = "2", default-features = false } */ #![doc(html_root_url = "https://docs.rs/url/2.2.2")] +#![no_std] +#[macro_use] +extern crate alloc; +extern crate std; pub use form_urlencoded; @@ -138,20 +142,25 @@ pub use form_urlencoded; 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 percent_encoding::utf8_percent_encode; +use std::net::IpAddr; +#[cfg(feature = "std")] +use std::{ + io, + net::{SocketAddr, ToSocketAddrs}, + path::{Path, PathBuf}, +}; pub use crate::host::Host; pub use crate::origin::{OpaqueOrigin, Origin}; @@ -1144,10 +1153,11 @@ impl Url { /// }) /// } /// ``` + #[cfg(feature = "std")] pub fn socket_addrs( &self, default_port_number: impl Fn() -> Option, - ) -> io::Result> { + ) -> io::Result> { // Note: trying to avoid the Vec allocation by returning `impl AsRef<[SocketAddr]>` // causes borrowck issues because the return value borrows `default_port_number`: // @@ -1156,6 +1166,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(opt: Option, message: &str) -> io::Result { opt.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, message)) } @@ -2314,7 +2325,9 @@ 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>(path: P) -> Result { let mut serialization = "file://".to_owned(); @@ -2351,7 +2364,9 @@ 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>(path: P) -> Result { let mut url = Url::from_file_path(path)?; @@ -2467,8 +2482,10 @@ 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 { if let Some(segments) = self.path_segments() { @@ -2672,11 +2689,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")] @@ -2702,7 +2721,7 @@ 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, @@ -2710,12 +2729,15 @@ fn path_to_file_url_segments( 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(()); @@ -2770,11 +2792,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 { + 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; @@ -2810,7 +2834,7 @@ 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, @@ -2818,12 +2842,14 @@ fn file_url_segments_to_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 { + use percent_encoding::percent_decode; let mut string = if let Some(host) = host { r"\\".to_owned() + host } else { diff --git a/url/src/origin.rs b/url/src/origin.rs index 838be55bd..c3d64fe00 100644 --- a/url/src/origin.rs +++ b/url/src/origin.rs @@ -9,7 +9,10 @@ use crate::host::Host; use crate::parser::default_port; use crate::Url; -use std::sync::atomic::{AtomicUsize, Ordering}; +use alloc::borrow::ToOwned; +use alloc::string::String; +use core::sync::atomic::{AtomicUsize, Ordering}; +use idna::domain_to_unicode; pub fn url_origin(url: &Url) -> Origin { let scheme = url.scheme(); diff --git a/url/src/parser.rs b/url/src/parser.rs index ed4ca3c4e..4b8df3a6b 100644 --- a/url/src/parser.rs +++ b/url/src/parser.rs @@ -6,9 +6,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::error::Error; -use std::fmt::{self, Formatter, Write}; -use std::str; +use alloc::borrow::ToOwned; +use alloc::string::{String, ToString}; +use core::fmt::{self, Formatter, Write}; +use core::str; use crate::host::{Host, HostInternal}; use crate::Url; @@ -72,7 +73,8 @@ macro_rules! simple_enum_error { } } -impl Error for ParseError {} +#[cfg(feature = "std")] +impl std::error::Error for ParseError {} simple_enum_error! { EmptyHost => "empty host", @@ -1107,7 +1109,7 @@ impl<'a> Parser<'a> { while let (Some(c), remaining) = input.split_first() { if let Some(digit) = c.to_digit(10) { port = port * 10 + digit; - if port > ::std::u16::MAX as u32 { + if port > core::u16::MAX as u32 { return Err(ParseError::InvalidPort); } has_any_digit = true; @@ -1541,7 +1543,7 @@ pub fn ascii_alpha(ch: char) -> bool { #[inline] pub fn to_u32(i: usize) -> ParseResult { - if i <= ::std::u32::MAX as usize { + if i <= core::u32::MAX as usize { Ok(i as u32) } else { Err(ParseError::Overflow) diff --git a/url/src/path_segments.rs b/url/src/path_segments.rs index 29afc1e7e..131f0b82d 100644 --- a/url/src/path_segments.rs +++ b/url/src/path_segments.rs @@ -8,7 +8,8 @@ use crate::parser::{self, to_u32, SchemeType}; use crate::Url; -use std::str; +use alloc::string::String; +use core::str; /// Exposes methods to manipulate the path of an URL that is not cannot-be-base. /// diff --git a/url/src/quirks.rs b/url/src/quirks.rs index 3ecb3c24d..6ec774657 100644 --- a/url/src/quirks.rs +++ b/url/src/quirks.rs @@ -11,6 +11,8 @@ //! Unless you need to be interoperable with web browsers, //! you probably want to use `Url` method instead. +use alloc::string::{String, ToString}; + use crate::parser::{default_port, Context, Input, Parser, SchemeType}; use crate::{Host, ParseError, Position, Url}; diff --git a/url/src/slicing.rs b/url/src/slicing.rs index a90337bb6..1c6fe7616 100644 --- a/url/src/slicing.rs +++ b/url/src/slicing.rs @@ -6,8 +6,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use core::ops::{Index, Range, RangeFrom, RangeFull, RangeTo}; + use crate::Url; -use std::ops::{Index, Range, RangeFrom, RangeFull, RangeTo}; impl Index for Url { type Output = str; From bb7955a4cd014c2915255b0c0d928ae52dd7d344 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sat, 8 Apr 2023 17:17:54 +0200 Subject: [PATCH 06/10] Fixed testcases for no_std --- url/Cargo.toml | 4 +- url/src/host.rs | 14 ++++--- url/src/lib.rs | 77 +++++++++++++++++++++++++++++++----- url/src/origin.rs | 4 +- url/src/parser.rs | 1 - url/src/path_segments.rs | 85 +++++++++++++++++++++++++++++++++++----- url/tests/unit.rs | 22 +++++++++-- 7 files changed, 172 insertions(+), 35 deletions(-) diff --git a/url/Cargo.toml b/url/Cargo.toml index 9b512f6d7..27a2d7b61 100644 --- a/url/Cargo.toml +++ b/url/Cargo.toml @@ -34,11 +34,9 @@ percent-encoding = { version = "2.2.0", path = "../percent_encoding" } serde = {version = "1.0", optional = true, features = ["derive"]} [features] -[features] -default = ["std", "idna"] +default = ["std"] std = ["idna/std", "alloc"] alloc = [] -default = [] # UNSTABLE FEATURES (requires Rust nightly) # Enable to use the #[debugger_visualizer] attribute. debugger_visualizer = [] diff --git a/url/src/host.rs b/url/src/host.rs index c9ad2e759..1f31a7340 100644 --- a/url/src/host.rs +++ b/url/src/host.rs @@ -6,11 +6,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use alloc::borrow::ToOwned; -use alloc::string::{String, ToString}; -use alloc::vec::Vec; -use core::cmp; -use core::fmt::{self, Formatter}; +use alloc::{ + borrow::ToOwned, + string::{String, ToString}, + vec::Vec, +}; +use core::{ + cmp, + fmt::{self, Formatter}, +}; use std::net::{Ipv4Addr, Ipv6Addr}; use percent_encoding::{percent_decode, utf8_percent_encode, CONTROLS}; diff --git a/url/src/lib.rs b/url/src/lib.rs index 799070843..522f2893b 100644 --- a/url/src/lib.rs +++ b/url/src/lib.rs @@ -130,6 +130,15 @@ url = { version = "2", features = ["serde"] } pub use form_urlencoded; +// For forwards compatibility +#[cfg(feature = "std")] +extern crate std; + +extern crate alloc; + +#[cfg(not(feature = "alloc"))] +compile_error!("the `alloc` feature must be enabled"); + #[cfg(feature = "serde")] extern crate serde; @@ -1317,9 +1326,23 @@ impl Url { /// /// ``` /// use url::Url; - /// # use std::error::Error; /// - /// # fn run() -> Result<(), Box> { + /// # use url::ParseError; + /// # #[derive(Debug)] + /// # /// A simple wrapper error struct for `no_std` support + /// # struct TestError; + /// # impl From 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")); @@ -1724,9 +1747,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 for TestError { + /// # fn from(value: ParseError) -> Self { + /// # TestError {} + /// # } + /// # } + /// # impl From<&str> for TestError { + /// # fn from(value: &str) -> Self { + /// # TestError {} + /// # } + /// # } /// - /// # fn run() -> Result<(), Box> { + /// # fn run() -> Result<(), TestError> { /// let mut url = Url::parse("ssh://example.net:2048/")?; /// /// url.set_port(Some(4096)).map_err(|_| "cannot be base")?; @@ -1743,9 +1779,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 for TestError { + /// # fn from(value: ParseError) -> Self { + /// # TestError {} + /// # } + /// # } + /// # impl From<&str> for TestError { + /// # fn from(value: &str) -> Self { + /// # TestError {} + /// # } + /// # } /// - /// # fn run() -> Result<(), Box> { + /// # fn run() -> Result<(), TestError> { /// let mut url = Url::parse("https://example.org/")?; /// /// url.set_port(Some(443)).map_err(|_| "cannot be base")?; @@ -2428,7 +2477,10 @@ impl Url { /// ``` /// /// 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")))] + #[cfg(all( + feature = "std", + any(unix, windows, target_os = "redox", target_os = "wasi") + ))] #[allow(clippy::result_unit_err)] pub fn from_file_path>(path: P) -> Result { let mut serialization = "file://".to_owned(); @@ -2467,7 +2519,10 @@ impl Url { /// and usually does not include them (e.g. in `Path::parent()`). /// /// 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")))] + #[cfg(all( + feature = "std", + any(unix, windows, target_os = "redox", target_os = "wasi") + ))] #[allow(clippy::result_unit_err)] pub fn from_directory_path>(path: P) -> Result { let mut url = Url::from_file_path(path)?; @@ -2586,7 +2641,10 @@ impl Url { /// /// This method is only available if the `std` Cargo feature is enabled. #[inline] - #[cfg(all(feature = "std", 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 { if let Some(segments) = self.path_segments() { @@ -2902,7 +2960,6 @@ fn file_url_segments_to_pathbuf( host: Option<&str>, segments: str::Split<'_, char>, ) -> Result { - use alloc::vec::Vec; use percent_encoding::percent_decode; use std::ffi::OsStr; #[cfg(any(unix, target_os = "redox"))] diff --git a/url/src/origin.rs b/url/src/origin.rs index b693ded6a..8dd716a8e 100644 --- a/url/src/origin.rs +++ b/url/src/origin.rs @@ -9,10 +9,8 @@ use crate::host::Host; use crate::parser::default_port; use crate::Url; -use alloc::borrow::ToOwned; -use alloc::string::String; +use alloc::{borrow::ToOwned, string::String}; use core::sync::atomic::{AtomicUsize, Ordering}; -use idna::domain_to_unicode; pub fn url_origin(url: &Url) -> Origin { let scheme = url.scheme(); diff --git a/url/src/parser.rs b/url/src/parser.rs index ea1ce3192..76946526c 100644 --- a/url/src/parser.rs +++ b/url/src/parser.rs @@ -6,7 +6,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use alloc::borrow::ToOwned; use alloc::string::{String, ToString}; use core::fmt::{self, Formatter, Write}; use core::str; diff --git a/url/src/path_segments.rs b/url/src/path_segments.rs index 131f0b82d..d73899845 100644 --- a/url/src/path_segments.rs +++ b/url/src/path_segments.rs @@ -20,9 +20,22 @@ use core::str; /// /// ```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 for TestError { +/// # fn from(value: ParseError) -> Self { +/// # TestError {} +/// # } +/// # } +/// # impl From<&str> for TestError { +/// # fn from(value: &str) -> Self { +/// # TestError {} +/// # } +/// # } /// -/// # fn run() -> Result<(), Box> { +/// # fn run() -> Result<(), TestError> { /// let mut url = Url::parse("mailto:me@example.com")?; /// assert!(url.path_segments_mut().is_err()); /// @@ -79,9 +92,22 @@ impl<'a> PathSegmentsMut<'a> { /// /// ```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 for TestError { + /// # fn from(value: ParseError) -> Self { + /// # TestError {} + /// # } + /// # } + /// # impl From<&str> for TestError { + /// # fn from(value: &str) -> Self { + /// # TestError {} + /// # } + /// # } /// - /// # fn run() -> Result<(), Box> { + /// # fn run() -> Result<(), TestError> { /// let mut url = Url::parse("https://github.com/servo/rust-url/")?; /// url.path_segments_mut().map_err(|_| "cannot be base")? /// .clear().push("logout"); @@ -107,9 +133,22 @@ impl<'a> PathSegmentsMut<'a> { /// /// ```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 for TestError { + /// # fn from(value: ParseError) -> Self { + /// # TestError {} + /// # } + /// # } + /// # impl From<&str> for TestError { + /// # fn from(value: &str) -> Self { + /// # TestError {} + /// # } + /// # } /// - /// # fn run() -> Result<(), Box> { + /// # fn run() -> Result<(), TestError> { /// let mut url = Url::parse("https://github.com/servo/rust-url/")?; /// url.path_segments_mut().map_err(|_| "cannot be base")? /// .push("pulls"); @@ -182,9 +221,22 @@ impl<'a> PathSegmentsMut<'a> { /// /// ```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 for TestError { + /// # fn from(value: ParseError) -> Self { + /// # TestError {} + /// # } + /// # } + /// # impl From<&str> for TestError { + /// # fn from(value: &str) -> Self { + /// # TestError {} + /// # } + /// # } /// - /// # fn run() -> Result<(), Box> { + /// # fn run() -> Result<(), TestError> { /// let mut url = Url::parse("https://github.com/")?; /// let org = "servo"; /// let repo = "rust-url"; @@ -202,9 +254,22 @@ impl<'a> PathSegmentsMut<'a> { /// /// ```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 for TestError { + /// # fn from(value: ParseError) -> Self { + /// # TestError {} + /// # } + /// # } + /// # impl From<&str> for TestError { + /// # fn from(value: &str) -> Self { + /// # TestError {} + /// # } + /// # } /// - /// # fn run() -> Result<(), Box> { + /// # fn run() -> Result<(), TestError> { /// let mut url = Url::parse("https://github.com/servo")?; /// url.path_segments_mut().map_err(|_| "cannot be base")? /// .extend(&["..", "rust-url", ".", "pulls"]); diff --git a/url/tests/unit.rs b/url/tests/unit.rs index 8957aaf32..fc9a9f3b4 100644 --- a/url/tests/unit.rs +++ b/url/tests/unit.rs @@ -11,6 +11,7 @@ use std::borrow::Cow; use std::cell::{Cell, RefCell}; use std::net::{Ipv4Addr, Ipv6Addr}; +#[cfg(feature = "std")] use std::path::{Path, PathBuf}; use url::{form_urlencoded, Host, Origin, Url}; @@ -117,6 +118,10 @@ fn test_set_empty_query() { assert_eq!(base.as_str(), "moz://example.com/path"); } +#[cfg(all( + feature = "std", + any(unix, windows, target_os = "redox", target_os = "wasi") +))] macro_rules! assert_from_file_path { ($path: expr) => { assert_from_file_path!($path, $path) @@ -130,6 +135,7 @@ macro_rules! assert_from_file_path { } #[test] +#[cfg(feature = "std")] fn new_file_paths() { if cfg!(unix) { assert_eq!(Url::from_file_path(Path::new("relative")), Err(())); @@ -150,7 +156,7 @@ fn new_file_paths() { } #[test] -#[cfg(unix)] +#[cfg(all(feature = "std", unix))] fn new_path_bad_utf8() { use std::ffi::OsStr; use std::os::unix::prelude::*; @@ -161,6 +167,10 @@ fn new_path_bad_utf8() { } #[test] +#[cfg(all( + feature = "std", + any(unix, windows, target_os = "redox", target_os = "wasi") +))] fn new_path_windows_fun() { if cfg!(windows) { assert_from_file_path!(r"C:\foo\bar", "/C:/foo/bar"); @@ -183,6 +193,10 @@ fn new_path_windows_fun() { } #[test] +#[cfg(all( + feature = "std", + any(unix, windows, target_os = "redox", target_os = "wasi") +))] fn new_directory_paths() { if cfg!(unix) { assert_eq!(Url::from_directory_path(Path::new("relative")), Err(())); @@ -438,7 +452,7 @@ fn issue_61() { } #[test] -#[cfg(not(windows))] +#[cfg(all(feature = "std", any(unix, target_os = "redox", target_os = "wasi")))] /// https://github.com/servo/rust-url/issues/197 fn issue_197() { let mut url = Url::from_file_path("/").expect("Failed to parse path"); @@ -622,6 +636,7 @@ fn test_origin_unicode_serialization() { } #[test] +#[cfg(feature = "std")] fn test_socket_addrs() { use std::net::ToSocketAddrs; @@ -803,6 +818,7 @@ fn test_expose_internals() { } #[test] +#[cfg(feature = "std")] fn test_windows_unc_path() { if !cfg!(windows) { return; @@ -927,8 +943,8 @@ fn test_url_from_file_path() { } /// https://github.com/servo/rust-url/issues/505 -#[cfg(not(windows))] #[test] +#[cfg(all(feature = "std", not(windows)))] fn test_url_from_file_path() { use std::path::PathBuf; use url::Url; From a98aef8029c5474c9598907e07403005dc78cca7 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sat, 8 Apr 2023 18:08:01 +0200 Subject: [PATCH 07/10] fix no_std for nightly --- idna/src/uts46.rs | 2 +- url/Cargo.toml | 8 ++++---- url/src/host.rs | 3 +++ url/src/lib.rs | 6 ++++++ 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/idna/src/uts46.rs b/idna/src/uts46.rs index fb39c1512..2ffb8414b 100644 --- a/idna/src/uts46.rs +++ b/idna/src/uts46.rs @@ -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 { diff --git a/url/Cargo.toml b/url/Cargo.toml index 27a2d7b61..10a66eb3f 100644 --- a/url/Cargo.toml +++ b/url/Cargo.toml @@ -28,14 +28,14 @@ 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" } +form_urlencoded = { version = "1.1.0", path = "../form_urlencoded", default-features = false } +idna = { version = "0.3.0", path = "../idna", default-features = false } +percent-encoding = { version = "2.2.0", path = "../percent_encoding", default-features = false } serde = {version = "1.0", optional = true, features = ["derive"]} [features] default = ["std"] -std = ["idna/std", "alloc"] +std = ["idna/std", "percent-encoding/std", "form_urlencoded/std", "alloc"] alloc = [] # UNSTABLE FEATURES (requires Rust nightly) # Enable to use the #[debugger_visualizer] attribute. diff --git a/url/src/host.rs b/url/src/host.rs index 1f31a7340..8981ec4aa 100644 --- a/url/src/host.rs +++ b/url/src/host.rs @@ -11,10 +11,13 @@ use alloc::{ string::{String, ToString}, vec::Vec, }; +#[cfg(not(feature = "std"))] +use core::net::{Ipv4Addr, Ipv6Addr}; use core::{ cmp, fmt::{self, Formatter}, }; +#[cfg(feature = "std")] use std::net::{Ipv4Addr, Ipv6Addr}; use percent_encoding::{percent_decode, utf8_percent_encode, CONTROLS}; diff --git a/url/src/lib.rs b/url/src/lib.rs index 522f2893b..3dcb290d5 100644 --- a/url/src/lib.rs +++ b/url/src/lib.rs @@ -127,6 +127,8 @@ url = { version = "2", features = ["serde"] } feature(debugger_visualizer), debugger_visualizer(natvis_file = "../../debug_metadata/url.natvis") )] +#![no_std] +#![cfg_attr(not(feature = "std"), feature(ip_in_core))] pub use form_urlencoded; @@ -134,6 +136,7 @@ pub use form_urlencoded; #[cfg(feature = "std")] extern crate std; +#[macro_use] extern crate alloc; #[cfg(not(feature = "alloc"))] @@ -152,9 +155,12 @@ use core::convert::TryFrom; use core::fmt::{self, Write}; use core::hash; use core::mem; +#[cfg(not(feature = "std"))] +use core::net::IpAddr; use core::ops::{Range, RangeFrom, RangeTo}; use core::str; use percent_encoding::utf8_percent_encode; +#[cfg(feature = "std")] use std::net::IpAddr; #[cfg(feature = "std")] use std::{ From 0d14d5acab4e271e285947a0c754486f0660e271 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sat, 8 Apr 2023 18:14:43 +0200 Subject: [PATCH 08/10] Add no_std to CI --- .github/workflows/main.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c05ee1f89..35e369c36 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -33,6 +33,8 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} + # Add toolchain for no_std tests + - run: rustup target add aarch64-unknown-none - run: cargo build --all-targets # Run tests - name: Run tests @@ -49,6 +51,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 WASM: runs-on: ubuntu-latest From 315eb43036c65ab5d89d11618db8d3bd49a724ab Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 10 Apr 2023 15:53:06 +0200 Subject: [PATCH 09/10] Fix no_std for deps --- .github/workflows/main.yml | 4 ++-- url/Cargo.toml | 2 +- url/src/lib.rs | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 35e369c36..07f0fb726 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -50,9 +50,9 @@ jobs: matrix.rust == 'nightly' 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 + run: cargo +nightly 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 + run: cargo +nightly build -Zbuild-std=core,alloc --target aarch64-unknown-none -v --release --no-default-features --features=alloc WASM: runs-on: ubuntu-latest diff --git a/url/Cargo.toml b/url/Cargo.toml index 10a66eb3f..8292d1251 100644 --- a/url/Cargo.toml +++ b/url/Cargo.toml @@ -36,7 +36,7 @@ serde = {version = "1.0", optional = true, features = ["derive"]} [features] default = ["std"] std = ["idna/std", "percent-encoding/std", "form_urlencoded/std", "alloc"] -alloc = [] +alloc = ["idna/alloc", "percent-encoding/alloc", "form_urlencoded/alloc"] # UNSTABLE FEATURES (requires Rust nightly) # Enable to use the #[debugger_visualizer] attribute. debugger_visualizer = [] diff --git a/url/src/lib.rs b/url/src/lib.rs index 3dcb290d5..837cad48f 100644 --- a/url/src/lib.rs +++ b/url/src/lib.rs @@ -2966,6 +2966,7 @@ fn file_url_segments_to_pathbuf( host: Option<&str>, segments: str::Split<'_, char>, ) -> Result { + use alloc::vec::Vec; use percent_encoding::percent_decode; use std::ffi::OsStr; #[cfg(any(unix, target_os = "redox"))] From 6d622abbe36a5f11ce83cdd2f9f47c5169901c74 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 10 Apr 2023 16:31:32 +0200 Subject: [PATCH 10/10] Moved to no-std-net --- .github/workflows/main.yml | 3 ++- data-url/Cargo.toml | 2 +- idna/Cargo.toml | 2 +- idna/tests/punycode.rs | 3 +-- url/Cargo.toml | 15 +++++++++------ url/src/host.rs | 5 +---- url/src/lib.rs | 7 ++----- url/tests/unit.rs | 4 ++-- 8 files changed, 19 insertions(+), 22 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 07f0fb726..131b243ca 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,6 +34,7 @@ jobs: 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 @@ -50,7 +51,7 @@ jobs: matrix.rust == 'nightly' run: cargo test --test debugger_visualizer --features "url/serde,url/debugger_visualizer" -- --test-threads=1 - name: Test `no_std` support - run: cargo +nightly test --no-default-features --features=alloc + 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 diff --git a/data-url/Cargo.toml b/data-url/Cargo.toml index 514fb620c..07d5e9c6c 100644 --- a/data-url/Cargo.toml +++ b/data-url/Cargo.toml @@ -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] diff --git a/idna/Cargo.toml b/idna/Cargo.toml index 7fd9a53f1..a495de299 100644 --- a/idna/Cargo.toml +++ b/idna/Cargo.toml @@ -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"] } diff --git a/idna/tests/punycode.rs b/idna/tests/punycode.rs index 95674137a..4f3f42096 100644 --- a/idna/tests/punycode.rs +++ b/idna/tests/punycode.rs @@ -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) { diff --git a/url/Cargo.toml b/url/Cargo.toml index 8292d1251..c86b96c35 100644 --- a/url/Cargo.toml +++ b/url/Cargo.toml @@ -28,20 +28,23 @@ debugger_test = "0.1" debugger_test_parser = "0.1" [dependencies] -form_urlencoded = { version = "1.1.0", path = "../form_urlencoded", default-features = false } -idna = { version = "0.3.0", path = "../idna", default-features = false } -percent-encoding = { version = "2.2.0", path = "../percent_encoding", default-features = false } -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 = ["std"] -std = ["idna/std", "percent-encoding/std", "form_urlencoded/std", "alloc"] -alloc = ["idna/alloc", "percent-encoding/alloc", "form_urlencoded/alloc"] +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" diff --git a/url/src/host.rs b/url/src/host.rs index 8981ec4aa..98c588f1c 100644 --- a/url/src/host.rs +++ b/url/src/host.rs @@ -11,14 +11,11 @@ use alloc::{ string::{String, ToString}, vec::Vec, }; -#[cfg(not(feature = "std"))] -use core::net::{Ipv4Addr, Ipv6Addr}; use core::{ cmp, fmt::{self, Formatter}, }; -#[cfg(feature = "std")] -use std::net::{Ipv4Addr, Ipv6Addr}; +use no_std_net::{Ipv4Addr, Ipv6Addr}; use percent_encoding::{percent_decode, utf8_percent_encode, CONTROLS}; #[cfg(feature = "serde")] diff --git a/url/src/lib.rs b/url/src/lib.rs index 837cad48f..5ff902553 100644 --- a/url/src/lib.rs +++ b/url/src/lib.rs @@ -128,7 +128,6 @@ url = { version = "2", features = ["serde"] } debugger_visualizer(natvis_file = "../../debug_metadata/url.natvis") )] #![no_std] -#![cfg_attr(not(feature = "std"), feature(ip_in_core))] pub use form_urlencoded; @@ -155,13 +154,11 @@ use core::convert::TryFrom; use core::fmt::{self, Write}; use core::hash; use core::mem; -#[cfg(not(feature = "std"))] -use core::net::IpAddr; 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::net::IpAddr; + #[cfg(feature = "std")] use std::{ io, diff --git a/url/tests/unit.rs b/url/tests/unit.rs index fc9a9f3b4..182ba6604 100644 --- a/url/tests/unit.rs +++ b/url/tests/unit.rs @@ -8,9 +8,9 @@ //! Unit tests +use core::cell::{Cell, RefCell}; +use no_std_net::{Ipv4Addr, Ipv6Addr}; use std::borrow::Cow; -use std::cell::{Cell, RefCell}; -use std::net::{Ipv4Addr, Ipv6Addr}; #[cfg(feature = "std")] use std::path::{Path, PathBuf}; use url::{form_urlencoded, Host, Origin, Url};