expose more internals

This commit is contained in:
Connor Skees 2023-02-18 15:05:51 -05:00
parent 7d19140b4d
commit b13fcc3f08
22 changed files with 197 additions and 94 deletions

View File

@ -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"]
wasm-exports = ["wasm-bindgen"]
# Option: expose internals necessary to implement custom builtin functions
custom-builtin-fns = []

View File

@ -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.

View File

@ -131,11 +131,11 @@ impl ArgumentDeclaration {
#[derive(Debug, Clone)]
pub(crate) struct ArgumentInvocation {
pub positional: Vec<AstExpr>,
pub named: BTreeMap<Identifier, AstExpr>,
pub rest: Option<AstExpr>,
pub keyword_rest: Option<AstExpr>,
pub span: Span,
pub(crate) positional: Vec<AstExpr>,
pub(crate) named: BTreeMap<Identifier, AstExpr>,
pub(crate) rest: Option<AstExpr>,
pub(crate) keyword_rest: Option<AstExpr>,
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<Value>,
pub named: BTreeMap<Identifier, Value>,
pub separator: ListSeparator,
pub span: Span,
pub struct ArgumentResult {
pub(crate) positional: Vec<Value>,
pub(crate) named: BTreeMap<Identifier, Value>,
pub(crate) separator: ListSeparator,
pub(crate) span: Span,
// todo: hack
pub touched: BTreeSet<usize>,
pub(crate) touched: BTreeSet<usize>,
}
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<Spanned<Value>> {
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<T: Into<Identifier>>(&mut self, position: usize, name: T) -> Option<Spanned<Value>> {
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<Value> {
/// Like [`ArgumentResult::get`], but returns a result if the argument doesn't exist
pub fn get_err(&mut self, position: usize, name: &str) -> SassResult<Value> {
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<Value> {
pub(crate) fn remove_positional(&mut self, position: usize) -> Option<Value> {
if self.positional.len() > position {
Some(self.positional.remove(position))
} else {
@ -267,7 +281,7 @@ impl ArgumentResult {
}
}
pub fn get_variadic(self) -> SassResult<Vec<Spanned<Value>>> {
pub(crate) fn get_variadic(self) -> SassResult<Vec<Spanned<Value>>> {
if let Some((name, _)) = self.named.iter().next() {
return Err((format!("No argument named ${}.", name), self.span).into());
}

View File

@ -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;

View File

@ -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<Value> {
/// 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<Value>,
pub struct Builtin(
pub(crate) fn(ArgumentResult, &mut Visitor) -> SassResult<Value>,
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<Value>) -> Builtin {
let count = FUNCTION_COUNT.fetch_add(1, Ordering::Relaxed);

View File

@ -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};

View File

@ -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<Hsl>,
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,

View File

@ -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 {

View File

@ -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;

View File

@ -99,24 +99,26 @@ pub(crate) struct CallableContentBlock {
env: Environment,
}
pub(crate) struct Visitor<'a> {
pub declaration_name: Option<String>,
pub flags: ContextFlags,
pub env: Environment,
pub style_rule_ignoring_at_root: Option<ExtendedSelector>,
/// Evaluation context of the current execution
#[derive(Debug)]
pub struct Visitor<'a> {
pub(crate) declaration_name: Option<String>,
pub(crate) flags: ContextFlags,
pub(crate) env: Environment,
pub(crate) style_rule_ignoring_at_root: Option<ExtendedSelector>,
// avoid emitting duplicate warnings for the same span
pub warnings_emitted: HashSet<Span>,
pub media_queries: Option<Vec<MediaQuery>>,
pub media_query_sources: Option<IndexSet<MediaQuery>>,
pub extender: ExtensionStore,
pub current_import_path: PathBuf,
pub is_plain_css: bool,
pub(crate) warnings_emitted: HashSet<Span>,
pub(crate) media_queries: Option<Vec<MediaQuery>>,
pub(crate) media_query_sources: Option<IndexSet<MediaQuery>>,
pub(crate) extender: ExtensionStore,
pub(crate) current_import_path: PathBuf,
pub(crate) is_plain_css: bool,
css_tree: CssTree,
parent: Option<CssTreeIdx>,
configuration: Arc<RefCell<Configuration>>,
import_nodes: Vec<CssStmt>,
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<PathBuf, StyleSheet>,
@ -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<CssStmt> {
pub(crate) fn finish(mut self) -> Vec<CssStmt> {
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<Value> from all of these children
pub fn visit_stmt(&mut self, stmt: AstStmt) -> SassResult<Option<Value>> {
pub(crate) fn visit_stmt(&mut self, stmt: AstStmt) -> SassResult<Option<Value>> {
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<Arc<RefCell<Configuration>>>,
@ -690,7 +692,7 @@ impl<'a> Visitor<'a> {
Ok(())
}
pub fn assert_configuration_is_empty(
pub(crate) fn assert_configuration_is_empty(
config: &Arc<RefCell<Configuration>>,
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<Option<Value>> {
pub(crate) fn visit_ruleset(&mut self, ruleset: AstRuleSet) -> SassResult<Option<Value>> {
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<Option<Value>> {
pub(crate) fn visit_style(&mut self, style: AstStyle) -> SassResult<Option<Value>> {
if !self.style_rule_exists()
&& !self.flags.in_unknown_at_rule()
&& !self.flags.in_keyframes()

View File

@ -6,7 +6,7 @@ use std::fmt::{self, Display};
thread_local!(static STRINGS: RefCell<Rodeo<Spur>> = 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<T: AsRef<str>>(s: T) -> Self {

View File

@ -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: AsRef<Path>>(p: P, options: &Options) -> Result<String> {
/// }
/// ```
#[inline]
pub fn from_string(input: String, options: &Options) -> Result<String> {
from_string_with_file_name(input, "stdin", options)
pub fn from_string<S: Into<String>>(input: S, options: &Options) -> Result<String> {
from_string_with_file_name(input.into(), "stdin", options)
}
#[cfg(feature = "wasm-exports")]

View File

@ -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<InputSyntax>,
pub(crate) custom_fns: HashMap<String, Builtin>,
}
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<S: Into<String>>(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)
}

View File

@ -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

View File

@ -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 <html>), converted to an absolute length
/// (typically \<html\>), 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<Unit>,
pub(crate) denom: Vec<Unit>,
pub struct ComplexUnit {
pub numer: Vec<Unit>,
pub denom: Vec<Unit>,
}
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<Self>, denom: Vec<Self>) -> Self {
pub(crate) fn new(mut numer: Vec<Self>, denom: Vec<Self>) -> 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<Unit>, Vec<Unit>) {
pub(crate) fn numer_and_denom(self) -> (Vec<Unit>, Vec<Unit>) {
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;
}

View File

@ -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<Value>,
were_keywords_accessed: Arc<Cell<bool>>,
// todo: special wrapper around this field to avoid having to make it private?

View File

@ -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<CalculationArg>,
}

View File

@ -8,7 +8,7 @@ use crate::{
};
#[derive(Debug, Clone, Default)]
pub(crate) struct SassMap(Vec<(Spanned<Value>, Value)>);
pub struct SassMap(Vec<(Spanned<Value>, Value)>);
impl PartialEq for SassMap {
fn eq(&self, other: &Self) -> bool {

View File

@ -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,

View File

@ -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

View File

@ -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<AstFunctionDecl>,
pub struct UserDefinedFunction {
pub(crate) function: Arc<AstFunctionDecl>,
pub name: Identifier,
pub env: Environment,
pub(crate) env: Environment,
}
impl PartialEq for UserDefinedFunction {

View File

@ -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<Arc<(Self, Self)>>,
@ -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();