expose more internals
This commit is contained in:
parent
7d19140b4d
commit
b13fcc3f08
@ -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 = []
|
5
crates/compiler/README.md
Normal file
5
crates/compiler/README.md
Normal 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.
|
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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};
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
|
@ -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 {
|
||||
|
@ -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")]
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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?
|
||||
|
@ -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>,
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user