1use crate::{bumpalo, Align, Background, Color, Length};
3
4use std::collections::BTreeMap;
5
6#[derive(Debug)]
8pub enum Rule {
9 Column,
11
12 Row,
14
15 Padding(u16),
17
18 Spacing(u16),
20}
21
22impl Rule {
23 pub fn class<'a>(&self) -> String {
25 match self {
26 Rule::Column => String::from("c"),
27 Rule::Row => String::from("r"),
28 Rule::Padding(padding) => format!("p-{}", padding),
29 Rule::Spacing(spacing) => format!("s-{}", spacing),
30 }
31 }
32
33 pub fn declaration<'a>(&self, bump: &'a bumpalo::Bump) -> &'a str {
35 let class = self.class();
36
37 match self {
38 Rule::Column => {
39 let body = "{ display: flex; flex-direction: column; }";
40
41 bumpalo::format!(in bump, ".{} {}", class, body).into_bump_str()
42 }
43 Rule::Row => {
44 let body = "{ display: flex; flex-direction: row; }";
45
46 bumpalo::format!(in bump, ".{} {}", class, body).into_bump_str()
47 }
48 Rule::Padding(padding) => bumpalo::format!(
49 in bump,
50 ".{} {{ box-sizing: border-box; padding: {}px }}",
51 class,
52 padding
53 )
54 .into_bump_str(),
55 Rule::Spacing(spacing) => bumpalo::format!(
56 in bump,
57 ".c.{} > * {{ margin-bottom: {}px }} \
58 .r.{} > * {{ margin-right: {}px }} \
59 .c.{} > *:last-child {{ margin-bottom: 0 }} \
60 .r.{} > *:last-child {{ margin-right: 0 }}",
61 class,
62 spacing,
63 class,
64 spacing,
65 class,
66 class
67 )
68 .into_bump_str(),
69 }
70 }
71}
72
73#[derive(Debug)]
75pub struct Css<'a> {
76 rules: BTreeMap<String, &'a str>,
77}
78
79impl<'a> Css<'a> {
80 pub fn new() -> Self {
82 Css {
83 rules: BTreeMap::new(),
84 }
85 }
86
87 pub fn insert(&mut self, bump: &'a bumpalo::Bump, rule: Rule) -> String {
92 let class = rule.class();
93
94 if !self.rules.contains_key(&class) {
95 let _ = self.rules.insert(class.clone(), rule.declaration(bump));
96 }
97
98 class
99 }
100
101 pub fn node(self, bump: &'a bumpalo::Bump) -> dodrio::Node<'a> {
103 use dodrio::builder::*;
104
105 let mut declarations = bumpalo::collections::Vec::new_in(bump);
106
107 declarations.push(text("html { height: 100% }"));
108 declarations.push(text(
109 "body { height: 100%; margin: 0; padding: 0; font-family: sans-serif }",
110 ));
111 declarations.push(text("* { margin: 0; padding: 0 }"));
112 declarations.push(text(
113 "button { border: none; cursor: pointer; outline: none }",
114 ));
115
116 for declaration in self.rules.values() {
117 declarations.push(text(*declaration));
118 }
119
120 style(bump).children(declarations).finish()
121 }
122}
123
124pub fn length(length: Length) -> String {
126 match length {
127 Length::Shrink => String::from("auto"),
128 Length::Units(px) => format!("{}px", px),
129 Length::Fill | Length::FillPortion(_) => String::from("100%"),
130 }
131}
132
133pub fn max_length(units: u32) -> String {
135 use std::u32;
136
137 if units == u32::MAX {
138 String::from("initial")
139 } else {
140 format!("{}px", units)
141 }
142}
143
144pub fn min_length(units: u32) -> String {
146 if units == 0 {
147 String::from("initial")
148 } else {
149 format!("{}px", units)
150 }
151}
152
153pub fn color(Color { r, g, b, a }: Color) -> String {
155 format!("rgba({}, {}, {}, {})", 255.0 * r, 255.0 * g, 255.0 * b, a)
156}
157
158pub fn background(background: Background) -> String {
160 match background {
161 Background::Color(c) => color(c),
162 }
163}
164
165pub fn align(align: Align) -> &'static str {
167 match align {
168 Align::Start => "flex-start",
169 Align::Center => "center",
170 Align::End => "flex-end",
171 }
172}