Skip to content

Commit 947f64c

Browse files
authored
Merge pull request #1 from OverOrion/no_std_net
Add support for `no_std`
2 parents 6385c81 + 6d622ab commit 947f64c

File tree

14 files changed

+251
-65
lines changed

14 files changed

+251
-65
lines changed

.github/workflows/main.yml

+5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ jobs:
3333
- uses: dtolnay/rust-toolchain@master
3434
with:
3535
toolchain: ${{ matrix.rust }}
36+
# Add toolchain for no_std tests
37+
- run: rustup toolchain install nightly
38+
- run: rustup target add aarch64-unknown-none
3639
- run: cargo build --all-targets
3740
# Run tests
3841
- name: Run tests
@@ -49,6 +52,8 @@ jobs:
4952
run: cargo test --test debugger_visualizer --features "url/serde,url/debugger_visualizer" -- --test-threads=1
5053
- name: Test `no_std` support
5154
run: cargo test --no-default-features --features=alloc
55+
- name: Build `aarch64-unknown-none` with `no_std`
56+
run: cargo +nightly build -Zbuild-std=core,alloc --target aarch64-unknown-none -v --release --no-default-features --features=alloc
5257

5358
WASM:
5459
runs-on: ubuntu-latest

data-url/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ alloc = []
1717

1818
[dev-dependencies]
1919
tester = "0.9"
20-
serde = {version = "1.0", features = ["derive"]}
20+
serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] }
2121
serde_json = "1.0"
2222

2323
[lib]

idna/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ name = "unit"
2929
assert_matches = "1.3"
3030
bencher = "0.1"
3131
tester = "0.9"
32-
serde_json = "1.0"
32+
serde_json = { version = "1.0" }
3333

3434
[dependencies]
3535
unicode-bidi = { version = "0.3.10", default-features = false, features = ["hardcoded-data"] }

idna/src/uts46.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ impl Idna {
453453
return Errors::default();
454454
}
455455
let mut errors = processing(domain, self.config, &mut self.normalized, out);
456-
self.output = std::mem::replace(out, String::with_capacity(out.len()));
456+
self.output = core::mem::replace(out, String::with_capacity(out.len()));
457457
let mut first = true;
458458
for label in self.output.split('.') {
459459
if !first {

idna/tests/punycode.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88

99
use crate::test::TestFn;
1010
use idna::punycode::{decode, encode_str};
11-
use serde_json::map::Map;
12-
use serde_json::Value;
11+
use serde_json::{map::Map, Value};
1312
use std::str::FromStr;
1413

1514
fn one_test(decoded: &str, encoded: &str) {

url/Cargo.toml

+10-5
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,23 @@ debugger_test = "0.1"
2828
debugger_test_parser = "0.1"
2929

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

3638
[features]
37-
default = []
39+
default = ["std"]
40+
std = ["idna/std", "percent-encoding/std", "form_urlencoded/std", "no-std-net/std", "alloc"]
41+
alloc = []
3842
# UNSTABLE FEATURES (requires Rust nightly)
3943
# Enable to use the #[debugger_visualizer] attribute.
4044
debugger_visualizer = []
4145
# Expose internal offsets of the URL.
4246
expose_internals = []
47+
serde = ["dep:serde", "no-std-net/serde"]
4348

4449
[[bench]]
4550
name = "parse_url"

url/src/host.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,16 @@
66
// option. This file may not be copied, modified, or distributed
77
// except according to those terms.
88

9-
use std::cmp;
10-
use std::fmt::{self, Formatter};
11-
use std::net::{Ipv4Addr, Ipv6Addr};
9+
use alloc::{
10+
borrow::ToOwned,
11+
string::{String, ToString},
12+
vec::Vec,
13+
};
14+
use core::{
15+
cmp,
16+
fmt::{self, Formatter},
17+
};
18+
use no_std_net::{Ipv4Addr, Ipv6Addr};
1219

1320
use percent_encoding::{percent_decode, utf8_percent_encode, CONTROLS};
1421
#[cfg(feature = "serde")]

url/src/lib.rs

+111-28
Original file line numberDiff line numberDiff line change
@@ -127,27 +127,44 @@ url = { version = "2", features = ["serde"] }
127127
feature(debugger_visualizer),
128128
debugger_visualizer(natvis_file = "../../debug_metadata/url.natvis")
129129
)]
130+
#![no_std]
130131

131132
pub use form_urlencoded;
132133

134+
// For forwards compatibility
135+
#[cfg(feature = "std")]
136+
extern crate std;
137+
138+
#[macro_use]
139+
extern crate alloc;
140+
141+
#[cfg(not(feature = "alloc"))]
142+
compile_error!("the `alloc` feature must be enabled");
143+
133144
#[cfg(feature = "serde")]
134145
extern crate serde;
135146

136147
use crate::host::HostInternal;
137-
use crate::parser::{to_u32, Context, Parser, SchemeType, PATH_SEGMENT, USERINFO};
138-
use percent_encoding::{percent_decode, percent_encode, utf8_percent_encode};
139-
use std::borrow::Borrow;
140-
use std::cmp;
141-
use std::fmt::{self, Write};
142-
use std::hash;
143-
use std::io;
144-
use std::mem;
145-
use std::net::{IpAddr, SocketAddr, ToSocketAddrs};
146-
use std::ops::{Range, RangeFrom, RangeTo};
147-
use std::path::{Path, PathBuf};
148-
use std::str;
149-
150-
use std::convert::TryFrom;
148+
use crate::parser::{to_u32, Context, Parser, SchemeType, USERINFO};
149+
use alloc::borrow::ToOwned;
150+
use alloc::string::{String, ToString};
151+
use core::borrow::Borrow;
152+
use core::cmp;
153+
use core::convert::TryFrom;
154+
use core::fmt::{self, Write};
155+
use core::hash;
156+
use core::mem;
157+
use core::ops::{Range, RangeFrom, RangeTo};
158+
use core::str;
159+
use no_std_net::IpAddr;
160+
use percent_encoding::utf8_percent_encode;
161+
162+
#[cfg(feature = "std")]
163+
use std::{
164+
io,
165+
net::{SocketAddr, ToSocketAddrs},
166+
path::{Path, PathBuf},
167+
};
151168

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

1270+
// TODO: Return custom error type to support no_std
12521271
fn io_result<T>(opt: Option<T>, message: &str) -> io::Result<T> {
12531272
opt.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, message))
12541273
}
@@ -1310,9 +1329,23 @@ impl Url {
13101329
///
13111330
/// ```
13121331
/// use url::Url;
1313-
/// # use std::error::Error;
13141332
///
1315-
/// # fn run() -> Result<(), Box<dyn Error>> {
1333+
/// # use url::ParseError;
1334+
/// # #[derive(Debug)]
1335+
/// # /// A simple wrapper error struct for `no_std` support
1336+
/// # struct TestError;
1337+
/// # impl From<ParseError> for TestError {
1338+
/// # fn from(value: ParseError) -> Self {
1339+
/// # TestError {}
1340+
/// # }
1341+
/// # }
1342+
/// # impl From<&str> for TestError {
1343+
/// # fn from(value: &str) -> Self {
1344+
/// # TestError {}
1345+
/// # }
1346+
/// # }
1347+
///
1348+
/// # fn run() -> Result<(), TestError> {
13161349
/// let url = Url::parse("https://example.com/foo/bar")?;
13171350
/// let mut path_segments = url.path_segments().ok_or_else(|| "cannot be base")?;
13181351
/// assert_eq!(path_segments.next(), Some("foo"));
@@ -1717,9 +1750,22 @@ impl Url {
17171750
///
17181751
/// ```
17191752
/// use url::Url;
1720-
/// # use std::error::Error;
1753+
/// # use url::ParseError;
1754+
/// # #[derive(Debug)]
1755+
/// # /// A simple wrapper error struct for `no_std` support
1756+
/// # struct TestError;
1757+
/// # impl From<ParseError> for TestError {
1758+
/// # fn from(value: ParseError) -> Self {
1759+
/// # TestError {}
1760+
/// # }
1761+
/// # }
1762+
/// # impl From<&str> for TestError {
1763+
/// # fn from(value: &str) -> Self {
1764+
/// # TestError {}
1765+
/// # }
1766+
/// # }
17211767
///
1722-
/// # fn run() -> Result<(), Box<dyn Error>> {
1768+
/// # fn run() -> Result<(), TestError> {
17231769
/// let mut url = Url::parse("ssh://example.net:2048/")?;
17241770
///
17251771
/// url.set_port(Some(4096)).map_err(|_| "cannot be base")?;
@@ -1736,9 +1782,22 @@ impl Url {
17361782
///
17371783
/// ```rust
17381784
/// use url::Url;
1739-
/// # use std::error::Error;
1785+
/// # use url::ParseError;
1786+
/// # #[derive(Debug)]
1787+
/// # /// A simple wrapper error struct for `no_std` support
1788+
/// # struct TestError;
1789+
/// # impl From<ParseError> for TestError {
1790+
/// # fn from(value: ParseError) -> Self {
1791+
/// # TestError {}
1792+
/// # }
1793+
/// # }
1794+
/// # impl From<&str> for TestError {
1795+
/// # fn from(value: &str) -> Self {
1796+
/// # TestError {}
1797+
/// # }
1798+
/// # }
17401799
///
1741-
/// # fn run() -> Result<(), Box<dyn Error>> {
1800+
/// # fn run() -> Result<(), TestError> {
17421801
/// let mut url = Url::parse("https://example.org/")?;
17431802
///
17441803
/// url.set_port(Some(443)).map_err(|_| "cannot be base")?;
@@ -2419,7 +2478,12 @@ impl Url {
24192478
/// # run().unwrap();
24202479
/// # }
24212480
/// ```
2422-
#[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))]
2481+
///
2482+
/// This method is only available if the `std` Cargo feature is enabled.
2483+
#[cfg(all(
2484+
feature = "std",
2485+
any(unix, windows, target_os = "redox", target_os = "wasi")
2486+
))]
24232487
#[allow(clippy::result_unit_err)]
24242488
pub fn from_file_path<P: AsRef<Path>>(path: P) -> Result<Url, ()> {
24252489
let mut serialization = "file://".to_owned();
@@ -2456,7 +2520,12 @@ impl Url {
24562520
///
24572521
/// Note that `std::path` does not consider trailing slashes significant
24582522
/// and usually does not include them (e.g. in `Path::parent()`).
2459-
#[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))]
2523+
///
2524+
/// This method is only available if the `std` Cargo feature is enabled.
2525+
#[cfg(all(
2526+
feature = "std",
2527+
any(unix, windows, target_os = "redox", target_os = "wasi")
2528+
))]
24602529
#[allow(clippy::result_unit_err)]
24612530
pub fn from_directory_path<P: AsRef<Path>>(path: P) -> Result<Url, ()> {
24622531
let mut url = Url::from_file_path(path)?;
@@ -2572,8 +2641,13 @@ impl Url {
25722641
/// or if `Path::new_opt()` returns `None`.
25732642
/// (That is, if the percent-decoded path contains a NUL byte or,
25742643
/// for a Windows path, is not UTF-8.)
2644+
///
2645+
/// This method is only available if the `std` Cargo feature is enabled.
25752646
#[inline]
2576-
#[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))]
2647+
#[cfg(all(
2648+
feature = "std",
2649+
any(unix, windows, target_os = "redox", target_os = "wasi")
2650+
))]
25772651
#[allow(clippy::result_unit_err)]
25782652
pub fn to_file_path(&self) -> Result<PathBuf, ()> {
25792653
if let Some(segments) = self.path_segments() {
@@ -2777,11 +2851,13 @@ impl<'de> serde::Deserialize<'de> for Url {
27772851
}
27782852
}
27792853

2780-
#[cfg(any(unix, target_os = "redox", target_os = "wasi"))]
2854+
#[cfg(all(feature = "std", any(unix, target_os = "redox", target_os = "wasi")))]
27812855
fn path_to_file_url_segments(
27822856
path: &Path,
27832857
serialization: &mut String,
27842858
) -> Result<(u32, HostInternal), ()> {
2859+
use crate::parser::PATH_SEGMENT;
2860+
use percent_encoding::percent_encode;
27852861
#[cfg(any(unix, target_os = "redox"))]
27862862
use std::os::unix::prelude::OsStrExt;
27872863
#[cfg(target_os = "wasi")]
@@ -2807,20 +2883,23 @@ fn path_to_file_url_segments(
28072883
Ok((host_end, HostInternal::None))
28082884
}
28092885

2810-
#[cfg(windows)]
2886+
#[cfg(all(feature = "std", windows))]
28112887
fn path_to_file_url_segments(
28122888
path: &Path,
28132889
serialization: &mut String,
28142890
) -> Result<(u32, HostInternal), ()> {
28152891
path_to_file_url_segments_windows(path, serialization)
28162892
}
28172893

2894+
#[cfg(feature = "std")]
28182895
// Build this unconditionally to alleviate https://github.com/servo/rust-url/issues/102
28192896
#[cfg_attr(not(windows), allow(dead_code))]
28202897
fn path_to_file_url_segments_windows(
28212898
path: &Path,
28222899
serialization: &mut String,
28232900
) -> Result<(u32, HostInternal), ()> {
2901+
use crate::parser::PATH_SEGMENT;
2902+
use percent_encoding::percent_encode;
28242903
use std::path::{Component, Prefix};
28252904
if !path.is_absolute() {
28262905
return Err(());
@@ -2879,11 +2958,13 @@ fn path_to_file_url_segments_windows(
28792958
Ok((host_end, host_internal))
28802959
}
28812960

2882-
#[cfg(any(unix, target_os = "redox", target_os = "wasi"))]
2961+
#[cfg(all(feature = "std", any(unix, target_os = "redox", target_os = "wasi")))]
28832962
fn file_url_segments_to_pathbuf(
28842963
host: Option<&str>,
28852964
segments: str::Split<'_, char>,
28862965
) -> Result<PathBuf, ()> {
2966+
use alloc::vec::Vec;
2967+
use percent_encoding::percent_decode;
28872968
use std::ffi::OsStr;
28882969
#[cfg(any(unix, target_os = "redox"))]
28892970
use std::os::unix::prelude::OsStrExt;
@@ -2924,20 +3005,22 @@ fn file_url_segments_to_pathbuf(
29243005
Ok(path)
29253006
}
29263007

2927-
#[cfg(windows)]
3008+
#[cfg(all(feature = "std", windows))]
29283009
fn file_url_segments_to_pathbuf(
29293010
host: Option<&str>,
29303011
segments: str::Split<char>,
29313012
) -> Result<PathBuf, ()> {
29323013
file_url_segments_to_pathbuf_windows(host, segments)
29333014
}
29343015

3016+
#[cfg(feature = "std")]
29353017
// Build this unconditionally to alleviate https://github.com/servo/rust-url/issues/102
29363018
#[cfg_attr(not(windows), allow(dead_code))]
29373019
fn file_url_segments_to_pathbuf_windows(
29383020
host: Option<&str>,
29393021
mut segments: str::Split<'_, char>,
29403022
) -> Result<PathBuf, ()> {
3023+
use percent_encoding::percent_decode;
29413024
let mut string = if let Some(host) = host {
29423025
r"\\".to_owned() + host
29433026
} else {

url/src/origin.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
use crate::host::Host;
1010
use crate::parser::default_port;
1111
use crate::Url;
12-
use std::sync::atomic::{AtomicUsize, Ordering};
12+
use alloc::{borrow::ToOwned, string::String};
13+
use core::sync::atomic::{AtomicUsize, Ordering};
1314

1415
pub fn url_origin(url: &Url) -> Origin {
1516
let scheme = url.scheme();

0 commit comments

Comments
 (0)