diff --git a/crates/compiler/Cargo.toml b/crates/compiler/Cargo.toml index a7a1ce6..3ef10b1 100644 --- a/crates/compiler/Cargo.toml +++ b/crates/compiler/Cargo.toml @@ -3,7 +3,7 @@ name = "grass_compiler" version = "0.12.2" edition = "2021" description = "Internal implementation of the grass compiler" -readme = "../../README.md" +readme = "README.md" license = "MIT" categories = ["web-programming"] keywords = ["scss", "sass", "css", "web"] @@ -33,8 +33,10 @@ indexmap = "1.9.0" lasso = "0.6" [features] -default = ["random"] +default = ["random", "custom-builtin-fns"] # Option (enabled by default): enable the builtin functions `random([$limit])` and `unique-id()` random = ["rand"] # Option: expose JavaScript-friendly WebAssembly exports -wasm-exports = ["wasm-bindgen"] \ No newline at end of file +wasm-exports = ["wasm-bindgen"] +# Option: expose internals necessary to implement custom builtin functions +custom-builtin-fns = [] \ No newline at end of file diff --git a/crates/compiler/README.md b/crates/compiler/README.md new file mode 100644 index 0000000..61d1a83 --- /dev/null +++ b/crates/compiler/README.md @@ -0,0 +1,5 @@ +# grass_compiler + +This crate exposes the internals of the main package, [`grass`](https://crates.io/crates/grass). For most users, the preferred crate should be `grass`, as it is more stable and has a simpler API. + +This crate will see frequent breaking changes. diff --git a/crates/compiler/src/ast/args.rs b/crates/compiler/src/ast/args.rs index 15b1cc3..efadf08 100644 --- a/crates/compiler/src/ast/args.rs +++ b/crates/compiler/src/ast/args.rs @@ -131,11 +131,11 @@ impl ArgumentDeclaration { #[derive(Debug, Clone)] pub(crate) struct ArgumentInvocation { - pub positional: Vec, - pub named: BTreeMap, - pub rest: Option, - pub keyword_rest: Option, - pub span: Span, + pub(crate) positional: Vec, + pub(crate) named: BTreeMap, + pub(crate) rest: Option, + pub(crate) keyword_rest: Option, + pub(crate) span: Span, } impl ArgumentInvocation { @@ -157,14 +157,18 @@ pub(crate) enum MaybeEvaledArguments { Evaled(ArgumentResult), } +/// Function arguments that have been evaluated +/// +/// Arguments may be passed either positionally or by name. Positional arguments +/// may not come after named ones. #[derive(Debug, Clone)] -pub(crate) struct ArgumentResult { - pub positional: Vec, - pub named: BTreeMap, - pub separator: ListSeparator, - pub span: Span, +pub struct ArgumentResult { + pub(crate) positional: Vec, + pub(crate) named: BTreeMap, + pub(crate) separator: ListSeparator, + pub(crate) span: Span, // todo: hack - pub touched: BTreeSet, + pub(crate) touched: BTreeSet, } impl ArgumentResult { @@ -180,7 +184,7 @@ impl ArgumentResult { /// Get a positional argument by 0-indexed position /// - /// Replaces argument with `Value::Null` gravestone + /// Replaces argument with [`Value::Null`] gravestone pub fn get_positional(&mut self, idx: usize) -> Option> { let val = match self.positional.get_mut(idx) { Some(v) => Some(Spanned { @@ -194,6 +198,11 @@ impl ArgumentResult { val } + /// Get an argument by either name or position + /// + /// If the named argument does not exist, then the position is checked. Like + /// [`ArgumentResult::get_named`] and [`ArgumentResult::get_positional`], this + /// function removes the argument or replaces it with a gravestone pub fn get>(&mut self, position: usize, name: T) -> Option> { match self.get_named(name) { Some(v) => Some(v), @@ -201,7 +210,8 @@ impl ArgumentResult { } } - pub fn get_err(&mut self, position: usize, name: &'static str) -> SassResult { + /// Like [`ArgumentResult::get`], but returns a result if the argument doesn't exist + pub fn get_err(&mut self, position: usize, name: &str) -> SassResult { match self.get_named(name) { Some(v) => Ok(v.node), None => match self.get_positional(position) { @@ -215,11 +225,12 @@ impl ArgumentResult { self.span } - pub fn len(&self) -> usize { + pub(crate) fn len(&self) -> usize { self.positional.len() + self.named.len() } - pub fn min_args(&self, min: usize) -> SassResult<()> { + /// Assert that this function has at least `min` number of args + pub(crate) fn min_args(&self, min: usize) -> SassResult<()> { let len = self.len(); if len < min { if min == 1 { @@ -230,6 +241,7 @@ impl ArgumentResult { Ok(()) } + /// Assert that this function has at most `max` number of args pub fn max_args(&self, max: usize) -> SassResult<()> { let len = self.len(); if len > max { @@ -252,6 +264,8 @@ impl ArgumentResult { Ok(()) } + /// Get an argument by name or position. If the argument does not exist, use + /// the default value provided pub fn default_arg(&mut self, position: usize, name: &'static str, default: Value) -> Value { match self.get(position, name) { Some(val) => val.node, @@ -259,7 +273,7 @@ impl ArgumentResult { } } - pub fn remove_positional(&mut self, position: usize) -> Option { + pub(crate) fn remove_positional(&mut self, position: usize) -> Option { if self.positional.len() > position { Some(self.positional.remove(position)) } else { @@ -267,7 +281,7 @@ impl ArgumentResult { } } - pub fn get_variadic(self) -> SassResult>> { + pub(crate) fn get_variadic(self) -> SassResult>> { if let Some((name, _)) = self.named.iter().next() { return Err((format!("No argument named ${}.", name), self.span).into()); } diff --git a/crates/compiler/src/ast/mod.rs b/crates/compiler/src/ast/mod.rs index 5ec3781..d58761b 100644 --- a/crates/compiler/src/ast/mod.rs +++ b/crates/compiler/src/ast/mod.rs @@ -8,6 +8,8 @@ pub(crate) use stmt::*; pub(crate) use style::*; pub(crate) use unknown::*; +pub use args::ArgumentResult; + mod args; mod css; mod expr; diff --git a/crates/compiler/src/builtin/functions/mod.rs b/crates/compiler/src/builtin/functions/mod.rs index 29d8f2f..01ab9ae 100644 --- a/crates/compiler/src/builtin/functions/mod.rs +++ b/crates/compiler/src/builtin/functions/mod.rs @@ -3,6 +3,7 @@ use std::{ collections::{BTreeSet, HashMap}, + fmt, sync::atomic::{AtomicUsize, Ordering}, }; @@ -23,12 +24,47 @@ pub(crate) type GlobalFunctionMap = HashMap<&'static str, Builtin>; static FUNCTION_COUNT: AtomicUsize = AtomicUsize::new(0); +/// A function implemented in rust that is accessible from within Sass +/// +/// +/// #### Usage +/// ```rust +/// use grass_compiler::{ +/// sass_value::{ArgumentResult, SassNumber, Value}, +/// Builtin, Options, Result as SassResult, Visitor, +/// }; +/// +/// // An example function that looks up the length of an array or map and adds 2 to it +/// fn length(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult { +/// args.max_args(1)?; +/// +/// let len = args.get_err(0, "list")?.as_list().len(); +/// +/// Ok(Value::Dimension(SassNumber::new_unitless(len + 2))) +/// } +/// +/// fn main() { +/// let options = Options::default().add_custom_fn("length", Builtin::new(length)); +/// let css = grass_compiler::from_string("a { color: length([a, b]); }", &options).unwrap(); +/// +/// assert_eq!(css, "a {\n color: 4;\n}\n"); +/// } +/// ``` #[derive(Clone)] -pub(crate) struct Builtin( - pub fn(ArgumentResult, &mut Visitor) -> SassResult, +pub struct Builtin( + pub(crate) fn(ArgumentResult, &mut Visitor) -> SassResult, usize, ); +impl fmt::Debug for Builtin { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Builtin") + .field("id", &self.1) + .field("fn_ptr", &(self.0 as usize)) + .finish() + } +} + impl Builtin { pub fn new(body: fn(ArgumentResult, &mut Visitor) -> SassResult) -> Builtin { let count = FUNCTION_COUNT.fetch_add(1, Ordering::Relaxed); diff --git a/crates/compiler/src/builtin/mod.rs b/crates/compiler/src/builtin/mod.rs index 0e1950c..36aed69 100644 --- a/crates/compiler/src/builtin/mod.rs +++ b/crates/compiler/src/builtin/mod.rs @@ -2,10 +2,12 @@ mod functions; pub(crate) mod modules; pub(crate) use functions::{ - color, list, map, math, meta, selector, string, Builtin, DISALLOWED_PLAIN_CSS_FUNCTION_NAMES, + color, list, map, math, meta, selector, string, DISALLOWED_PLAIN_CSS_FUNCTION_NAMES, GLOBAL_FUNCTIONS, }; +pub use functions::Builtin; + /// Imports common to all builtin fns mod builtin_imports { pub(crate) use super::functions::{Builtin, GlobalFunctionMap, GLOBAL_FUNCTIONS}; diff --git a/crates/compiler/src/color/mod.rs b/crates/compiler/src/color/mod.rs index e30e835..e805619 100644 --- a/crates/compiler/src/color/mod.rs +++ b/crates/compiler/src/color/mod.rs @@ -22,11 +22,11 @@ mod name; // todo: only store alpha once on color #[derive(Debug, Clone)] -pub(crate) struct Color { +pub struct Color { rgba: Rgb, hsla: Option, alpha: Number, - pub format: ColorFormat, + pub(crate) format: ColorFormat, } #[derive(Debug, Clone, Eq, PartialEq)] @@ -55,7 +55,7 @@ impl PartialEq for Color { impl Eq for Color {} impl Color { - pub const fn new_rgba( + pub(crate) const fn new_rgba( red: Number, green: Number, blue: Number, diff --git a/crates/compiler/src/common.rs b/crates/compiler/src/common.rs index 688c8f6..a81a401 100644 --- a/crates/compiler/src/common.rs +++ b/crates/compiler/src/common.rs @@ -63,8 +63,9 @@ impl Display for BinaryOp { } } +/// Strings can either have quotes or not #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub(crate) enum QuoteKind { +pub enum QuoteKind { Quoted, None, } @@ -79,14 +80,15 @@ impl Display for QuoteKind { } } +/// Lists can either be bracketed or not #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub(crate) enum Brackets { +pub enum Brackets { None, Bracketed, } #[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub(crate) enum ListSeparator { +pub enum ListSeparator { Space, Comma, Slash, @@ -115,7 +117,7 @@ impl ListSeparator { /// /// This struct protects that invariant by normalizing all underscores into hypens. #[derive(Clone, Eq, PartialEq, Hash, PartialOrd, Ord, Copy)] -pub(crate) struct Identifier(InternedString); +pub struct Identifier(InternedString); impl fmt::Debug for Identifier { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/crates/compiler/src/evaluate/mod.rs b/crates/compiler/src/evaluate/mod.rs index 4b428c7..212faf7 100644 --- a/crates/compiler/src/evaluate/mod.rs +++ b/crates/compiler/src/evaluate/mod.rs @@ -1,6 +1,6 @@ pub(crate) use bin_op::{cmp, div}; pub(crate) use env::Environment; -pub(crate) use visitor::*; +pub use visitor::Visitor; mod bin_op; mod css_tree; diff --git a/crates/compiler/src/evaluate/visitor.rs b/crates/compiler/src/evaluate/visitor.rs index 89e0525..2cfa22d 100644 --- a/crates/compiler/src/evaluate/visitor.rs +++ b/crates/compiler/src/evaluate/visitor.rs @@ -99,24 +99,26 @@ pub(crate) struct CallableContentBlock { env: Environment, } -pub(crate) struct Visitor<'a> { - pub declaration_name: Option, - pub flags: ContextFlags, - pub env: Environment, - pub style_rule_ignoring_at_root: Option, +/// Evaluation context of the current execution +#[derive(Debug)] +pub struct Visitor<'a> { + pub(crate) declaration_name: Option, + pub(crate) flags: ContextFlags, + pub(crate) env: Environment, + pub(crate) style_rule_ignoring_at_root: Option, // avoid emitting duplicate warnings for the same span - pub warnings_emitted: HashSet, - pub media_queries: Option>, - pub media_query_sources: Option>, - pub extender: ExtensionStore, - pub current_import_path: PathBuf, - pub is_plain_css: bool, + pub(crate) warnings_emitted: HashSet, + pub(crate) media_queries: Option>, + pub(crate) media_query_sources: Option>, + pub(crate) extender: ExtensionStore, + pub(crate) current_import_path: PathBuf, + pub(crate) is_plain_css: bool, css_tree: CssTree, parent: Option, configuration: Arc>, import_nodes: Vec, - pub options: &'a Options<'a>, - pub map: &'a mut CodeMap, + pub(crate) options: &'a Options<'a>, + pub(crate) map: &'a mut CodeMap, // todo: remove span_before: Span, import_cache: BTreeMap, @@ -127,7 +129,7 @@ pub(crate) struct Visitor<'a> { } impl<'a> Visitor<'a> { - pub fn new( + pub(crate) fn new( path: &Path, options: &'a Options<'a>, map: &'a mut CodeMap, @@ -163,7 +165,7 @@ impl<'a> Visitor<'a> { } } - pub fn visit_stylesheet(&mut self, mut style_sheet: StyleSheet) -> SassResult<()> { + pub(crate) fn visit_stylesheet(&mut self, mut style_sheet: StyleSheet) -> SassResult<()> { let was_in_plain_css = self.is_plain_css; self.is_plain_css = style_sheet.is_plain_css; mem::swap(&mut self.current_import_path, &mut style_sheet.url); @@ -179,7 +181,7 @@ impl<'a> Visitor<'a> { Ok(()) } - pub fn finish(mut self) -> Vec { + pub(crate) fn finish(mut self) -> Vec { let mut finished_tree = self.css_tree.finish(); if self.import_nodes.is_empty() { finished_tree @@ -196,7 +198,7 @@ impl<'a> Visitor<'a> { } // todo: we really don't have to return Option from all of these children - pub fn visit_stmt(&mut self, stmt: AstStmt) -> SassResult> { + pub(crate) fn visit_stmt(&mut self, stmt: AstStmt) -> SassResult> { match stmt { AstStmt::RuleSet(ruleset) => self.visit_ruleset(ruleset), AstStmt::Style(style) => self.visit_style(style), @@ -590,7 +592,7 @@ impl<'a> Visitor<'a> { Ok(module) } - pub fn load_module( + pub(crate) fn load_module( &mut self, url: &Path, configuration: Option>>, @@ -690,7 +692,7 @@ impl<'a> Visitor<'a> { Ok(()) } - pub fn assert_configuration_is_empty( + pub(crate) fn assert_configuration_is_empty( config: &Arc>, name_in_error: bool, ) -> SassResult<()> { @@ -860,7 +862,7 @@ impl<'a> Visitor<'a> { Err(("Can't find stylesheet to import.", span).into()) } - pub fn load_style_sheet( + pub(crate) fn load_style_sheet( &mut self, url: &str, // default=false @@ -1202,7 +1204,7 @@ impl<'a> Visitor<'a> { self.env.insert_fn(func); } - pub fn parse_selector_from_string( + pub(crate) fn parse_selector_from_string( &mut self, selector_text: &str, allows_parent: bool, @@ -1507,7 +1509,7 @@ impl<'a> Visitor<'a> { Ok(None) } - pub fn emit_warning(&mut self, message: &str, span: Span) { + pub(crate) fn emit_warning(&mut self, message: &str, span: Span) { if self.options.quiet { return; } @@ -2406,7 +2408,9 @@ impl<'a> Visitor<'a> { let func = match self.env.get_fn(name, func_call.namespace)? { Some(func) => func, None => { - if let Some(f) = GLOBAL_FUNCTIONS.get(name.as_str()) { + if let Some(f) = self.options.custom_fns.get(name.as_str()) { + SassFunction::Builtin(f.clone(), name) + } else if let Some(f) = GLOBAL_FUNCTIONS.get(name.as_str()) { SassFunction::Builtin(f.clone(), name) } else { if func_call.namespace.is_some() { @@ -2830,7 +2834,7 @@ impl<'a> Visitor<'a> { expr.to_css_string(span, self.options.is_compressed()) } - pub fn visit_ruleset(&mut self, ruleset: AstRuleSet) -> SassResult> { + pub(crate) fn visit_ruleset(&mut self, ruleset: AstRuleSet) -> SassResult> { if self.declaration_name.is_some() { return Err(( "Style rules may not be used within nested declarations.", @@ -2954,7 +2958,7 @@ impl<'a> Visitor<'a> { !self.flags.at_root_excluding_style_rule() && self.style_rule_ignoring_at_root.is_some() } - pub fn visit_style(&mut self, style: AstStyle) -> SassResult> { + pub(crate) fn visit_style(&mut self, style: AstStyle) -> SassResult> { if !self.style_rule_exists() && !self.flags.in_unknown_at_rule() && !self.flags.in_keyframes() diff --git a/crates/compiler/src/interner.rs b/crates/compiler/src/interner.rs index 5a2c37f..809c275 100644 --- a/crates/compiler/src/interner.rs +++ b/crates/compiler/src/interner.rs @@ -6,7 +6,7 @@ use std::fmt::{self, Display}; thread_local!(static STRINGS: RefCell> = RefCell::new(Rodeo::default())); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub(crate) struct InternedString(Spur); +pub struct InternedString(Spur); impl InternedString { pub fn get_or_intern>(s: T) -> Self { diff --git a/crates/compiler/src/lib.rs b/crates/compiler/src/lib.rs index be352ad..3e4532a 100644 --- a/crates/compiler/src/lib.rs +++ b/crates/compiler/src/lib.rs @@ -32,6 +32,7 @@ grass input.scss ``` */ +#![cfg_attr(doc, feature(doc_cfg))] #![warn(clippy::all, clippy::cargo, clippy::dbg_macro)] #![deny(missing_debug_implementations)] #![allow( @@ -94,8 +95,22 @@ pub use crate::error::{ }; pub use crate::fs::{Fs, NullFs, StdFs}; pub use crate::options::{InputSyntax, Options, OutputStyle}; +pub use crate::{builtin::Builtin, evaluate::Visitor}; pub(crate) use crate::{context_flags::ContextFlags, lexer::Token}; -use crate::{evaluate::Visitor, lexer::Lexer, parse::ScssParser}; +use crate::{lexer::Lexer, parse::ScssParser}; + +pub mod sass_value { + pub use crate::{ + ast::ArgumentResult, + color::Color, + common::{BinaryOp, Brackets, ListSeparator, QuoteKind}, + unit::{ComplexUnit, Unit}, + value::{ + ArgList, CalculationArg, CalculationName, Number, SassCalculation, SassFunction, + SassMap, SassNumber, Value, + }, + }; +} mod ast; mod builtin; @@ -209,8 +224,8 @@ pub fn from_path>(p: P, options: &Options) -> Result { /// } /// ``` #[inline] -pub fn from_string(input: String, options: &Options) -> Result { - from_string_with_file_name(input, "stdin", options) +pub fn from_string>(input: S, options: &Options) -> Result { + from_string_with_file_name(input.into(), "stdin", options) } #[cfg(feature = "wasm-exports")] diff --git a/crates/compiler/src/options.rs b/crates/compiler/src/options.rs index 834535c..8cdcd48 100644 --- a/crates/compiler/src/options.rs +++ b/crates/compiler/src/options.rs @@ -1,6 +1,9 @@ -use std::path::{Path, PathBuf}; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; -use crate::{Fs, StdFs}; +use crate::{builtin::Builtin, Fs, StdFs}; /// Configuration for Sass compilation /// @@ -15,6 +18,7 @@ pub struct Options<'a> { pub(crate) unicode_error_messages: bool, pub(crate) quiet: bool, pub(crate) input_syntax: Option, + pub(crate) custom_fns: HashMap, } impl Default for Options<'_> { @@ -28,6 +32,7 @@ impl Default for Options<'_> { unicode_error_messages: true, quiet: false, input_syntax: None, + custom_fns: HashMap::new(), } } } @@ -139,7 +144,7 @@ impl<'a> Options<'a> { /// This option forces Sass to parse input using the given syntax. /// /// By default, Sass will attempt to read the file extension to determine - /// the syntax. If this is not possible, it will default to [`InputSyntax::Scss`] + /// the syntax. If this is not possible, it will default to [`InputSyntax::Scss`]. /// /// This flag only affects the first file loaded. Files that are loaded using /// `@import`, `@use`, or `@forward` will always have their syntax inferred. @@ -150,6 +155,19 @@ impl<'a> Options<'a> { self } + /// Add a custom function accessible from within Sass + /// + /// See the [`Builtin`] documentation for additional information + #[must_use] + #[inline] + #[cfg(feature = "custom-builtin-fns")] + #[cfg(any(feature = "custom-builtin-fns", doc))] + #[cfg_attr(doc, doc(cfg(feature = "custom-builtin-fns")))] + pub fn add_custom_fn>(mut self, name: S, func: Builtin) -> Self { + self.custom_fns.insert(name.into(), func); + self + } + pub(crate) fn is_compressed(&self) -> bool { matches!(self.style, OutputStyle::Compressed) } diff --git a/crates/compiler/src/selector/mod.rs b/crates/compiler/src/selector/mod.rs index 8040d88..d8982d2 100644 --- a/crates/compiler/src/selector/mod.rs +++ b/crates/compiler/src/selector/mod.rs @@ -22,7 +22,7 @@ mod simple; // todo: delete this selector wrapper #[derive(Clone, Debug, Eq, PartialEq)] -pub(crate) struct Selector(pub SelectorList); +pub struct Selector(pub(crate) SelectorList); impl Selector { /// Small wrapper around `SelectorList`'s method that turns an empty parent selector diff --git a/crates/compiler/src/unit/mod.rs b/crates/compiler/src/unit/mod.rs index 7e38e55..e43e0a2 100644 --- a/crates/compiler/src/unit/mod.rs +++ b/crates/compiler/src/unit/mod.rs @@ -7,7 +7,7 @@ pub(crate) use conversion::{known_compatibilities_by_unit, UNIT_CONVERSION_TABLE mod conversion; #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub(crate) enum Unit { +pub enum Unit { // Absolute units /// Pixels Px, @@ -41,7 +41,7 @@ pub(crate) enum Unit { /// found in the font used to render it Ic, /// Equal to the computed value of the line-height property on the root element - /// (typically ), converted to an absolute length + /// (typically \), converted to an absolute length Rlh, // Viewport relative units @@ -104,9 +104,9 @@ pub(crate) enum Unit { } #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub(crate) struct ComplexUnit { - pub(crate) numer: Vec, - pub(crate) denom: Vec, +pub struct ComplexUnit { + pub numer: Vec, + pub denom: Vec, } pub(crate) fn are_any_convertible(units1: &[Unit], units2: &[Unit]) -> bool { @@ -135,7 +135,7 @@ pub(crate) enum UnitKind { } impl Unit { - pub fn new(mut numer: Vec, denom: Vec) -> Self { + pub(crate) fn new(mut numer: Vec, denom: Vec) -> Self { if denom.is_empty() && numer.is_empty() { Unit::None } else if denom.is_empty() && numer.len() == 1 { @@ -145,7 +145,7 @@ impl Unit { } } - pub fn numer_and_denom(self) -> (Vec, Vec) { + pub(crate) fn numer_and_denom(self) -> (Vec, Vec) { match self { Self::Complex(complex) => (complex.numer.clone(), complex.denom.clone()), Self::None => (Vec::new(), Vec::new()), @@ -153,17 +153,17 @@ impl Unit { } } - pub fn invert(self) -> Self { + pub(crate) fn invert(self) -> Self { let (numer, denom) = self.numer_and_denom(); Self::new(denom, numer) } - pub fn is_complex(&self) -> bool { + pub(crate) fn is_complex(&self) -> bool { matches!(self, Unit::Complex(complex) if complex.numer.len() != 1 || !complex.denom.is_empty()) } - pub fn comparable(&self, other: &Unit) -> bool { + pub(crate) fn comparable(&self, other: &Unit) -> bool { if other == &Unit::None { return true; } diff --git a/crates/compiler/src/value/arglist.rs b/crates/compiler/src/value/arglist.rs index de524b8..25ea16d 100644 --- a/crates/compiler/src/value/arglist.rs +++ b/crates/compiler/src/value/arglist.rs @@ -5,7 +5,7 @@ use crate::common::{Identifier, ListSeparator}; use super::Value; #[derive(Debug, Clone)] -pub(crate) struct ArgList { +pub struct ArgList { pub elems: Vec, were_keywords_accessed: Arc>, // todo: special wrapper around this field to avoid having to make it private? diff --git a/crates/compiler/src/value/calculation.rs b/crates/compiler/src/value/calculation.rs index 1706730..edf73b2 100644 --- a/crates/compiler/src/value/calculation.rs +++ b/crates/compiler/src/value/calculation.rs @@ -13,7 +13,7 @@ use crate::{ }; #[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) enum CalculationArg { +pub enum CalculationArg { Number(SassNumber), Calculation(SassCalculation), String(String), @@ -38,7 +38,7 @@ impl CalculationArg { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub(crate) enum CalculationName { +pub enum CalculationName { Calc, Min, Max, @@ -57,13 +57,13 @@ impl fmt::Display for CalculationName { } impl CalculationName { - pub fn in_min_or_max(self) -> bool { + pub(crate) fn in_min_or_max(self) -> bool { self == CalculationName::Min || self == CalculationName::Max } } #[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) struct SassCalculation { +pub struct SassCalculation { pub name: CalculationName, pub args: Vec, } diff --git a/crates/compiler/src/value/map.rs b/crates/compiler/src/value/map.rs index 1e3ce50..6b609be 100644 --- a/crates/compiler/src/value/map.rs +++ b/crates/compiler/src/value/map.rs @@ -8,7 +8,7 @@ use crate::{ }; #[derive(Debug, Clone, Default)] -pub(crate) struct SassMap(Vec<(Spanned, Value)>); +pub struct SassMap(Vec<(Spanned, Value)>); impl PartialEq for SassMap { fn eq(&self, other: &Self) -> bool { diff --git a/crates/compiler/src/value/mod.rs b/crates/compiler/src/value/mod.rs index c50b2ca..bf84c8d 100644 --- a/crates/compiler/src/value/mod.rs +++ b/crates/compiler/src/value/mod.rs @@ -14,12 +14,13 @@ use crate::{ Options, OutputStyle, }; -pub(crate) use arglist::ArgList; -pub(crate) use calculation::*; -pub(crate) use map::SassMap; -pub(crate) use number::*; -pub(crate) use sass_function::{SassFunction, UserDefinedFunction}; -pub(crate) use sass_number::{conversion_factor, SassNumber}; +pub use arglist::ArgList; +pub use calculation::*; +pub use map::SassMap; +pub use number::*; +pub use sass_function::{SassFunction, UserDefinedFunction}; +pub(crate) use sass_number::conversion_factor; +pub use sass_number::SassNumber; mod arglist; mod calculation; @@ -29,7 +30,7 @@ mod sass_function; mod sass_number; #[derive(Debug, Clone)] -pub(crate) enum Value { +pub enum Value { True, False, Null, diff --git a/crates/compiler/src/value/number.rs b/crates/compiler/src/value/number.rs index 02aea39..281ae78 100644 --- a/crates/compiler/src/value/number.rs +++ b/crates/compiler/src/value/number.rs @@ -27,7 +27,7 @@ fn inverse_epsilon() -> f64 { /// operations -- namely a Sass-compatible modulo #[derive(Clone, Copy, PartialOrd)] #[repr(transparent)] -pub(crate) struct Number(pub f64); +pub struct Number(pub f64); impl PartialEq for Number { fn eq(&self, other: &Self) -> bool { @@ -84,6 +84,7 @@ pub(crate) fn fuzzy_less_than_or_equals(number1: f64, number2: f64) -> bool { } impl Number { + /// This differs from `std::cmp::min` when either value is NaN pub fn min(self, other: Self) -> Self { if self < other { self @@ -92,6 +93,7 @@ impl Number { } } + /// This differs from `std::cmp::max` when either value is NaN pub fn max(self, other: Self) -> Self { if self > other { self diff --git a/crates/compiler/src/value/sass_function.rs b/crates/compiler/src/value/sass_function.rs index 26d96f3..801d620 100644 --- a/crates/compiler/src/value/sass_function.rs +++ b/crates/compiler/src/value/sass_function.rs @@ -7,7 +7,7 @@ use crate::{ast::AstFunctionDecl, builtin::Builtin, common::Identifier, evaluate /// The function name is stored in addition to the body /// for use in the builtin function `inspect()` #[derive(Clone, Eq, PartialEq)] -pub(crate) enum SassFunction { +pub enum SassFunction { // todo: Cow<'static>? /// Builtin functions are those that have been implemented in Rust and are /// in the global scope. @@ -23,10 +23,10 @@ pub(crate) enum SassFunction { } #[derive(Debug, Clone)] -pub(crate) struct UserDefinedFunction { - pub function: Arc, +pub struct UserDefinedFunction { + pub(crate) function: Arc, pub name: Identifier, - pub env: Environment, + pub(crate) env: Environment, } impl PartialEq for UserDefinedFunction { diff --git a/crates/compiler/src/value/sass_number.rs b/crates/compiler/src/value/sass_number.rs index a28654b..235e2a4 100644 --- a/crates/compiler/src/value/sass_number.rs +++ b/crates/compiler/src/value/sass_number.rs @@ -15,7 +15,7 @@ use crate::{ use super::{fuzzy_as_int, Number}; #[derive(Debug, Clone)] -pub(crate) struct SassNumber { +pub struct SassNumber { pub num: Number, pub unit: Unit, pub as_slash: Option>, @@ -53,7 +53,7 @@ impl SassNumber { } #[allow(clippy::collapsible_if)] - pub fn multiply_units(&self, mut num: f64, other_unit: Unit) -> SassNumber { + pub(crate) fn multiply_units(&self, mut num: f64, other_unit: Unit) -> SassNumber { let (numer_units, denom_units) = self.unit.clone().numer_and_denom(); let (other_numer, other_denom) = other_unit.numer_and_denom();