diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 789808d..7af89eb 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. @@ -105,11 +111,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) @@ -135,10 +144,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 @@ -216,8 +225,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) @@ -242,7 +254,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 a65ed31..ba2199e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,9 +29,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89234b2cc610a7dd927ebde6b41dd1a5d4214cffaef4cf1fb2195d592f92518f" dependencies = [ "arrayvec", + "libm", "smallvec", ] +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + [[package]] name = "siphasher" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index 7b52869..ca2fb3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,11 @@ exclude = ["benches/", "codegen/", "fuzz/"] [workspace] members = ["benches"] +[features] +default = ["std"] +std = ["kurbo/std"] +libm = ["kurbo/libm"] + [dependencies] siphasher = "1.0" # perfect hash implementation for color names -kurbo = "0.11" # ArcTo to CurveTo(s) +kurbo = { version = "0.11", default-features = false } # ArcTo to CurveTo(s) diff --git a/src/angle.rs b/src/angle.rs index baab9b5..a18ea2b 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,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/aspect_ratio.rs b/src/aspect_ratio.rs index 869e64f..9e96812 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 c9d47ce..2303c33 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. @@ -300,7 +303,7 @@ fn hue_to_rgb(t1: f32, t2: f32, mut hue: f32) -> f32 { #[inline] fn bound(min: T, val: T, max: T) -> T { - std::cmp::max(min, std::cmp::min(max, val)) + core::cmp::max(min, core::cmp::min(max, val)) } #[inline] @@ -314,7 +317,7 @@ fn f64_bound(min: f64, val: f64, max: f64) -> f64 { #[rustfmt::skip] #[cfg(test)] mod tests { - use std::str::FromStr; + 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 06faae7..487c2da 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)] @@ -52,7 +54,7 @@ impl From for Length { } } -impl std::str::FromStr for DirectionalPosition { +impl core::str::FromStr for DirectionalPosition { type Err = Error; #[inline] @@ -108,7 +110,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 1352588..9108f5b 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,7 @@ impl std::str::FromStr for EnableBackground { #[cfg(test)] mod tests { use super::*; - use std::str::FromStr; + use core::str::FromStr; #[test] fn parse_1() { diff --git a/src/error.rs b/src/error.rs index 6699ab1..df11ef1 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 { Error::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 1e94384..18d6a67 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 { FilterValueListParserError::PercentageValue(pos) => { write!(f, "a percentage value detected at position {}", pos) @@ -102,7 +102,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" } diff --git a/src/font.rs b/src/font.rs index 42d5396..e8c8ccc 100644 --- a/src/font.rs +++ b/src/font.rs @@ -3,7 +3,11 @@ use crate::stream::{ByteExt, Stream}; use crate::Error; -use std::fmt::Display; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec; +use alloc::vec::Vec; +use core::fmt::Display; /// Parses a list of font families and generic families from a string. pub fn parse_font_families(text: &str) -> 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 { FontFamily::Monospace => "monospace".to_string(), FontFamily::Serif => "serif".to_string(), diff --git a/src/length.rs b/src/length.rs index c67a049..2c7daee 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,7 @@ impl Iterator for LengthListParser<'_> { #[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/lib.rs b/src/lib.rs index f6e3c61..85f42a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,11 +51,14 @@ None. */ +#![cfg_attr(all(not(feature = "std"), not(test)), 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..a2362e4 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 { diff --git a/src/paint.rs b/src/paint.rs index ba9bfc4..e7aac3b 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}; diff --git a/src/paint_order.rs b/src/paint_order.rs index 3999ced..2079c57 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 ca35bb3..ababfb1 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 9683c36..d22fc36 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,7 +282,7 @@ fn multiply(ts1: &Transform, ts2: &Transform) -> Transform { #[rustfmt::skip] #[cfg(test)] mod tests { - use std::str::FromStr; + use core::str::FromStr; use super::*; macro_rules! test { diff --git a/src/transform_origin.rs b/src/transform_origin.rs index 00ba839..afa89e5 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 { TransformOriginError::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,7 @@ impl std::str::FromStr for TransformOrigin { #[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/viewbox.rs b/src/viewbox.rs index f1ed629..ef3b982 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 { ViewBoxError::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,7 @@ impl std::str::FromStr for ViewBox { #[cfg(test)] mod tests { use super::*; - use std::str::FromStr; + use core::str::FromStr; macro_rules! test { ($name:ident, $text:expr, $result:expr) => (