Skip to main content

stylex_css_parser/
lib.rs

1/*!
2# StyleX CSS Parser
3
4A high-performance CSS value parser for StyleX, written in Rust.
5
6This crate provides comprehensive CSS value parsing capabilities with excellent performance
7and type safety.
8
9## Features
10
11- **Token Parser Combinators**: Monadic parser combinators for building complex parsers
12- **CSS Type Parsers**: Support for all CSS value types (colors, lengths, angles, calc expressions, etc.)
13- **Property Parsers**: Specialized parsers for complex CSS properties (transform, box-shadow, border-radius)
14- **Media Query Support**: Complete media query parsing and transformation
15- **Type Safety**: Full Rust type safety with comprehensive error handling
16- **Performance**: High-performance parsing with minimal allocations
17
18## Usage Examples
19
20```rust,ignore
21use stylex_css_parser::{tokenParser, properties, last_media_query_wins_transform};
22use stylex_css_parser::css_types::{Color, Length, Calc};
23use swc_core::ecma::ast::{KeyValueProp, PropName, Expr, Lit};
24
25// Parse CSS values
26let color = Color::parse(); // CSS color parser
27let length = Length::parser(); // CSS length parser
28let calc = Calc::parse(); // CSS calc() expression parser
29
30// Property parsers
31let transform = properties::Transform::parser();
32let box_shadow = properties::BoxShadow::parser();
33let border_radius = properties::BorderRadiusShorthand::parser();
34
35// Media query transformation
36let queries = &[KeyValueProp{
37  key: PropName::Ident("key".into()),
38  value: Box::new(Expr::Lit(Lit::Str("value".into()))),
39}];
40let transformed = last_media_query_wins_transform(queries);
41```
42
43## Module Organization
44
45- [`tokenParser`] - Core parser combinator library
46- [`properties`] - Property-specific parsers
47- [`css_types`] - Individual CSS type parsers (color, length, angle, etc.)
48- [`at_queries`] - Media query and at-rule parsing
49- [`base_types`] - Base utility types (SubString, etc.)
50- [`token_types`] - Tokenization utilities
51
52*/
53
54pub mod at_queries;
55pub mod base_types;
56pub mod css_types;
57pub mod properties;
58pub mod token_parser;
59pub mod token_types;
60
61pub mod css_value;
62pub mod flex_parser;
63
64#[cfg(test)]
65pub mod tests;
66
67pub use at_queries::last_media_query_wins_transform;
68pub use token_parser as tokenParser;
69
70pub use css_value::CssValue;
71pub use flex_parser::{FlexCombinators, FlexParser};
72
73/// Main error type for CSS parsing operations
74#[derive(Debug, Clone, thiserror::Error)]
75pub enum CssParseError {
76  #[error("Parse error: {message}")]
77  ParseError { message: String },
78
79  #[error("Invalid CSS value: {value}")]
80  InvalidValue { value: String },
81
82  #[error("Unexpected end of input")]
83  UnexpectedEndOfInput,
84
85  #[error("Invalid token: {token}")]
86  InvalidToken { token: String },
87}
88
89/// Result type for CSS parsing operations
90pub type CssResult<T> = std::result::Result<T, CssParseError>;
91
92#[cfg(test)]
93mod lib_tests {
94  use super::*;
95
96  #[test]
97  fn test_api_exports_available() {
98    // Test tokenParser module access
99    let _token_parser = tokenParser::TokenParser::<i32>::never();
100
101    // Test properties module access
102    let _transform = properties::Transform::new(vec![]);
103    let _box_shadow = properties::BoxShadow::new(
104      css_types::Length::new(0.0, "px".to_string()),
105      css_types::Length::new(0.0, "px".to_string()),
106      css_types::Length::new(0.0, "px".to_string()),
107      css_types::Length::new(0.0, "px".to_string()),
108      css_types::Color::Named(css_types::NamedColor::new("black".to_string())),
109      false,
110    );
111    let _border_radius = properties::BorderRadiusIndividual::new(
112      css_types::LengthPercentage::Length(css_types::Length::new(0.0, "px".to_string())),
113      None,
114    );
115
116    // Test last_media_query_wins_transform function access
117    let styles = vec![];
118    let _transformed = last_media_query_wins_transform(&styles);
119
120    // Test css_types module access
121    let _color = css_types::Color::parse();
122    let _length = css_types::Length::parser();
123    let _calc = css_types::Calc::parse();
124  }
125
126  #[test]
127  fn test_standard_imports() {
128    use crate::css_types::{Calc, Color, Length};
129    use crate::{last_media_query_wins_transform, properties};
130
131    let _transform = properties::Transform::new(vec![]);
132    let _transform_fn = last_media_query_wins_transform;
133    let _color_parser = Color::parse();
134    let _length_parser = Length::parser();
135    let _calc_parser = Calc::parse();
136  }
137
138  #[test]
139  fn test_crate_level_types() {
140    // Test that crate-level types are working
141    let error = CssParseError::ParseError {
142      message: "test error".to_string(),
143    };
144
145    let result: CssResult<i32> = Err(error);
146    assert!(result.is_err());
147  }
148
149  #[test]
150  fn test_module_accessibility() {
151    // Test that all modules are accessible
152    let _substring = base_types::SubString::new("test");
153    let _token_list = token_types::TokenList::new("test");
154    let _token_parser = token_parser::TokenParser::<()>::never();
155    let _color = css_types::Color::parse();
156    let _transform = properties::Transform::new(vec![]);
157    let _media_query = at_queries::MediaQuery::parser()
158      .parse_to_end("@media screen")
159      .unwrap();
160  }
161
162  #[test]
163  fn test_error_types_comprehensive() {
164    // Test all error variants
165    let errors = vec![
166      CssParseError::ParseError {
167        message: "parse error".to_string(),
168      },
169      CssParseError::InvalidValue {
170        value: "invalid".to_string(),
171      },
172      CssParseError::UnexpectedEndOfInput,
173      CssParseError::InvalidToken {
174        token: "bad".to_string(),
175      },
176    ];
177
178    for error in errors {
179      // Test that errors can be cloned and debugged
180      let _cloned = error.clone();
181      let _debug_str = format!("{:?}", error);
182      let _display_str = format!("{}", error);
183    }
184  }
185
186  #[test]
187  fn test_readme_example_compatibility() {
188    // Test examples from the README/documentation work
189    use crate::css_types::{Calc, Color, Length};
190    use crate::{last_media_query_wins_transform, properties};
191
192    // Parse CSS values
193    let _color = Color::parse(); // CSS color parser
194    let _length = Length::parser(); // CSS length parser
195    let _calc = Calc::parse(); // CSS calc() expression parser
196
197    // Property parsers
198    let _transform = properties::Transform::parser();
199    let _box_shadow = properties::BoxShadow::parser();
200    let _border_radius = properties::BorderRadiusShorthand::parser();
201
202    // Media query transformation
203    let styles = vec![];
204    let _transformed = last_media_query_wins_transform(&styles);
205  }
206}