mirror of
https://github.com/danbulant/mangui
synced 2026-05-19 03:58:34 +00:00
start working on css generator
This commit is contained in:
parent
7bc7a6253e
commit
4f454684d3
4 changed files with 840 additions and 0 deletions
20
mangades-plain/Cargo.toml
Normal file
20
mangades-plain/Cargo.toml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
[package]
|
||||
name = "mangades-plain"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
mangui = { path = "../ui" }
|
||||
uno-gen = { path = "../uno-gen" }
|
||||
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
serde_json = "1.0.114"
|
||||
tokio = { version = "1.36.0", features = ["full"] }
|
||||
futures = "0.3.30"
|
||||
reqwest = "0.11.24"
|
||||
image = "0.24.9"
|
||||
bytes = "*"
|
||||
lazy_static = "1.4.0"
|
||||
84
mangades-plain/src/main.rs
Normal file
84
mangades-plain/src/main.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
use std::sync::{Arc, mpsc, Mutex};
|
||||
use mangui::nodes::layout::Layout;
|
||||
use mangui::{MainEntry, SharedNode};
|
||||
use mangui::femtovg::Paint;
|
||||
use mangui::nodes::text::Text;
|
||||
use mangui::nodes::{Style, TaffyStyle, ToShared};
|
||||
use mangui::taffy::{AlignItems, FlexDirection, JustifyContent, LengthPercentage, Rect};
|
||||
use uno_gen::uno;
|
||||
use crate::anilist::load_demo_async;
|
||||
use crate::tokens::TEXT_LARGE;
|
||||
use crate::utils::{append, detach};
|
||||
|
||||
mod anilist;
|
||||
mod utils;
|
||||
mod tokens;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let tx = Arc::new(tx);
|
||||
let root = Layout::default();
|
||||
let groot: SharedNode = Arc::new(Mutex::new(root));
|
||||
|
||||
let loading_container = Layout::default()
|
||||
.style(Style {
|
||||
layout: TaffyStyle {
|
||||
flex_grow: 1.,
|
||||
justify_content: JustifyContent::Center.into(),
|
||||
align_items: AlignItems::Center.into(),
|
||||
..Default::default()
|
||||
},
|
||||
background: Some(Paint::color(*tokens::BACKGROUND)),
|
||||
..Default::default()
|
||||
})
|
||||
.to_shared();
|
||||
let loading_text = Text::new("Loading...".to_owned(), TEXT_LARGE)
|
||||
.style(Style {
|
||||
text_fill: Some(Paint::color(*tokens::WHITE)),
|
||||
..Default::default()
|
||||
})
|
||||
.to_shared();
|
||||
append(&groot, &{ loading_container.clone() });
|
||||
append(&loading_container, &{ loading_text.clone() });
|
||||
|
||||
let groot_clone = groot.clone();
|
||||
tokio::spawn(async move {
|
||||
let data = load_demo_async().await;
|
||||
|
||||
let mainview_container = Layout::default()
|
||||
.style(Style {
|
||||
layout: TaffyStyle {
|
||||
flex_grow: 1.,
|
||||
flex_direction: FlexDirection::Column,
|
||||
..Default::default()
|
||||
},
|
||||
background: Some(Paint::color(*tokens::BACKGROUND)),
|
||||
..Default::default()
|
||||
})
|
||||
.to_shared();
|
||||
let i = 2;
|
||||
uno!(gap-1 flex p-5px mt-1 mb-2 ml-[i] overflow-hidden);
|
||||
let title = Text::new("Mangades".to_owned(), TEXT_LARGE)
|
||||
.style(Style {
|
||||
layout: TaffyStyle {
|
||||
padding: Rect { left: LengthPercentage::Length(10.), right: LengthPercentage::Length(10.), top: LengthPercentage::Length(10.), bottom: LengthPercentage::Length(10.) },
|
||||
..Default::default()
|
||||
},
|
||||
text_fill: Some(Paint::color(*tokens::WHITE)),
|
||||
..Default::default()
|
||||
})
|
||||
.to_shared();
|
||||
|
||||
append(&mainview_container, &title);
|
||||
detach(&loading_container);
|
||||
append(&groot_clone, &mainview_container);
|
||||
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
|
||||
mangui::run_event_loop(MainEntry {
|
||||
root: groot.clone(),
|
||||
render: rx
|
||||
});
|
||||
}
|
||||
14
uno-gen/Cargo.toml
Normal file
14
uno-gen/Cargo.toml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "uno-gen"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
quote = "1.0"
|
||||
proc-macro2 = "1.0.78"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
722
uno-gen/src/lib.rs
Normal file
722
uno-gen/src/lib.rs
Normal file
|
|
@ -0,0 +1,722 @@
|
|||
use proc_macro2::{Span, TokenStream, TokenTree};
|
||||
use quote::{IdentFragment, quote_spanned};
|
||||
use quote::spanned::Spanned;
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
enum UserSettable<T> {
|
||||
Value(T),
|
||||
Arbitrary(TokenStream),
|
||||
#[default]
|
||||
None
|
||||
}
|
||||
|
||||
impl<T> UserSettable<T> {
|
||||
fn require_value(&mut self) -> Result<&mut T, RuleParseError> {
|
||||
match self {
|
||||
UserSettable::Value(value) => Ok(value),
|
||||
UserSettable::Arbitrary(stream) => {
|
||||
Err(RuleParseError {
|
||||
span: stream.__span(),
|
||||
message: "Expected a value".to_owned()
|
||||
})
|
||||
},
|
||||
UserSettable::None => {
|
||||
Err(RuleParseError {
|
||||
span: Span::call_site(),
|
||||
message: "Expected a value".to_owned()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default> UserSettable<T> {
|
||||
fn require_non_arbitrary(&mut self) -> Result<&mut T, RuleParseError> {
|
||||
match self {
|
||||
UserSettable::Value(value) => Ok(value),
|
||||
UserSettable::None => {
|
||||
*self = UserSettable::Value(Default::default());
|
||||
Ok(self.require_value()?)
|
||||
},
|
||||
UserSettable::Arbitrary(stream) => {
|
||||
Err(RuleParseError {
|
||||
span: stream.__span(),
|
||||
message: "Expected a value".to_owned()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
struct Point<T> {
|
||||
x: UserSettable<T>,
|
||||
y: UserSettable<T>
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
struct Size<T> {
|
||||
width: UserSettable<T>,
|
||||
height: UserSettable<T>
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
struct Color {
|
||||
r: f32,
|
||||
g: f32,
|
||||
b: f32,
|
||||
a: f32
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum Paint {
|
||||
Color(Color)
|
||||
}
|
||||
|
||||
impl Default for Paint {
|
||||
fn default() -> Self {
|
||||
Paint::Color(Color {
|
||||
r: 0.,
|
||||
g: 0.,
|
||||
b: 0.,
|
||||
a: 0.
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
enum Cursor {
|
||||
#[default]
|
||||
Default
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
struct Transform {
|
||||
pub position: UserSettable<Point<f32>>,
|
||||
pub scale: UserSettable<Size<f32>>,
|
||||
pub rotation: UserSettable<f32>
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
struct Style {
|
||||
pub layout: UserSettable<TaffyStyle>,
|
||||
pub cursor: UserSettable<Cursor>,
|
||||
pub background: UserSettable<Paint>,
|
||||
pub text_fill: UserSettable<Paint>,
|
||||
pub font_size: UserSettable<f32>,
|
||||
pub line_height: UserSettable<f32>,
|
||||
pub border_radius: UserSettable<f32>,
|
||||
pub transform: UserSettable<Transform>
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
struct Rect<T> {
|
||||
pub left: UserSettable<T>,
|
||||
pub right: UserSettable<T>,
|
||||
pub top: UserSettable<T>,
|
||||
pub bottom: UserSettable<T>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
enum Position {
|
||||
#[default]
|
||||
Relative,
|
||||
Absolute,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
enum Display {
|
||||
Block,
|
||||
#[default]
|
||||
Flex,
|
||||
Grid,
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
enum Overflow {
|
||||
#[default]
|
||||
Visible,
|
||||
Clip,
|
||||
Hidden,
|
||||
Scroll,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
enum LengthPercentageAuto {
|
||||
Length(f32),
|
||||
Percent(f32),
|
||||
#[default]
|
||||
Auto,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
enum Dimension {
|
||||
Length(f32),
|
||||
Percent(f32),
|
||||
#[default]
|
||||
Auto,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum LengthPercentage {
|
||||
Length(f32),
|
||||
Percent(f32),
|
||||
}
|
||||
|
||||
impl Default for LengthPercentage {
|
||||
fn default() -> Self {
|
||||
LengthPercentage::Length(0.)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum AlignItems {
|
||||
Start,
|
||||
End,
|
||||
FlexStart,
|
||||
FlexEnd,
|
||||
Center,
|
||||
Baseline,
|
||||
Stretch,
|
||||
}
|
||||
|
||||
type AlignSelf = AlignItems;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum AlignContent {
|
||||
Start,
|
||||
End,
|
||||
FlexStart,
|
||||
FlexEnd,
|
||||
Center,
|
||||
Stretch,
|
||||
SpaceBetween,
|
||||
SpaceEvenly,
|
||||
SpaceAround,
|
||||
}
|
||||
type JustifyContent = AlignContent;
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
enum FlexDirection {
|
||||
#[default]
|
||||
Row,
|
||||
Column,
|
||||
RowReverse,
|
||||
ColumnReverse,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
enum FlexWrap {
|
||||
#[default]
|
||||
NoWrap,
|
||||
Wrap,
|
||||
WrapReverse,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
struct Line<T> {
|
||||
pub start: T,
|
||||
pub end: T,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct MinMax<Min, Max> {
|
||||
pub min: Min,
|
||||
pub max: Max,
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
enum MinTrackSizingFunction {
|
||||
Fixed(LengthPercentage),
|
||||
MinContent,
|
||||
MaxContent,
|
||||
Auto,
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
enum MaxTrackSizingFunction {
|
||||
Fixed(LengthPercentage),
|
||||
MinContent,
|
||||
MaxContent,
|
||||
FitContent(LengthPercentage),
|
||||
Auto,
|
||||
Fraction(f32),
|
||||
}
|
||||
type NonRepeatedTrackSizingFunction = MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>;
|
||||
#[derive(Clone, Debug)]
|
||||
enum GridTrackRepetition {
|
||||
AutoFill,
|
||||
AutoFit,
|
||||
Count(u16),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum TrackSizingFunction {
|
||||
Single(NonRepeatedTrackSizingFunction),
|
||||
Repeat(GridTrackRepetition, Vec<NonRepeatedTrackSizingFunction>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
enum GridAutoFlow {
|
||||
#[default]
|
||||
Row,
|
||||
Column,
|
||||
RowDense,
|
||||
ColumnDense,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct GridLine(i16);
|
||||
#[derive(Clone, Default, Debug)]
|
||||
enum GridPlacement {
|
||||
#[default]
|
||||
Auto,
|
||||
Line(GridLine),
|
||||
Span(u16),
|
||||
}
|
||||
#[derive(Clone, Default, Debug)]
|
||||
struct TaffyStyle {
|
||||
pub display: UserSettable<Display>,
|
||||
pub overflow: UserSettable<Point<Overflow>>,
|
||||
pub scrollbar_width: UserSettable<f32>,
|
||||
pub position: UserSettable<Position>,
|
||||
pub inset: UserSettable<Rect<LengthPercentageAuto>>,
|
||||
pub size: UserSettable<Size<Dimension>>,
|
||||
pub min_size: UserSettable<Size<Dimension>>,
|
||||
pub max_size: UserSettable<Size<Dimension>>,
|
||||
pub aspect_ratio: UserSettable<f32>,
|
||||
pub margin: UserSettable<Rect<LengthPercentageAuto>>,
|
||||
pub padding: UserSettable<Rect<LengthPercentage>>,
|
||||
pub border: UserSettable<Rect<LengthPercentage>>,
|
||||
pub align_items: UserSettable<AlignItems>,
|
||||
pub align_self: UserSettable<AlignSelf>,
|
||||
pub justify_items: UserSettable<AlignItems>,
|
||||
pub justify_self: UserSettable<AlignSelf>,
|
||||
pub align_content: UserSettable<AlignContent>,
|
||||
pub justify_content: UserSettable<JustifyContent>,
|
||||
pub gap: UserSettable<Size<LengthPercentage>>,
|
||||
pub flex_direction: UserSettable<FlexDirection>,
|
||||
pub flex_wrap: UserSettable<FlexWrap>,
|
||||
pub flex_basis: UserSettable<Dimension>,
|
||||
pub flex_grow: UserSettable<f32>,
|
||||
pub flex_shrink: UserSettable<f32>,
|
||||
pub grid_template_rows: UserSettable<Vec<TrackSizingFunction>>,
|
||||
pub grid_template_columns: UserSettable<Vec<TrackSizingFunction>>,
|
||||
pub grid_auto_rows: UserSettable<Vec<NonRepeatedTrackSizingFunction>>,
|
||||
pub grid_auto_columns: UserSettable<Vec<NonRepeatedTrackSizingFunction>>,
|
||||
pub grid_auto_flow: UserSettable<GridAutoFlow>,
|
||||
pub grid_row: UserSettable<Line<GridPlacement>>,
|
||||
pub grid_column: UserSettable<Line<GridPlacement>>,
|
||||
}
|
||||
|
||||
trait ValueToUserSettable<T> {
|
||||
fn to_user_settable(self, span: Span, inverse: bool) -> Result<UserSettable<T>, RuleParseError>;
|
||||
}
|
||||
|
||||
impl ValueToUserSettable<f32> for TokenTree {
|
||||
fn to_user_settable(self, _span: Span, inverse: bool) -> Result<UserSettable<f32>, RuleParseError> {
|
||||
match self {
|
||||
TokenTree::Literal(lit) => {
|
||||
let text = lit.to_string();
|
||||
let mut lit = text.parse::<f32>().map_err(|_| RuleParseError {
|
||||
span: lit.span(),
|
||||
message: "Expected a float".to_owned()
|
||||
})?;
|
||||
if inverse {
|
||||
lit = -lit;
|
||||
}
|
||||
Ok(UserSettable::Value(lit))
|
||||
},
|
||||
TokenTree::Group(group) => {
|
||||
if inverse {
|
||||
return Err(RuleParseError {
|
||||
span: group.span(),
|
||||
message: "Groups are not invertible".to_owned()
|
||||
})
|
||||
}
|
||||
Ok(UserSettable::Arbitrary(group.stream()))
|
||||
},
|
||||
_ => {
|
||||
Err(RuleParseError {
|
||||
span: self.span(),
|
||||
message: "Expected a literal or a group".to_owned()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueToUserSettable<LengthPercentage> for TokenTree {
|
||||
fn to_user_settable(self, span: Span, inverse: bool) -> Result<UserSettable<LengthPercentage>, RuleParseError> {
|
||||
match self.to_user_settable(span, inverse)? {
|
||||
UserSettable::Value(value) => Ok(UserSettable::Value(LengthPercentage::Length(value))),
|
||||
UserSettable::Arbitrary(stream) => {
|
||||
Ok(UserSettable::Arbitrary(stream))
|
||||
},
|
||||
UserSettable::None => {
|
||||
Err(RuleParseError {
|
||||
span,
|
||||
message: "Expected a value".to_owned()
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueToUserSettable<LengthPercentageAuto> for TokenTree {
|
||||
fn to_user_settable(self, span: Span, inverse: bool) -> Result<UserSettable<LengthPercentageAuto>, RuleParseError> {
|
||||
match &self {
|
||||
TokenTree::Literal(lit) if lit.to_string() == "auto" => {
|
||||
if inverse {
|
||||
return Err(RuleParseError {
|
||||
span: lit.span(),
|
||||
message: "auto is not invertible".to_owned()
|
||||
})
|
||||
}
|
||||
return Ok(UserSettable::Value(LengthPercentageAuto::Auto))
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
match self.to_user_settable(span, inverse)? {
|
||||
UserSettable::Value(value) => Ok(UserSettable::Value(LengthPercentageAuto::Length(value))),
|
||||
UserSettable::Arbitrary(stream) => {
|
||||
Ok(UserSettable::Arbitrary(stream))
|
||||
},
|
||||
UserSettable::None => {
|
||||
Err(RuleParseError {
|
||||
span,
|
||||
message: "Expected a value".to_owned()
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueToUserSettable<Overflow> for TokenTree {
|
||||
fn to_user_settable(self, span: Span, inverse: bool) -> Result<UserSettable<Overflow>, RuleParseError> {
|
||||
if inverse {
|
||||
return Err(RuleParseError {
|
||||
span,
|
||||
message: "overflow is not invertible".to_owned()
|
||||
})
|
||||
}
|
||||
match self {
|
||||
TokenTree::Ident(ident) => {
|
||||
match ident.to_string().as_str() {
|
||||
"visible" => Ok(UserSettable::Value(Overflow::Visible)),
|
||||
"hidden" => Ok(UserSettable::Value(Overflow::Hidden)),
|
||||
"scroll" => Ok(UserSettable::Value(Overflow::Scroll)),
|
||||
"clip" => Ok(UserSettable::Value(Overflow::Clip)),
|
||||
_ => {
|
||||
Err(RuleParseError {
|
||||
span: ident.span(),
|
||||
message: "Expected a valid value (one of visible, hidden, scroll or clip)".to_owned()
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
Err(RuleParseError {
|
||||
span: self.span(),
|
||||
message: "Expected a valid value (one of visible, hidden, scroll or clip)".to_owned()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueToUserSettable<TaffyStyle> for TokenTree {
|
||||
fn to_user_settable(self, _span: Span, inverse: bool) -> Result<UserSettable<TaffyStyle>, RuleParseError> {
|
||||
match self {
|
||||
TokenTree::Group(group) => {
|
||||
if inverse {
|
||||
return Err(RuleParseError {
|
||||
span: group.span(),
|
||||
message: "Groups are not invertible".to_owned()
|
||||
})
|
||||
}
|
||||
Ok(UserSettable::Arbitrary(group.stream()))
|
||||
},
|
||||
_ => {
|
||||
Err(RuleParseError {
|
||||
span: self.span(),
|
||||
message: "Expected a group - layout doesn't support literal values".to_owned()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ValueToUserSettable<Y>, Y> ValueToUserSettable<Y> for Option<T> {
|
||||
fn to_user_settable(self, span: Span, inverse: bool) -> Result<UserSettable<Y>, RuleParseError> {
|
||||
match self {
|
||||
Some(value) => value.to_user_settable(span, inverse),
|
||||
None => Err(RuleParseError {
|
||||
span,
|
||||
message: "Expected a value".to_owned()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Rule {
|
||||
prefix: Option<String>,
|
||||
inverse: bool,
|
||||
prefix_span: Span,
|
||||
name: String,
|
||||
name_span: Span,
|
||||
value: Option<TokenTree>
|
||||
}
|
||||
|
||||
struct RuleParseError {
|
||||
span: Span,
|
||||
message: String
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn uno(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let item = TokenStream::from(item);
|
||||
let mut output = TokenStream::new();
|
||||
dbg!(&item);
|
||||
let rules = parse_rules(item);
|
||||
|
||||
let rules = match rules {
|
||||
Ok(rules) => rules,
|
||||
Err(err) => {
|
||||
let RuleParseError { span, message } = err;
|
||||
return quote_spanned!(span => {
|
||||
compile_error!(#message);
|
||||
}).into();
|
||||
}
|
||||
};
|
||||
|
||||
let style = match process_rules(rules) {
|
||||
Ok(style) => style,
|
||||
Err(err) => {
|
||||
let RuleParseError { span, message } = err;
|
||||
return quote_spanned!(span => {
|
||||
compile_error!(#message);
|
||||
}).into();
|
||||
}
|
||||
};
|
||||
|
||||
dbg!(style);
|
||||
|
||||
output.into()
|
||||
}
|
||||
|
||||
fn process_rules(rules: Vec<Rule>) -> Result<Style, RuleParseError> {
|
||||
let mut style = Style::default();
|
||||
dbg!(&rules);
|
||||
|
||||
for rule in rules {
|
||||
let Rule {
|
||||
prefix,
|
||||
prefix_span,
|
||||
name,
|
||||
name_span,
|
||||
value,
|
||||
inverse
|
||||
} = rule;
|
||||
|
||||
// todo: handle passing single objects to certain attributes (p, m, gap, overflow, etc)
|
||||
match name.as_str() {
|
||||
"flex" => {
|
||||
// todo: support certain identifiers like col, row, etc
|
||||
if let Some(value) = value {
|
||||
let value = value.to_user_settable(name_span, inverse)?;
|
||||
style.layout.require_non_arbitrary()?.flex_grow = value.clone();
|
||||
style.layout.require_non_arbitrary()?.flex_shrink = value;
|
||||
} else {
|
||||
style.layout.require_non_arbitrary()?.display = UserSettable::Value(Display::Flex);
|
||||
}
|
||||
},
|
||||
"p" => {
|
||||
let value = value.to_user_settable(name_span, inverse)?;
|
||||
style.layout.require_non_arbitrary()?.padding.require_non_arbitrary()?.top = value.clone();
|
||||
style.layout.require_non_arbitrary()?.padding.require_non_arbitrary()?.right = value.clone();
|
||||
style.layout.require_non_arbitrary()?.padding.require_non_arbitrary()?.left = value.clone();
|
||||
style.layout.require_non_arbitrary()?.padding.require_non_arbitrary()?.bottom = value;
|
||||
},
|
||||
"m" => {
|
||||
let value = value.to_user_settable(name_span, inverse)?;
|
||||
style.layout.require_non_arbitrary()?.margin.require_non_arbitrary()?.top = value.clone();
|
||||
style.layout.require_non_arbitrary()?.margin.require_non_arbitrary()?.right = value.clone();
|
||||
style.layout.require_non_arbitrary()?.margin.require_non_arbitrary()?.left = value.clone();
|
||||
style.layout.require_non_arbitrary()?.margin.require_non_arbitrary()?.bottom = value;
|
||||
},
|
||||
"pt" | "pr" | "pl" | "pb" => {
|
||||
let value = value.to_user_settable(name_span, inverse)?;
|
||||
match name.as_str() {
|
||||
"pt" => style.layout.require_non_arbitrary()?.padding.require_non_arbitrary()?.top = value,
|
||||
"pr" => style.layout.require_non_arbitrary()?.padding.require_non_arbitrary()?.right = value,
|
||||
"pl" => style.layout.require_non_arbitrary()?.padding.require_non_arbitrary()?.left = value,
|
||||
"pb" => style.layout.require_non_arbitrary()?.padding.require_non_arbitrary()?.bottom = value,
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
"mt" | "ml" | "mr" | "mb" => {
|
||||
let value = value.to_user_settable(name_span, inverse)?;
|
||||
match name.as_str() {
|
||||
"mt" => style.layout.require_non_arbitrary()?.margin.require_non_arbitrary()?.top = value,
|
||||
"mr" => style.layout.require_non_arbitrary()?.margin.require_non_arbitrary()?.right = value,
|
||||
"ml" => style.layout.require_non_arbitrary()?.margin.require_non_arbitrary()?.left = value,
|
||||
"mb" => style.layout.require_non_arbitrary()?.margin.require_non_arbitrary()?.bottom = value,
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
"gap" => {
|
||||
let value = value.to_user_settable(name_span, inverse)?;
|
||||
style.layout.require_non_arbitrary()?.gap.require_non_arbitrary()?.width = value.clone();
|
||||
style.layout.require_non_arbitrary()?.gap.require_non_arbitrary()?.height = value;
|
||||
},
|
||||
"overflow" => {
|
||||
let value = value.to_user_settable(name_span, inverse)?;
|
||||
style.layout.require_non_arbitrary()?.overflow.require_non_arbitrary()?.x = value.clone();
|
||||
style.layout.require_non_arbitrary()?.overflow.require_non_arbitrary()?.y = value;
|
||||
},
|
||||
"overflow_x" => {
|
||||
let value = value.to_user_settable(name_span, inverse)?;
|
||||
style.layout.require_non_arbitrary()?.overflow.require_non_arbitrary()?.x = value;
|
||||
},
|
||||
"overflow_y" => {
|
||||
let value = value.to_user_settable(name_span, inverse)?;
|
||||
style.layout.require_non_arbitrary()?.overflow.require_non_arbitrary()?.y = value;
|
||||
},
|
||||
"layout" => {
|
||||
if let Some(value) = value {
|
||||
let value = value.to_user_settable(name_span, inverse)?;
|
||||
style.layout = value;
|
||||
} else {
|
||||
return Err(RuleParseError {
|
||||
span: name_span,
|
||||
message: "Expected a value as a reference to TaffyStyle object".to_owned()
|
||||
});
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return Err(RuleParseError {
|
||||
span: name_span,
|
||||
message: "Unknown rule".to_owned()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(style)
|
||||
}
|
||||
|
||||
/// Parse the rules from the input
|
||||
///
|
||||
/// Example rules:
|
||||
///
|
||||
/// flex p-5px mt-1 mb-2 ml-[i] hover:test overflow-hidden flex-col mb-1/2
|
||||
fn parse_rules(item: TokenStream) -> Result<Vec<Rule>, RuleParseError> {
|
||||
let mut rules = vec![];
|
||||
let mut prefix = None;
|
||||
let mut prefix_span = Span::call_site();
|
||||
let mut name = String::new();
|
||||
let mut name_span = Span::call_site();
|
||||
let mut value = None;
|
||||
let mut should_parse_value = false;
|
||||
let mut inverse = false;
|
||||
|
||||
for token in item {
|
||||
match token {
|
||||
TokenTree::Ident(ident) => {
|
||||
if should_parse_value {
|
||||
value = Some(TokenTree::Ident(ident));
|
||||
should_parse_value = false;
|
||||
|
||||
rules.push(Rule {
|
||||
prefix: prefix.take(),
|
||||
prefix_span,
|
||||
name: std::mem::take(&mut name),
|
||||
name_span,
|
||||
value: value.take(),
|
||||
inverse
|
||||
});
|
||||
} else if prefix.is_some() && name.is_empty() {
|
||||
name = ident.to_string();
|
||||
name_span = ident.span();
|
||||
} else {
|
||||
if !name.is_empty() {
|
||||
rules.push(Rule {
|
||||
prefix: prefix.take(),
|
||||
prefix_span,
|
||||
name,
|
||||
name_span,
|
||||
value: None,
|
||||
inverse
|
||||
});
|
||||
}
|
||||
name = ident.to_string();
|
||||
name_span = ident.span();
|
||||
}
|
||||
inverse = false;
|
||||
prefix_span = Span::call_site();
|
||||
},
|
||||
TokenTree::Punct(punct) => {
|
||||
if punct.as_char() == '-' {
|
||||
if name.is_empty() {
|
||||
inverse = true;
|
||||
} else {
|
||||
should_parse_value = true;
|
||||
}
|
||||
} else if punct.as_char() == ':' {
|
||||
prefix = Some(name);
|
||||
prefix_span = name_span;
|
||||
name = String::new();
|
||||
} else if punct.as_char() == '/' {
|
||||
return Err(RuleParseError {
|
||||
span: punct.span(),
|
||||
message: "Fractions are not supported yet".to_owned()
|
||||
});
|
||||
} else {
|
||||
return Err(RuleParseError {
|
||||
span: punct.span(),
|
||||
message: "Unexpected punctuation".to_owned()
|
||||
});
|
||||
}
|
||||
},
|
||||
TokenTree::Literal(_) | TokenTree::Group(_) => {
|
||||
if !should_parse_value {
|
||||
// error
|
||||
return Err(RuleParseError {
|
||||
span: token.span(),
|
||||
message: "Unexpected value, expected literal (rule start)".to_owned()
|
||||
});
|
||||
}
|
||||
value = Some(token);
|
||||
|
||||
rules.push(Rule {
|
||||
prefix: prefix.take(),
|
||||
prefix_span,
|
||||
name: std::mem::take(&mut name),
|
||||
name_span,
|
||||
value: value.take(),
|
||||
inverse
|
||||
});
|
||||
should_parse_value = false;
|
||||
inverse = false;
|
||||
prefix_span = Span::call_site();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if !name.is_empty() {
|
||||
rules.push(Rule {
|
||||
prefix: prefix.take(),
|
||||
prefix_span,
|
||||
name,
|
||||
name_span,
|
||||
value: None,
|
||||
inverse
|
||||
});
|
||||
}
|
||||
|
||||
Ok(rules)
|
||||
}
|
||||
Loading…
Reference in a new issue