summaryrefslogtreecommitdiff
path: root/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs200
1 files changed, 175 insertions, 25 deletions
diff --git a/src/lib.rs b/src/lib.rs
index dec06a5..e07f85b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -6,6 +6,8 @@ use std::time::SystemTime;
6mod lexer; 6mod lexer;
7mod parser; 7mod parser;
8 8
9pub use parser::ParseError;
10
9#[derive(Debug,Copy,Clone)] 11#[derive(Debug,Copy,Clone)]
10pub struct Position { 12pub struct Position {
11 pub line: u32, 13 pub line: u32,
@@ -20,13 +22,14 @@ impl Position {
20 } 22 }
21} 23}
22 24
25#[derive(Debug)]
23pub struct Crimtag { 26pub struct Crimtag {
24 fragments: HashMap<String, Fragment>, 27 views: HashMap<String, View>,
25 sources: Vec<FragmentSource>, 28 sources: Vec<ViewSource>,
26} 29}
27 30
28#[derive(PartialEq,Eq,Debug,Clone)] 31#[derive(PartialEq,Eq,Debug,Clone)]
29enum FragmentSource { 32enum ViewSource {
30 File { 33 File {
31 path: PathBuf, 34 path: PathBuf,
32 loaded: SystemTime, 35 loaded: SystemTime,
@@ -37,12 +40,29 @@ enum FragmentSource {
37 }, 40 },
38} 41}
39 42
40struct Fragment { 43#[derive(Debug)]
41 name: String, 44struct View {
42 source: usize, 45 source: usize,
43 sections: HashMap<String, Output>, 46 outputs: Vec<Output>,
47// theme: Option<String>
48 layout: Option<String>,
44} 49}
45 50
51impl View {
52 fn process(&self, input: &Value) -> Result<Value, ParseError> {
53 let mut out_vars = HashMap::new();
54 for output in &self.outputs {
55 if let Token::Output(_,_,tokens) = &output.ast_root {
56 let mut buf = String::new();
57 exec( input, tokens, &mut buf )?;
58 out_vars.insert( output.name.clone(), Value::String(buf) );
59 }
60 }
61 Ok(Value::Dictionary(out_vars))
62 }
63}
64
65#[derive(Debug)]
46struct Output { 66struct Output {
47 name: String, 67 name: String,
48 ast_root: Token, 68 ast_root: Token,
@@ -53,7 +73,7 @@ type Properties = HashMap<String, String>;
53#[derive(Debug)] 73#[derive(Debug)]
54enum Token { 74enum Token {
55 Root(Vec<Token>), 75 Root(Vec<Token>),
56 Fragment(String,Properties,Vec<Token>), 76 View(String,Properties,Vec<Token>),
57 Output(String,Properties,Vec<Token>), 77 Output(String,Properties,Vec<Token>),
58 Show(String,Properties), 78 Show(String,Properties),
59 Text(String), 79 Text(String),
@@ -67,18 +87,24 @@ enum Token {
67 },*/ 87 },*/
68} 88}
69 89
70struct State { 90#[derive(Debug)]
91pub enum Value {
92 Dictionary(HashMap<String,Value>),
93 List(Vec<Value>),
94 String(String),
95 Int(i64),
96 Float(f64),
71} 97}
72 98
73impl Crimtag { 99impl Crimtag {
74 pub fn new() -> Self { 100 pub fn new() -> Self {
75 Self { 101 Self {
76 fragments: HashMap::new(), 102 views: HashMap::new(),
77 sources: vec![FragmentSource::Static], 103 sources: vec![ViewSource::Static],
78 } 104 }
79 } 105 }
80 106
81 fn id_source(&mut self, src: &FragmentSource ) -> usize { 107 fn id_source(&mut self, src: &ViewSource ) -> usize {
82 for idx in 0..self.sources.len() { 108 for idx in 0..self.sources.len() {
83 if self.sources[idx] == *src { 109 if self.sources[idx] == *src {
84 return idx; 110 return idx;
@@ -104,23 +130,133 @@ impl Crimtag {
104 SystemTime::now() 130 SystemTime::now()
105 }; 131 };
106 132
107 let mut p = parser::Parser::new(); 133 let p = parser::Parser::new();
108 p.parse( &String::from_utf8( std::fs::read( path )? )? ); 134 self.register_views(
109 135 p.parse( &String::from_utf8( std::fs::read( path )? )? )?,
136 ViewSource::File {
137 path: path.to_path_buf(),
138 loaded: time,
139 })?;
110 Ok(()) 140 Ok(())
111 } 141 }
112 142
113 pub fn load_static(&mut self, data: &str) -> Result<(),Box::<dyn Error>> { 143 pub fn load_static(&mut self, data: &str) -> Result<(),ParseError> {
114 let mut p = parser::Parser::new(); 144 let p = parser::Parser::new();
115 println!("Parsed token tree: {:?}", p.parse( &data )? ); 145 self.register_views( p.parse( &data )?, ViewSource::Static )
146 }
116 147
117 Ok(()) 148 pub fn load_external(&mut self, key: &str, data: &str) -> Result<(),ParseError> {
149 let p = parser::Parser::new();
150 self.register_views(
151 p.parse( &data )?,
152 ViewSource::External{ key: key.to_string()}
153 )
118 } 154 }
119 155
120 pub fn load_external(&mut self, key: &str, data: &str) -> Result<(),Box::<dyn Error>> { 156 fn register_views(&mut self, token: Token, source: ViewSource) -> Result<(),ParseError> {
121 Ok(()) 157 let source_id = self.id_source( &source );
158 if let Token::Root(views) = token {
159 for token in views {
160 if let Token::View(name,props,output_tokens) = token {
161 let mut outputs = Vec::new();
162 for ot in output_tokens {
163 let output_name = if let Token::Output(on,..) = &ot {
164 on.clone()
165 } else {
166 return Err(ParseError::new(
167 Position::new(0,0),
168 "Broken parser? Item in view is not an output."
169 ));
170 };
171 outputs.push(
172 Output {
173 name: output_name,
174 ast_root: ot,
175 }
176 );
177 }
178
179 let layout = if let Some(l) = props.get("layout") {
180 Some(l.clone())
181 } else {
182 None
183 };
184
185 self.views.insert( name, View {
186 source: source_id,
187 outputs: outputs,
188 layout: layout,
189 });
190 } else {
191 return Err(ParseError::new(
192 Position::new(0,0),
193 "Broken parser? Item in root is not a view."
194 ));
195 }
196 }
197 Ok(())
198 } else {
199 Err(ParseError::new(
200 Position::new(0,0),
201 "Broken parser? Root is not a root token."
202 ))
203 }
204 }
205
206 pub fn view_partial(&self, view: &str, input: &Value ) -> Result<Value,ParseError> {
207 if let Some(view) = self.views.get(view) {
208 return view.process( input )
209 } else {
210 return Err(ParseError::new(Position::new(0,0),"No such view found"));
211 }
212 }
213
214 pub fn view(&self, view: &str, input: &Value ) -> Result<String,ParseError> {
215 if let Some(view) = self.views.get( view ) {
216 let mut output = view.process( input )?;
217 if let Some(l) = &view.layout {
218 println!("Layout: {}", l );
219 self.view( l, &output )
220 } else {
221 println!("No layout, at the top");
222 if let Value::Dictionary(mut d) = output &&
223 let Some(content) = d.remove("content") &&
224 let Value::String(s) = content {
225
226 Ok(s)
227 } else {
228 Err(ParseError::new(Position::new(0,0),"No content found in root layout."))
229 }
230 }
231
232 } else {
233 Err(ParseError::new(Position::new(0,0),"No such view found"))
234 }
122 } 235 }
123} 236}
237fn exec(input: &Value, tokens: &Vec<Token>, buf: &mut String) -> Result<(),ParseError> {
238 for token in tokens {
239 match token {
240 Token::Root(..) | Token::View(..) | Token::Output(..) => {
241 println!("Too high!?");
242 // Error?
243 }
244 Token::Show(s,_) => {
245 if let Value::Dictionary(d) = input &&
246 let Some(Value::String(s)) = d.get(s) {
247 buf.push_str(s);
248 }
249 }
250 Token::Text(s) => {
251 buf.push_str( s );
252 }
253 Token::Literal(..) => {
254 println!("Literal!?");
255 }
256 }
257 }
258 Ok(())
259}
124 260
125#[cfg(test)] 261#[cfg(test)]
126mod tests { 262mod tests {
@@ -130,7 +266,7 @@ mod tests {
130 266
131 #[test] 267 #[test]
132 fn lexing() { 268 fn lexing() {
133 let data = r#"Leading comment: [|fragment "basic" something="yeup"|>Hello there <|fragment|] Trailing text"#; 269 let data = r#"Leading comment: [|view "basic" something="yeup"|>Hello there <|view|] Trailing text"#;
134 let ll = lexer::Lexer::new( &data ); 270 let ll = lexer::Lexer::new( &data );
135 for sym in ll { 271 for sym in ll {
136 if let SymbolType::Error{what} = sym.symbol() { 272 if let SymbolType::Error{what} = sym.symbol() {
@@ -146,11 +282,13 @@ mod tests {
146 fn parsing() { 282 fn parsing() {
147 let mut ct = Crimtag::new(); 283 let mut ct = Crimtag::new();
148 if let Err(e) = ct.load_static(r#"Here is a sample 284 if let Err(e) = ct.load_static(r#"Here is a sample
149[|fragment "index" theme="standard"|><html><body>Hi</body></html><|fragment|] 285[|view "index" theme="standard"|><html><body>Hi</body></html><|view|]
150That was fun! now a placeholder: [|fragment "placeholder"|] and now one with an implicit [|fragment "simple"|>Body [|show "content"|]<|fragment|] aoeu 286That was fun! now a placeholder: [|view "placeholder"|] and now one with an implicit [|view "simple"|>Body [|show "content"|] and end<|view|] aoeu
287
288[|view "index" layout="simple"|>Whatever man!<|view|]
151 289
152Now with explicit outputs: 290Now with explicit outputs:
153[|fragment "complex"|> 291[|view "complex"|>
154 [|output "content"|> 292 [|output "content"|>
155 Here's a [|show "name"|]. 293 Here's a [|show "name"|].
156 <|output|] 294 <|output|]
@@ -160,10 +298,22 @@ Now with explicit outputs:
160 [|output "footer"|> 298 [|output "footer"|>
161 Same, I guess! 299 Same, I guess!
162 <|output|] 300 <|output|]
163<|fragment|]"#) { 301<|view|]"#) {
164 println!("Error: {:?}", e ); 302 println!("Error: {:?}", e );
165 } else { 303 } else {
166 println!("It finished"); 304 println!("It finished");
305 if let Ok(v) = ct.view(
306 "index",
307 &Value::Dictionary(
308 HashMap::from([
309 ("hi".to_string(),Value::String("hi".to_string()))
310 ])
311 )
312 ) {
313 println!("View result: {:?}", v );
314 } else {
315 println!("Error?");
316 }
167 } 317 }
168 } 318 }
169} 319}