diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c05ee1f89..131b243ca 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -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 @@ -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 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/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/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 2ffab7754..c86b96c35 100644 --- a/url/Cargo.toml +++ b/url/Cargo.toml @@ -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" diff --git a/url/src/host.rs b/url/src/host.rs index 9931c2f87..98c588f1c 100644 --- a/url/src/host.rs +++ b/url/src/host.rs @@ -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")] diff --git a/url/src/lib.rs b/url/src/lib.rs index 023a89a28..5ff902553 100644 --- a/url/src/lib.rs +++ b/url/src/lib.rs @@ -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}; @@ -1237,10 +1254,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`: // @@ -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(opt: Option, message: &str) -> io::Result { opt.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, message)) } @@ -1310,9 +1329,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")); @@ -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 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")?; @@ -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 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")?; @@ -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>(path: P) -> Result { let mut serialization = "file://".to_owned(); @@ -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>(path: P) -> Result { let mut url = Url::from_file_path(path)?; @@ -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 { if let Some(segments) = self.path_segments() { @@ -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")] @@ -2807,7 +2883,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, @@ -2815,12 +2891,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(()); @@ -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 { + 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; @@ -2924,7 +3005,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, @@ -2932,12 +3013,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 81193f510..8dd716a8e 100644 --- a/url/src/origin.rs +++ b/url/src/origin.rs @@ -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(); diff --git a/url/src/parser.rs b/url/src/parser.rs index c32090e20..76946526c 100644 --- a/url/src/parser.rs +++ b/url/src/parser.rs @@ -6,9 +6,9 @@ // 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::string::{String, ToString}; +use core::fmt::{self, Formatter, Write}; +use core::str; use crate::host::{Host, HostInternal}; use crate::Url; @@ -72,7 +72,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 +1108,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; @@ -1576,7 +1577,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..d73899845 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. /// @@ -19,9 +20,22 @@ use std::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()); /// @@ -78,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"); @@ -106,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"); @@ -181,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"; @@ -201,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/src/quirks.rs b/url/src/quirks.rs index 0674ebb62..f352ee344 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 c061fee84..b2b693979 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; diff --git a/url/tests/unit.rs b/url/tests/unit.rs index 8957aaf32..182ba6604 100644 --- a/url/tests/unit.rs +++ b/url/tests/unit.rs @@ -8,9 +8,10 @@ //! 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}; @@ -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;