From 062048ef6e3e060bcf841ea2ec92e026f09d8da0 Mon Sep 17 00:00:00 2001 From: eichlan Date: Tue, 23 Jun 2026 15:31:51 -0700 Subject: Roorganized into workspace and packages. This is to accomidate a new proc_macro package, which can't have anything else in it. --- src/lib.rs | 544 ------------------------------------------------------------- 1 file changed, 544 deletions(-) delete mode 100644 src/lib.rs (limited to 'src/lib.rs') diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index e8f9e9f..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,544 +0,0 @@ -use std::path::{PathBuf,Path}; -use std::collections::HashMap; -use core::error::Error; -use std::time::SystemTime; - -mod lexer; -mod parser; -mod position; -mod error; -pub mod context; - -pub use error::{CrimError,CrimResult}; -pub use position::*; -pub use context::{Context,Value,MappedStructure,StatefulContext,MappedValue,MappedList,MappedHash}; - -#[derive(Debug)] -pub struct Crimtag { - views: HashMap, - sources: Vec, -} - -#[derive(PartialEq,Eq,Debug,Clone)] -pub enum ViewSource { - File { - path: PathBuf, - loaded: SystemTime, - }, - Static, - External { - key: String, - }, -} - -#[derive(Debug)] -struct View { - name: String, - source: usize, - outputs: Vec, -// theme: Option - layout: Option, -} - -impl View { - fn process(&self, input: &dyn for<'a> MappedStructure<'a>) -> CrimResult { - let mut out_vars = Context::new(); - for output in &self.outputs { - let mut buf = String::new(); - let sc = StatefulContext::root( input ); - exec( &sc, &output.code, &mut buf )?; - out_vars.insert( output.name.clone(), Value::String(buf) ); - } - Ok(out_vars) - } -} - -#[derive(Debug)] -struct Output { - name: String, - code: Vec, -} - -type Properties = HashMap; - -#[derive(Debug)] -enum Token { - Loop(Identifier, Vec), - Show(Identifier,Properties), - If(Identifier, Vec, Vec), - Text(String), -} - -#[derive(Debug,Clone,PartialEq)] -pub enum IdentifierValue { - Root, - Name(String), - Index(usize), -} - -pub type Identifier = Vec; - -impl Crimtag { - pub fn new() -> Self { - Self { - views: HashMap::new(), - sources: vec![ViewSource::Static], - } - } - - fn id_source(&mut self, src: &ViewSource ) -> usize { - for idx in 0..self.sources.len() { - if self.sources[idx] == *src { - return idx; - } - } - - // Nothing found, add a new one - let idx = self.sources.len(); - self.sources.push( src.clone() ); - idx - } - - pub fn load_file(&mut self, path: &Path) -> Result<(),Box::> { - let time = if let Ok(meta) = path.metadata() { - if let Ok(t) = meta.modified() { - t - } else if let Ok(t) = meta.created() { - t - } else { - SystemTime::now() - } - } else { - SystemTime::now() - }; - let source_id = self.id_source( - &ViewSource::File { - path: path.to_path_buf(), - loaded: time, - } - ); - let p = parser::Parser::new(); - self.register_views( - p.parse( - &String::from_utf8( std::fs::read( path )? )?, - source_id - )?, - )?; - Ok(()) - } - - pub fn load_static(&mut self, data: &str) -> CrimResult<()> { - let source_id = self.id_source( &ViewSource::Static ); - let p = parser::Parser::new(); - self.register_views( p.parse( &data, source_id )? ) - } - - pub fn load_external(&mut self, key: &str, data: &str) -> CrimResult<()> { - let source_id = self.id_source( - &ViewSource::External{ key: key.to_string()} - ); - - let p = parser::Parser::new(); - self.register_views( - p.parse( &data, source_id )?, - ) - } - - fn register_views(&mut self, views: Vec) -> CrimResult<()> { - for view in views { - self.views.insert( view.name.clone(), view ); - } - Ok(()) - } - - pub fn get_view_source(&self, view: &str) -> Option { - if let Some(view) = self.views.get(view) { - if view.source < self.sources.len() { - Some(self.sources[view.source].clone()) - } else { - None - } - } else { - None - } - } - - pub fn render_partial(&self, view: &str, input: &dyn for<'a> MappedStructure<'a> ) -> CrimResult { - if let Some(view) = self.views.get(view) { - return view.process( input ) - } else { - return Err(CrimError::other("No such view found".into())); - } - } - - pub fn render(&self, view: &str, input: &dyn for<'a> MappedStructure<'a> ) -> CrimResult { - if let Some(view) = self.views.get( view ) { - //println!("::Token tree::\n{:?}", view ); - let mut output = view.process( input )?; - if let Some(l) = &view.layout { - self.render( l, &output ) - } else { - if let Some(content) = output.remove("content") && - let Value::String(s) = content { - - Ok(s) - } else { - Err(CrimError::other("No content found in root layout.".into())) - } - } - - } else { - Err(CrimError::other("No such view found".into())) - } - } -} - -fn exec<'a>(input: &StatefulContext<'a>, tokens: &Vec, buf: &mut String) -> CrimResult<()> { - for token in tokens { - match token { - Token::Loop(ident,code) => { - //println!("!!! Loop over: {:?} {:?}", ident, input.get_value( &ident )); - if let Some(value) = input.get_value( &ident ) && - let MappedValue::List(list) = value { - let output = - &list.for_each(&|rec: &MappedValue, buf: &mut String| { - if let MappedValue::Struct(d) = rec { - exec( &input.local(*d), code, buf )?; - } - Ok(()) - })?; - //println!("!!! Output from loop: {}", output ); - buf.push_str( &output ); - } - } - Token::If(ident,code,other) => { - if let Some(value) = input.get_value( &ident ) && - let MappedValue::Bool(b) = value && b { - exec(input, code, buf)? - } else { - exec(input, other, buf)? - } - } - Token::Show(ident,p) => { - let format = if let Some(s) = p.get("format") { - s - } else { - &"".to_string() - }; - if let Some(value) = input.get_value( &ident ) { - match value { - MappedValue::Struct(_) => { - buf.push_str("Struct"); - } - MappedValue::List(_) => { - buf.push_str("List"); - } - MappedValue::Bool(b) => { - if b { - buf.push_str("true"); - } else { - buf.push_str("false"); - } - } - MappedValue::Str(s) => { - buf.push_str(s); - } - MappedValue::String(s) => { - buf.push_str(s.as_str()); - } - MappedValue::Int8(i) => { - buf.push_str(&i.to_string()); - } - MappedValue::Int16(i) => { - buf.push_str(&i.to_string()); - } - MappedValue::Int32(i) => { - buf.push_str(&i.to_string()); - } - MappedValue::Int64(i) => { - buf.push_str(&i.to_string()); - } - MappedValue::UInt8(i) => { - buf.push_str(&i.to_string()); - } - MappedValue::UInt16(i) => { - buf.push_str(&i.to_string()); - } - MappedValue::UInt32(i) => { - buf.push_str(&i.to_string()); - } - MappedValue::UInt64(i) => { - buf.push_str(&i.to_string()); - } - MappedValue::Float32(f) => { - if format == "" { - buf.push_str(&f.to_string()); - } else { - buf.push_str(&f.to_string()); - } - } - MappedValue::Float64(f) => { - if format == "" { - buf.push_str(&f.to_string()); - } else { - buf.push_str(&f.to_string()); - } - } - } - } - } - Token::Text(s) => { - buf.push_str( s ); - } - //_ => {} - } - } - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - - use crate::lexer::*; - - #[test] - fn lexing() { - let data = r#"Leading comment: [|view "basic" something="yeup"|>Hello there <|view|] Trailing text"#; - let ll = lexer::Lexer::new( &data ); - for sym in ll { - if let SymbolType::Error{what} = sym.symbol() { - println!("Error {}:{}: {:?}", sym.start().line, sym.start().column, what ); - break; - } else { - println!("Symbol: {:?}", sym ); - } - } - } - - #[test] - fn parsing() { - let mut ct = Crimtag::new(); - if let Err(e) = ct.load_static(r#"Here is a sample -[|view "index" theme="standard"|>Hi<|view|] -That was fun! now a placeholder: [|view "placeholder"|] and now one with an implicit [|view "simple"|>Body -> [|show content|] <- and end<|view|] aoeu - -[|view "person" layout="simple"|>Welcome [|show person.last_name|], [|show person.first_name|]! It's lovely to see you again.<|view|] - -Now with explicit outputs: -[|view "complex"|> - [|output "content"|> - Here's a [|show name|]. - <|output|] - [|output "sidebar"|> - What's up world? - <|output|] - [|output "footer"|> - Same, I guess! - <|output|] -<|view|]"#) { - println!("Error: {:?}", e ); - } else { - println!("It finished"); - if let Ok(v) = ct.render( - "person", - &Context::from([ - ("hi".to_string(),Value::String("hi".to_string())), - ("person".to_string(),Value::Dictionary(HashMap::from([ - ("first_name".to_string(), Value::String("Bob".to_string())), - ("last_name".to_string(), Value::String("Smith".to_string())), - ]))), - ]) - ) { - println!("View result: {:?}", v ); - } else { - println!("Error?"); - } - } - } - - #[test] - fn loops() -> Result<(),CrimError> { - let mut ct = Crimtag::new(); - ct.load_static(r#"Looping code: -[|view "index"|>We will enumerate people here:[|loop people|> - - [|show last_name|], [|show first_name|][|if show_title |>: [|show title|] <|else|> **title redacted** <|if|] ::>[|loop tags|> [|show tag|]<|loop|] <:: <|loop|] -<|view|] -"#)?; - - let ctx = Context::from([ - ("people".to_string(), vec![ - [ - ("first_name".into(), "Joe".into()), - ("last_name".into(), "Smith".into()), - ("show_title".into(), true.into()), - ("title".into(), "CEO".into()), - ("tags".into(), vec![ - Context::from([("tag".into(), "jerk".into()),]).into(), - Context::from([("tag".into(), "ugly".into()),]).into(), - ].into()), - ].into(), - [ - ("first_name".into(), "Chris".into()), - ("last_name".into(), "Perkens".into()), - ("show_title".into(), false.into()), - ("title".into(), "Baconeer".into()), - ("tags".into(), vec![ - Context::from([("tag".into(), "jerk".into()),]).into(), - Context::from([("tag".into(), "ugly".into()),]).into(), - ].into()), - ].into(), - [ - ("first_name".into(), "Will".into()), - ("last_name".into(), "Power".into()), - ("show_title".into(), false.into()), - ("title".into(), "CFO".into()), - ("tags".into(), vec![ - Context::from([("tag".into(), "jerk".into()),]).into(), - Context::from([("tag".into(), "ugly".into()),]).into(), - ].into()), - ].into(), - [ - ("first_name".into(), "Justin".into()), - ("last_name".into(), "Time".into()), - ("show_title".into(), true.into()), - ("title".into(), "CIO".into()), - ("tags".into(), vec![ - Context::from([("tag".into(), "jerk".into()),]).into(), - Context::from([("tag".into(), "ugly".into()),]).into(), - ].into()), - ].into(), - ].into()), - ]); - - println!("View result: {}", ct.render("index", &ctx)? ); - - Ok(()) - } - - #[test] - fn conditional() -> Result<(),CrimError> { - let mut ct = Crimtag::new(); - ct.load_static(r#"Hi there -[|view "index"|> - Color: [|if is_red|> red <|elif is_blue |> blue <|else|> green <|if|] -<|view|]"#)?; - - let ctx = Context::from([ - ("is_red".into(), false.into()), - ("is_blue".into(), false.into()), - ("color".into(), "purple".into()), - ]); - - println!("View result: {}", ct.render("index", &ctx)? ); - - Ok(()) - } - - struct Item { - pub id: i32, - pub title: String, - pub body: String, - } - - struct Person { - pub id: i32, - pub username: String, - pub name: String, - } - - struct Page { - pub user: Person, - pub total_items: i32, - pub total_pages: i32, - pub cur_page: i32, - pub items: Vec, - } - - impl Page { - fn new() -> Page { - Page { - user: Person { - id: 443, - username: "eichlan".into(), - name: "Mike".into(), - }, - total_items: 4000, - total_pages: 400, - cur_page: 35, - items: vec![ - Item{ - id: 123, - title: "hi".into(), - body: "body".into(), - }, - Item{ - id: 124, - title: "bye".into(), - body: "wooo".into(), - }, - Item{ - id: 125, - title: "ciao".into(), - body: "whatever".into(), - } - ], - } - } - } - - impl<'a> MappedStructure<'a> for Page { - fn get_value(&'a self, id: &str) -> Option> { - match id { - "user" => Some(MappedValue::Struct(&self.user)), - "total_items" => Some(MappedValue::Int32(self.total_items)), - "total_pages" => Some(MappedValue::Int32(self.total_pages)), - "cur_page" => Some(MappedValue::Int32(self.cur_page)), - "items" => Some(MappedValue::<'a>::List(&self.items)), - _ => None, - } - } - } - - impl<'a> MappedStructure<'a> for Person { - fn get_value(&'a self, id: &str) -> Option> { - match id { - "id" => Some(MappedValue::Int32(self.id)), - "username" => Some(MappedValue::String(&self.username)), - "name" => Some(MappedValue::String(&self.name)), - _ => None, - } - } - } - - impl<'a> MappedStructure<'a> for Item { - fn get_value(&'a self, id: &str) -> Option> { - match id { - "id" => Some(MappedValue::Int32(self.id)), - "title" => Some(MappedValue::String(&self.title)), - "body" => Some(MappedValue::String(&self.body)), - _ => None, - } - } - } - - #[test] - fn custom() -> Result<(), CrimError> { - let page = Page::new(); - - println!("{:?}", - page.get_value(&"total_pages") - ); - - if let Some(MappedValue::List(l)) = page.get_value(&"items") { - l.for_each(&|x: &MappedValue, buf: &mut String| { - if let MappedValue::Struct(s) = x { - println!(" - {:?}", s.get_value(&"title")); - buf.push_str(&format!("{:?}",s.get_value(&"title"))); - } - Ok(()) - }).expect("loop"); - } - Ok(()) - } -} -- cgit v1.2.3