diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 46f818a6..ba1623e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: profile: minimal toolchain: nightly override: true - - name: Test with no feature + - name: Test with default features uses: actions-rs/cargo@v1 with: command: test @@ -28,7 +28,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: doc - args: --no-deps --features ipv_future,rfc6874bis + args: --no-deps --features ipv_future,rfc6874bis,std - name: Deploy docs uses: peaceiris/actions-gh-pages@v3 with: diff --git a/Cargo.toml b/Cargo.toml index 5c49d6a4..4ed08a35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,12 +15,14 @@ categories = ["encoding", "parser-implementations"] bitflags = "1.3.2" [features] +default = ["std"] ipv_future = [] rfc6874bis = [] unstable = [] +std = [] [package.metadata.docs.rs] -features = ["ipv_future", "rfc6874bis"] +features = ["ipv_future", "rfc6874bis", "std"] # Commented out to reduce compile time. diff --git a/src/enc/estring.rs b/src/enc/estring.rs index 7779119e..7fb6a3f6 100644 --- a/src/enc/estring.rs +++ b/src/enc/estring.rs @@ -1,4 +1,8 @@ -use std::{borrow::Borrow, fmt, hash, marker::PhantomData, ops::Deref}; +use alloc::string::String; +use core::{borrow::Borrow, fmt, hash, marker::PhantomData, ops::Deref}; + +#[cfg(feature = "unstable")] +use alloc::vec::Vec; use super::{ encoder::Encoder, @@ -341,7 +345,7 @@ impl Clone for EString { impl fmt::Debug for EString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("EString") - .field("encoder", &std::any::type_name::()) + .field("encoder", &core::any::type_name::()) .field("contents", &self.string) .finish() } diff --git a/src/enc/imp.rs b/src/enc/imp.rs index eb6421b4..3196f424 100644 --- a/src/enc/imp.rs +++ b/src/enc/imp.rs @@ -3,10 +3,11 @@ use super::table; #[cfg(feature = "unstable")] use super::table::Table; -use std::{fmt, ptr}; +use alloc::vec::Vec; +use core::{fmt, ptr}; #[cfg(feature = "unstable")] -use std::{borrow::Cow, str}; +use alloc::{borrow::Cow, str, string::String}; /// Returns immediately with an encoding error. macro_rules! err { @@ -57,6 +58,7 @@ impl EncodingError { } } +#[cfg(feature = "std")] impl std::error::Error for EncodingError {} impl fmt::Display for EncodingError { @@ -70,7 +72,7 @@ impl fmt::Display for EncodingError { } } -pub(crate) type Result = std::result::Result; +pub(crate) type Result = core::result::Result; #[cfg(feature = "unstable")] const fn gen_hex_table() -> [u8; 512] { diff --git a/src/enc/mod.rs b/src/enc/mod.rs index 3ef19dc7..e4261a44 100644 --- a/src/enc/mod.rs +++ b/src/enc/mod.rs @@ -89,13 +89,16 @@ pub fn validate + ?Sized>(s: &S, table: &Table) -> Result<()> { } } -use std::{ +use alloc::{ borrow::{self, Cow}, + string::{FromUtf8Error, String}, + vec::Vec, +}; +use core::{ fmt, hash, iter::FusedIterator, mem, str::{self, Utf8Error}, - string::FromUtf8Error, }; use crate::view::View; @@ -275,7 +278,7 @@ impl EStr { /// assert_eq!(dec.to_str()?, "233"); /// assert!(dec.decoded_any()); /// assert_eq!(buf, b"233"); - /// # Ok::<_, std::str::Utf8Error>(()) + /// # Ok::<_, core::str::Utf8Error>(()) /// ``` #[cfg(feature = "unstable")] #[inline] @@ -778,7 +781,7 @@ impl FusedIterator for SplitView<'_> {} #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct BufferTooSmallError(()); -#[cfg(feature = "unstable")] +#[cfg(all(feature = "unstable", feature = "std"))] impl std::error::Error for BufferTooSmallError {} #[cfg(feature = "unstable")] @@ -791,7 +794,8 @@ impl fmt::Display for BufferTooSmallError { #[cfg(feature = "unstable")] pub(crate) mod internal { use crate::enc::BufferTooSmallError; - use std::{collections::TryReserveError, mem::MaybeUninit}; + use alloc::{collections::TryReserveError, string::String, vec::Vec}; + use core::mem::MaybeUninit; pub trait AsMutVec { unsafe fn as_mut_vec(&mut self) -> &mut Vec; diff --git a/src/fmt.rs b/src/fmt.rs index c85f8f50..ec7094ff 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -1,5 +1,6 @@ use super::*; -use std::fmt; +use alloc::string::String; +use core::fmt; impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/src/internal.rs b/src/internal.rs index 03eb7c8b..c4e4028d 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -1,12 +1,16 @@ #![allow(missing_debug_implementations)] -use std::{ +use alloc::{string::String, vec::Vec}; +use core::{ cell::Cell, mem::MaybeUninit, num::NonZeroU32, ops::{Deref, DerefMut}, }; +#[cfg(feature = "std")] +use std::net::{Ipv4Addr, Ipv6Addr}; + use super::*; use bitflags::bitflags; @@ -231,15 +235,17 @@ pub struct AuthData { #[derive(Clone, Copy)] pub union RawHostData { + #[cfg(feature = "std")] pub ipv4_addr: Ipv4Addr, pub ipv6: Ipv6Data, #[cfg(feature = "ipv_future")] pub ipv_future_dot_i: u32, - pub reg_name: (), + pub none: (), } #[derive(Clone, Copy)] pub struct Ipv6Data { + #[cfg(feature = "std")] pub addr: Ipv6Addr, #[cfg(feature = "rfc6874bis")] pub zone_id_start: Option, diff --git a/src/lib.rs b/src/lib.rs index b32d5171..0c7ac812 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![warn(missing_debug_implementations, missing_docs, rust_2018_idioms)] #![deny(unsafe_op_in_unsafe_fn)] +#![cfg_attr(not(feature = "std"), no_std)] //! A generic URI parser that strictly adheres to IETF [RFC 3986]. //! @@ -9,11 +10,14 @@ //! //! # Feature flags //! -//! All features are disabled by default. However, note that these features each -//! alter the enum [`HostData`] in a backward incompatible way that could make it +//! All features except `std` are disabled by default. Note that the last two features +//! each alter the enum [`HostData`] in a backward incompatible way that could make it //! impossible for two crates that depend on different features of `fluent-uri` to //! be used together. //! +//! - `std`: Enables `std` support. This includes [`Error`] implementations +//! and `Ip{v4, v6}Addr` support in [`HostData`]. +//! //! - `ipv_future`: Enables the parsing of [IPvFuture] literal addresses, //! which fails with [`InvalidIpLiteral`] when disabled. //! @@ -26,9 +30,12 @@ //! //! This feature is based on the homonymous [draft] and is thus subject to change. //! +//! [`Error`]: std::error::Error //! [IPvFuture]: https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.2 //! [`InvalidIpLiteral`]: ParseErrorKind::InvalidIpLiteral -//! [draft]: https://datatracker.ietf.org/doc/html/draft-ietf-6man-rfc6874bis-02 +//! [draft]: https://datatracker.ietf.org/doc/html/draft-ietf-6man-rfc6874bis-05 + +extern crate alloc; /// Utilities for percent-encoding. pub mod enc; @@ -41,13 +48,11 @@ pub use view::*; mod parser; use crate::enc::{EStr, Split}; -use std::{ - marker::PhantomData, - mem::ManuallyDrop, - net::{Ipv4Addr, Ipv6Addr}, - ptr::NonNull, - slice, str, -}; +use alloc::{string::String, vec::Vec}; +use core::{iter::Iterator, marker::PhantomData, mem::ManuallyDrop, ptr::NonNull, slice, str}; + +#[cfg(feature = "std")] +use std::net::{Ipv4Addr, Ipv6Addr}; mod internal; use internal::*; @@ -90,9 +95,10 @@ impl ParseError { } } +#[cfg(feature = "std")] impl std::error::Error for ParseError {} -type Result = std::result::Result; +type Result = core::result::Result; #[cold] fn len_overflow() -> ! { @@ -135,7 +141,7 @@ fn len_overflow() -> ! { /// # Examples /// /// Create and convert between `Uri<&str>` and `Uri`: -/// +/// /// ``` /// use fluent_uri::Uri; /// @@ -882,7 +888,7 @@ impl<'i, 'o, T: Io<'i, 'o>> Host { /// Returns the structured host data. #[inline] pub fn data(&'i self) -> HostData<'o> { - let data = self.raw_data(); + let _data = self.raw_data(); let tag = self.auth.uri.tag; // SAFETY: We only access the union after checking the tag. unsafe { @@ -890,11 +896,14 @@ impl<'i, 'o, T: Io<'i, 'o>> Host { // SAFETY: The validation is done. return HostData::RegName(EStr::new_unchecked(self.as_str().as_bytes())); } else if tag.contains(Tag::HOST_IPV4) { - return HostData::Ipv4(data.ipv4_addr); + return HostData::Ipv4( + #[cfg(feature = "std")] + _data.ipv4_addr, + ); } #[cfg(feature = "ipv_future")] if !tag.contains(Tag::HOST_IPV6) { - let dot_i = data.ipv_future_dot_i; + let dot_i = _data.ipv_future_dot_i; let bounds = self.bounds(); // SAFETY: The indexes are within bounds and the validation is done. return HostData::IpvFuture { @@ -903,10 +912,11 @@ impl<'i, 'o, T: Io<'i, 'o>> Host { }; } HostData::Ipv6 { - addr: data.ipv6.addr, + #[cfg(feature = "std")] + addr: _data.ipv6.addr, // SAFETY: The indexes are within bounds and the validation is done. #[cfg(feature = "rfc6874bis")] - zone_id: data + zone_id: _data .ipv6 .zone_id_start .map(|start| self.auth.uri.slice(start.get(), self.bounds().1 - 1)), @@ -919,10 +929,13 @@ impl<'i, 'o, T: Io<'i, 'o>> Host { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum HostData<'a> { /// An IPv4 address. - Ipv4(Ipv4Addr), + #[cfg_attr(not(feature = "std"), non_exhaustive)] + Ipv4(#[cfg(feature = "std")] Ipv4Addr), /// An IPv6 address. + #[cfg_attr(not(feature = "std"), non_exhaustive)] Ipv6 { /// The address. + #[cfg(feature = "std")] addr: Ipv6Addr, /// An optional zone identifier. /// diff --git a/src/parser.rs b/src/parser.rs index d903f395..14729183 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3,13 +3,7 @@ use crate::{ internal::Pointer, AuthData, Data, RawHostData as HostData, Result, Tag, Uri, }; -use std::{ - cell::Cell, - marker::PhantomData, - net::{Ipv4Addr, Ipv6Addr}, - num::NonZeroU32, - str, -}; +use core::{cell::Cell, marker::PhantomData, num::NonZeroU32, str}; use super::{internal::Storage, Ipv6Data}; @@ -244,7 +238,7 @@ impl Parser { } else { // Empty authority. self.out.tag = Tag::HOST_REG_NAME; - host = (self.pos, self.pos, HostData { reg_name: () }); + host = (self.pos, self.pos, HostData { none: () }); } } else { // The whole authority scanned. Try to parse the host and port. @@ -293,10 +287,16 @@ impl Parser { let v4 = self.scan_v4(); let (tag, data) = match v4 { - Some(addr) if !self.has_remaining() => { - (Tag::HOST_IPV4, HostData { ipv4_addr: addr }) - } - _ => (Tag::HOST_REG_NAME, HostData { reg_name: () }), + Some(_addr) if !self.has_remaining() => ( + Tag::HOST_IPV4, + HostData { + #[cfg(feature = "std")] + ipv4_addr: _addr.into(), + #[cfg(not(feature = "std"))] + none: (), + }, + ), + _ => (Tag::HOST_REG_NAME, HostData { none: () }), }; self.out.tag = tag; @@ -330,11 +330,12 @@ impl Parser { return Ok(None); } - let host = if let Some(addr) = self.scan_v6() { + let host = if let Some(_addr) = self.scan_v6() { self.out.tag = Tag::HOST_IPV6; HostData { ipv6: Ipv6Data { - addr, + #[cfg(feature = "std")] + addr: _addr.into(), #[cfg(feature = "rfc6874bis")] zone_id_start: self.read_zone_id()?, }, @@ -356,7 +357,7 @@ impl Parser { Ok(Some(host)) } - fn scan_v6(&mut self) -> Option { + fn scan_v6(&mut self) -> Option<[u16; 8]> { let mut segs = [0; 8]; let mut ellipsis_i = 8; @@ -383,7 +384,7 @@ impl Parser { // Not enough space, triple colons, or no colon. return None; } - let octets = self.scan_v4()?.octets(); + let octets = self.scan_v4()?.to_be_bytes(); segs[i] = u16::from_be_bytes([octets[0], octets[1]]); segs[i + 1] = u16::from_be_bytes([octets[2], octets[3]]); i += 2; @@ -411,7 +412,7 @@ impl Parser { } } - Some(segs.into()) + Some(segs) } fn scan_v6_segment(&mut self) -> Option { @@ -481,22 +482,30 @@ impl Parser { self.scan(REG_NAME)?; let (tag, data) = match v4 { - Some(addr) if self.pos == v4_end => (Tag::HOST_IPV4, HostData { ipv4_addr: addr }), - _ => (Tag::HOST_REG_NAME, HostData { reg_name: () }), + Some(_addr) if self.pos == v4_end => ( + Tag::HOST_IPV4, + HostData { + #[cfg(feature = "std")] + ipv4_addr: _addr.into(), + #[cfg(not(feature = "std"))] + none: (), + }, + ), + _ => (Tag::HOST_REG_NAME, HostData { none: () }), }; self.out.tag = tag; Ok(data) } - fn scan_v4(&mut self) -> Option { - let mut res = self.scan_v4_octet()? << 24; + fn scan_v4(&mut self) -> Option { + let mut addr = self.scan_v4_octet()? << 24; for i in (0..3).rev() { if !self.read_str(".") { return None; } - res |= self.scan_v4_octet()? << (i * 8); + addr |= self.scan_v4_octet()? << (i * 8); } - Some(Ipv4Addr::from(res)) + Some(addr) } fn scan_v4_octet(&mut self) -> Option { diff --git a/src/view.rs b/src/view.rs index f79bb1e2..65609604 100644 --- a/src/view.rs +++ b/src/view.rs @@ -1,4 +1,4 @@ -use std::{num::NonZeroU32, ops::Deref}; +use core::{num::NonZeroU32, ops::Deref}; use super::*; use crate::enc::SplitView;