diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0184f90..e8ee6c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,8 @@ env: # List of packages that will be checked with the minimum supported Rust version. # This should be limited to packages that are intended for publishing. RUST_MIN_VER_PKGS: "-p svgtypes" + # List of features that depend on the standard library and will be excluded from no_std checks. + FEATURES_DEPENDING_ON_STD: "std,default" # Rationale @@ -39,6 +41,10 @@ env: # The MSRV jobs run only cargo check because different clippy versions can disagree on goals and # running tests introduces dev dependencies which may require a higher MSRV than the bare package. # +# For no_std checks we target x86_64-unknown-none, because this target doesn't support std +# and as such will error out if our dependency tree accidentally tries to use std. +# https://doc.rust-lang.org/stable/rustc/platform-support/x86_64-unknown-none.html +# # We don't save caches in the merge-group cases, because those caches will never be re-used (apart # from the very rare cases where there are multiple PRs in the merge queue). # This is because GitHub doesn't share caches between merge queues and the main branch. @@ -113,11 +119,14 @@ jobs: with: save-if: ${{ github.event_name != 'merge_group' }} + - name: cargo clippy (no_std) + run: cargo hack clippy --workspace --locked --optional-deps --each-feature --ignore-unknown-features --features libm --exclude-features ${{ env.FEATURES_DEPENDING_ON_STD }} --target x86_64-unknown-none -- -D warnings + - name: cargo clippy - run: cargo hack clippy --workspace --locked --optional-deps --each-feature -- -D warnings + run: cargo hack clippy --workspace --locked --optional-deps --each-feature --ignore-unknown-features --features std -- -D warnings - name: cargo clippy (auxiliary) - run: cargo hack clippy --workspace --locked --optional-deps --each-feature --tests --examples -- -D warnings + run: cargo hack clippy --workspace --locked --optional-deps --each-feature --ignore-unknown-features --features std --tests --examples -- -D warnings clippy-stable-wasm: name: cargo clippy (wasm32) @@ -143,10 +152,10 @@ jobs: save-if: ${{ github.event_name != 'merge_group' }} - name: cargo clippy - run: cargo hack clippy --workspace --locked --target wasm32-unknown-unknown --optional-deps --each-feature -- -D warnings + run: cargo hack clippy --workspace --locked --target wasm32-unknown-unknown --optional-deps --each-feature --ignore-unknown-features --features std -- -D warnings - name: cargo clippy (auxiliary) - run: cargo hack clippy --workspace --locked --target wasm32-unknown-unknown --optional-deps --each-feature --tests --examples -- -D warnings + run: cargo hack clippy --workspace --locked --target wasm32-unknown-unknown --optional-deps --each-feature --ignore-unknown-features --features std --tests --examples -- -D warnings test-stable: name: cargo test @@ -224,8 +233,11 @@ jobs: with: save-if: ${{ github.event_name != 'merge_group' }} + - name: cargo check (no_std) + run: cargo hack check ${{ env.RUST_MIN_VER_PKGS }} --locked --optional-deps --each-feature --ignore-unknown-features --features libm --exclude-features ${{ env.FEATURES_DEPENDING_ON_STD }} --target x86_64-unknown-none + - name: cargo check - run: cargo hack check ${{ env.RUST_MIN_VER_PKGS }} --locked --optional-deps --each-feature + run: cargo hack check ${{ env.RUST_MIN_VER_PKGS }} --locked --optional-deps --each-feature --ignore-unknown-features --features std check-msrv-wasm: name: cargo check (msrv) (wasm32) @@ -250,7 +262,7 @@ jobs: save-if: ${{ github.event_name != 'merge_group' }} - name: cargo check - run: cargo hack check ${{ env.RUST_MIN_VER_PKGS }} --locked --target wasm32-unknown-unknown --optional-deps --each-feature + run: cargo hack check ${{ env.RUST_MIN_VER_PKGS }} --locked --target wasm32-unknown-unknown --optional-deps --each-feature --ignore-unknown-features --features std doc: name: cargo doc diff --git a/Cargo.lock b/Cargo.lock index f37cf96..426dbf2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,9 +45,16 @@ checksum = "ce9729cc38c18d86123ab736fd2e7151763ba226ac2490ec092d1dd148825e32" dependencies = [ "arrayvec", "euclid", + "libm", "smallvec", ] +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + [[package]] name = "num-traits" version = "0.2.19" @@ -55,6 +62,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ba5d4e0..96a5eef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,11 @@ exclude = [".github", ".clippy.toml", ".gitignore", ".typos.toml", "benches/", " [workspace] members = ["benches"] +[features] +default = ["std"] +std = ["kurbo/std"] +libm = ["kurbo/libm"] + [dependencies] siphasher = "1.0" # perfect hash implementation for color names -kurbo = "0.12" # ArcTo to CurveTo(s) +kurbo = { version = "0.12", default-features = false } # ArcTo to CurveTo(s) diff --git a/src/angle.rs b/src/angle.rs index d33257b..e5e4120 100644 --- a/src/angle.rs +++ b/src/angle.rs @@ -42,7 +42,7 @@ impl Angle { } } -impl std::str::FromStr for Angle { +impl core::str::FromStr for Angle { type Err = Error; #[inline] @@ -99,7 +99,8 @@ impl Stream<'_> { #[cfg(test)] mod tests { use super::*; - use std::str::FromStr; + use alloc::string::ToString; + use core::str::FromStr; macro_rules! test_p { ($name:ident, $text:expr, $result:expr) => ( diff --git a/src/aspect_ratio.rs b/src/aspect_ratio.rs index 903bdc2..94d07d9 100644 --- a/src/aspect_ratio.rs +++ b/src/aspect_ratio.rs @@ -41,7 +41,7 @@ pub struct AspectRatio { pub slice: bool, } -impl std::str::FromStr for AspectRatio { +impl core::str::FromStr for AspectRatio { type Err = Error; fn from_str(text: &str) -> Result { @@ -109,7 +109,7 @@ impl Default for AspectRatio { #[cfg(test)] mod tests { use super::*; - use std::str::FromStr; + use core::str::FromStr; macro_rules! test { ($name:ident, $text:expr, $result:expr) => ( diff --git a/src/color.rs b/src/color.rs index 7318ead..0a896b8 100644 --- a/src/color.rs +++ b/src/color.rs @@ -3,6 +3,9 @@ use crate::{colors, ByteExt, Error, Stream}; +#[cfg(not(feature = "std"))] +use kurbo::common::FloatFuncs; + /// Representation of the [``] type. /// /// [``]: https://www.w3.org/TR/css-color-3/ @@ -75,7 +78,7 @@ impl Color { } } -impl std::str::FromStr for Color { +impl core::str::FromStr for Color { type Err = Error; /// Parses [CSS3](https://www.w3.org/TR/css-color-3/) `Color` from a string. @@ -304,7 +307,8 @@ fn f64_bound(min: f64, val: f64, max: f64) -> f64 { #[rustfmt::skip] #[cfg(test)] mod tests { - use std::str::FromStr; + use alloc::string::ToString; + use core::str::FromStr; use crate::Color; macro_rules! test { diff --git a/src/colors.rs b/src/colors.rs index 77141f8..20a7ae0 100644 --- a/src/colors.rs +++ b/src/colors.rs @@ -199,7 +199,7 @@ pub fn from_str(text: &str) -> Option { // // https://github.com/sfackler/rust-phf -use std::hash::Hasher; +use core::hash::Hasher; pub struct Map { pub key: u64, diff --git a/src/directional_position.rs b/src/directional_position.rs index 8817ee8..8aa0c6f 100644 --- a/src/directional_position.rs +++ b/src/directional_position.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use crate::{Error, Length, LengthUnit, Stream}; +use alloc::string::ToString; +use alloc::vec; /// List of all SVG directional positions. #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -46,7 +48,7 @@ impl From for Length { } } -impl std::str::FromStr for DirectionalPosition { +impl core::str::FromStr for DirectionalPosition { type Err = Error; #[inline] @@ -102,7 +104,7 @@ impl Stream<'_> { #[cfg(test)] mod tests { use super::*; - use std::str::FromStr; + use core::str::FromStr; macro_rules! test_p { ($name:ident, $text:expr, $result:expr) => ( diff --git a/src/enable_background.rs b/src/enable_background.rs index b1034a1..92af22d 100644 --- a/src/enable_background.rs +++ b/src/enable_background.rs @@ -19,7 +19,7 @@ pub enum EnableBackground { }, } -impl std::str::FromStr for EnableBackground { +impl core::str::FromStr for EnableBackground { type Err = Error; fn from_str(text: &str) -> Result { @@ -71,7 +71,8 @@ impl std::str::FromStr for EnableBackground { #[cfg(test)] mod tests { use super::*; - use std::str::FromStr; + use alloc::string::ToString; + use core::str::FromStr; #[test] fn parse_1() { diff --git a/src/error.rs b/src/error.rs index a59a2aa..528fbd5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,10 @@ // Copyright 2018 the SVG Types Authors // SPDX-License-Identifier: Apache-2.0 OR MIT +use alloc::string::String; +use alloc::vec; +use alloc::vec::Vec; + /// List of all errors. #[derive(Debug, PartialEq, Eq)] pub enum Error { @@ -46,8 +50,8 @@ pub enum Error { InvalidNumber(usize), } -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match *self { Self::UnexpectedEndOfStream => { write!(f, "unexpected end of stream") @@ -93,7 +97,7 @@ impl std::fmt::Display for Error { } } -impl std::error::Error for Error { +impl core::error::Error for Error { fn description(&self) -> &str { "an SVG data parsing error" } diff --git a/src/filter_functions.rs b/src/filter_functions.rs index aa8bb8b..6b2abf5 100644 --- a/src/filter_functions.rs +++ b/src/filter_functions.rs @@ -73,8 +73,8 @@ impl From for FilterValueListParserError { } } -impl std::fmt::Display for FilterValueListParserError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl core::fmt::Display for FilterValueListParserError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match *self { Self::PercentageValue(pos) => { write!(f, "a percentage value detected at position {pos}") @@ -101,7 +101,7 @@ impl std::fmt::Display for FilterValueListParserError { } } -impl std::error::Error for FilterValueListParserError { +impl core::error::Error for FilterValueListParserError { fn description(&self) -> &str { "filter-value-list parsing error" } @@ -351,6 +351,7 @@ fn parse_filter_angle(s: &mut Stream<'_>) -> Result Result, Error> { @@ -36,7 +40,7 @@ pub enum FontFamily { } impl Display for FontFamily { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let str = match self { Self::Monospace => "monospace".to_string(), Self::Serif => "serif".to_string(), diff --git a/src/funciri.rs b/src/funciri.rs index a3fd335..04d5451 100644 --- a/src/funciri.rs +++ b/src/funciri.rs @@ -112,6 +112,7 @@ impl<'a> Stream<'a> { #[cfg(test)] mod tests { use super::*; + use alloc::string::ToString; #[test] fn parse_iri_1() { diff --git a/src/length.rs b/src/length.rs index ec093de..34055e1 100644 --- a/src/length.rs +++ b/src/length.rs @@ -64,7 +64,7 @@ impl Default for Length { } } -impl std::str::FromStr for Length { +impl core::str::FromStr for Length { type Err = Error; #[inline] @@ -187,7 +187,8 @@ impl Iterator for LengthListParser<'_> { #[cfg(test)] mod tests { use super::*; - use std::str::FromStr; + use alloc::string::ToString; + use core::str::FromStr; macro_rules! test_p { ($name:ident, $text:expr, $result:expr) => ( diff --git a/src/lib.rs b/src/lib.rs index f6e3c61..95058a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,11 +51,14 @@ None. */ +#![no_std] #![forbid(unsafe_code)] #![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(missing_copy_implementations)] +extern crate alloc; + macro_rules! matches { ($expression:expr, $($pattern:tt)+) => { match $expression { diff --git a/src/number.rs b/src/number.rs index fad023b..3115826 100644 --- a/src/number.rs +++ b/src/number.rs @@ -1,7 +1,7 @@ // Copyright 2018 the SVG Types Authors // SPDX-License-Identifier: Apache-2.0 OR MIT -use std::str::FromStr; +use core::str::FromStr; use crate::{ByteExt, Error, Stream}; @@ -9,7 +9,7 @@ use crate::{ByteExt, Error, Stream}; #[derive(Clone, Copy, PartialEq, Debug)] pub struct Number(pub f64); -impl std::str::FromStr for Number { +impl core::str::FromStr for Number { type Err = Error; fn from_str(text: &str) -> Result { @@ -165,6 +165,7 @@ impl Iterator for NumberListParser<'_> { #[rustfmt::skip] #[cfg(test)] mod tests { + use alloc::string::ToString; use crate::Stream; macro_rules! test_p { diff --git a/src/paint.rs b/src/paint.rs index ba9bfc4..ff55805 100644 --- a/src/paint.rs +++ b/src/paint.rs @@ -1,7 +1,7 @@ // Copyright 2018 the SVG Types Authors // SPDX-License-Identifier: Apache-2.0 OR MIT -use std::str::FromStr; +use core::str::FromStr; use crate::{Color, Error, Stream}; @@ -119,6 +119,7 @@ impl<'a> Paint<'a> { #[cfg(test)] mod tests { use super::*; + use alloc::string::ToString; macro_rules! test { ($name:ident, $text:expr, $result:expr) => ( diff --git a/src/paint_order.rs b/src/paint_order.rs index 102e9b7..d4b48be 100644 --- a/src/paint_order.rs +++ b/src/paint_order.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use crate::stream::Stream; +use alloc::vec; +use alloc::vec::Vec; /// [`paint-order`] property variants. /// @@ -45,7 +47,7 @@ impl From<[PaintOrderKind; 3]> for PaintOrder { } } -impl std::str::FromStr for PaintOrder { +impl core::str::FromStr for PaintOrder { type Err = (); /// Parses `PaintOrder` from a string. @@ -112,7 +114,7 @@ impl std::str::FromStr for PaintOrder { #[cfg(test)] mod tests { use super::*; - use std::str::FromStr; + use core::str::FromStr; #[test] fn parse_1() { diff --git a/src/path.rs b/src/path.rs index a20539b..2c09cba 100644 --- a/src/path.rs +++ b/src/path.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use crate::{Error, Stream}; +use alloc::vec::Vec; /// Representation of a path segment. /// diff --git a/src/stream.rs b/src/stream.rs index cd6e1b9..881e8ad 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use crate::Error; +use alloc::borrow::ToOwned; +use alloc::vec; /// Extension methods for XML-subset only operations. pub(crate) trait ByteExt { @@ -169,7 +171,7 @@ impl<'a> Stream<'a> { } #[inline] - pub fn chars(&self) -> std::str::Chars<'a> { + pub fn chars(&self) -> core::str::Chars<'a> { self.text[self.pos..].chars() } @@ -349,14 +351,14 @@ impl<'a> Stream<'a> { } if !self.starts_with(text) { - let len = std::cmp::min(text.len(), self.text.len() - self.pos); + let len = core::cmp::min(text.len(), self.text.len() - self.pos); // Collect chars and do not slice a string, // because the `len` can be on the char boundary. // Which lead to a panic. let actual = self.text[self.pos..].chars().take(len).collect(); // Assume that all input `text` are valid UTF-8 strings, so unwrap is safe. - let expected = std::str::from_utf8(text).unwrap().to_owned(); + let expected = core::str::from_utf8(text).unwrap().to_owned(); return Err(Error::InvalidString( vec![actual, expected], diff --git a/src/transform.rs b/src/transform.rs index bd44103..e515e99 100644 --- a/src/transform.rs +++ b/src/transform.rs @@ -1,10 +1,13 @@ // Copyright 2021 the SVG Types Authors // SPDX-License-Identifier: Apache-2.0 OR MIT -use std::f64; +use core::f64; use crate::{Error, Stream}; +#[cfg(not(feature = "std"))] +use kurbo::common::FloatFuncs; + /// Representation of the [``] type. /// /// [``]: https://www.w3.org/TR/SVG2/coords.html#InterfaceSVGTransform @@ -223,7 +226,7 @@ impl TransformListParser<'_> { } } -impl std::str::FromStr for Transform { +impl core::str::FromStr for Transform { type Err = Error; fn from_str(text: &str) -> Result { @@ -279,8 +282,10 @@ fn multiply(ts1: &Transform, ts2: &Transform) -> Transform { #[rustfmt::skip] #[cfg(test)] mod tests { - use std::str::FromStr; use super::*; + use alloc::format; + use alloc::string::ToString; + use core::str::FromStr; macro_rules! test { ($name:ident, $text:expr, $result:expr) => ( diff --git a/src/transform_origin.rs b/src/transform_origin.rs index 351e9a4..a671aa0 100644 --- a/src/transform_origin.rs +++ b/src/transform_origin.rs @@ -73,8 +73,8 @@ pub enum TransformOriginError { ZIndexIsPercentage, } -impl std::fmt::Display for TransformOriginError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl core::fmt::Display for TransformOriginError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match *self { Self::MissingParameters => { write!(f, "transform origin doesn't have enough parameters") @@ -89,13 +89,13 @@ impl std::fmt::Display for TransformOriginError { } } -impl std::error::Error for TransformOriginError { +impl core::error::Error for TransformOriginError { fn description(&self) -> &str { "a transform origin parsing error" } } -impl std::str::FromStr for TransformOrigin { +impl core::str::FromStr for TransformOrigin { type Err = TransformOriginError; fn from_str(text: &str) -> Result { @@ -192,7 +192,8 @@ impl std::str::FromStr for TransformOrigin { #[cfg(test)] mod tests { use super::*; - use std::str::FromStr; + use alloc::string::ToString; + use core::str::FromStr; macro_rules! test { ($name:ident, $text:expr, $result:expr) => ( diff --git a/src/viewbox.rs b/src/viewbox.rs index 13ec660..8de4827 100644 --- a/src/viewbox.rs +++ b/src/viewbox.rs @@ -13,8 +13,8 @@ pub enum ViewBoxError { InvalidSize, } -impl std::fmt::Display for ViewBoxError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl core::fmt::Display for ViewBoxError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match *self { Self::InvalidNumber => { write!(f, "viewBox contains an invalid number") @@ -26,7 +26,7 @@ impl std::fmt::Display for ViewBoxError { } } -impl std::error::Error for ViewBoxError { +impl core::error::Error for ViewBoxError { fn description(&self) -> &str { "a viewBox parsing error" } @@ -51,7 +51,7 @@ impl ViewBox { } } -impl std::str::FromStr for ViewBox { +impl core::str::FromStr for ViewBox { type Err = ViewBoxError; fn from_str(text: &str) -> Result { @@ -82,7 +82,8 @@ impl std::str::FromStr for ViewBox { #[cfg(test)] mod tests { use super::*; - use std::str::FromStr; + use alloc::string::ToString; + use core::str::FromStr; macro_rules! test { ($name:ident, $text:expr, $result:expr) => (