diff --git a/Cargo.lock b/Cargo.lock index 2e0f9b5..6a4b961 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,6 +59,16 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "auto-registry" +version = "0.0.4" +dependencies = [ + "lazy_static", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "auto_impl" version = "1.2.0" @@ -733,6 +743,7 @@ name = "nml" version = "0.1.0" dependencies = [ "ariadne", + "auto-registry", "dashmap 6.0.1", "downcast-rs", "getopts", @@ -743,6 +754,7 @@ dependencies = [ "mlua", "rand 0.8.5", "regex", + "runtime-format", "rusqlite", "rust-crypto", "serde", @@ -1094,6 +1106,15 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "runtime-format" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09958d5b38bca768ede7928c767c89a08ba568144a7b61992aecae79b03c8c94" +dependencies = [ + "tinyvec", +] + [[package]] name = "rusqlite" version = "0.31.0" diff --git a/Cargo.toml b/Cargo.toml index 660725f..7f1bdc3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ inherits = "release" debug = true [dependencies] +auto-registry = { path = "crates/auto-registry" } ariadne = "0.4.1" dashmap = "6.0.1" downcast-rs = "1.2.1" @@ -32,11 +33,16 @@ rust-crypto = "0.2.36" serde = "1.0.204" serde_json = "1.0.120" syntect = "5.2.0" -tokio = { version = "1.38.1", features = ["macros", "rt-multi-thread", "io-std"]} +tokio = { version = "1.38.1", features = [ + "macros", + "rt-multi-thread", + "io-std", +] } tower-lsp = "0.20.0" unicode-segmentation = "1.11.0" walkdir = "2.5.0" +runtime-format = "0.1.3" [dev-dependencies] rand = "0.8.5" diff --git a/src/elements/blockquote.rs b/src/elements/blockquote.rs index 6fd8a64..0c159bf 100644 --- a/src/elements/blockquote.rs +++ b/src/elements/blockquote.rs @@ -1,3 +1,4 @@ +use core::fmt; use std::any::Any; use std::collections::HashMap; use std::ops::Range; @@ -11,9 +12,13 @@ use blockquote_style::AuthorPos::Before; use blockquote_style::BlockquoteStyle; use regex::Match; use regex::Regex; -use runtime_fmt::rt_format; +use runtime_format::FormatArgs; +use runtime_format::FormatError; +use runtime_format::FormatKey; +use runtime_format::FormatKeyError; use crate::compiler::compiler::Compiler; +use crate::compiler::compiler::Target; use crate::compiler::compiler::Target::HTML; use crate::document::document::Document; use crate::document::element::ContainerElement; @@ -42,6 +47,28 @@ pub struct Blockquote { pub(self) style: Rc, } +struct FmtPair<'a>(Target, &'a Blockquote); + +impl FormatKey for FmtPair<'_> { + fn fmt(&self, key: &str, f: &mut fmt::Formatter<'_>) -> Result<(), FormatKeyError> { + match key { + "author" => write!( + f, + "{}", + Compiler::sanitize(self.0, self.1.author.as_ref().unwrap_or(&"".into())) + ) + .map_err(FormatKeyError::Fmt), + "cite" => write!( + f, + "{}", + Compiler::sanitize(self.0, self.1.cite.as_ref().unwrap_or(&"".into())) + ) + .map_err(FormatKeyError::Fmt), + _ => Err(FormatKeyError::UnknownKey), + } + } +} + impl Element for Blockquote { fn location(&self) -> &Token { &self.location } @@ -53,34 +80,32 @@ impl Element for Blockquote { match compiler.target() { HTML => { let mut result = r#"
"#.to_string(); - let format_author = || -> Result { + let format_author = || -> Result { let mut result = String::new(); + if self.cite.is_some() || self.author.is_some() { result += r#"

"#; - match (&self.author, &self.cite) { - (Some(author), Some(cite)) => { - result += rt_format!( - self.style.format[0].as_str(), - author, - format!("{}", Compiler::sanitize(HTML, cite)), - )? - .as_str(); + let fmt_pair = FmtPair(compiler.target(), &self); + match (self.author.is_some(), self.cite.is_some()) { + (true, true) => { + let args = + FormatArgs::new(self.style.format[0].as_str(), &fmt_pair); + args.status()?; + result += args.to_string().as_str(); } - (Some(author), None) => { - result += rt_format!( - self.style.format[1].as_str(), - Compiler::sanitize(HTML, author), - )? - .as_str() + (true, false) => { + let args = + FormatArgs::new(self.style.format[1].as_str(), &fmt_pair); + args.status()?; + result += args.to_string().as_str(); } - (None, Some(cite)) => { - result += rt_format!( - self.style.format[2].as_str(), - format!("{}", Compiler::sanitize(HTML, cite)), - )? - .as_str() + (false, false) => { + let args = + FormatArgs::new(self.style.format[2].as_str(), &fmt_pair); + args.status()?; + result += args.to_string().as_str(); } - (None, None) => panic!(""), + _ => panic!(""), } result += "

"; } @@ -94,7 +119,7 @@ impl Element for Blockquote { result += "
"; } if self.style.author_pos == Before { - result += format_author().as_str(); + result += format_author().map_err(|err| err.to_string())?.as_str(); } result += "

"; @@ -103,7 +128,7 @@ impl Element for Blockquote { } result += "

"; if self.style.author_pos == After { - result += format_author().as_str(); + result += format_author().map_err(|err| err.to_string())?.as_str(); } result += "
"; @@ -371,9 +396,9 @@ mod blockquote_style { Self { author_pos: AuthorPos::After, format: [ - "A{author}, {cite}".into(), - "B{author}".into(), - "C{cite}".into(), + "{author}, {cite}".into(), + "{author}".into(), + "{cite}".into(), ], } } @@ -409,8 +434,7 @@ AFTER None, )); let parser = LangParser::default(); - let state = ParserState::new(&parser, None); - let (doc, _) = parser.parse(state, source, None); + let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); validate_document!(doc.content().borrow(), 0, Paragraph { Text{ content == "BEFORE" }; }; @@ -427,4 +451,44 @@ AFTER Paragraph { Text{ content == "AFTER" }; }; ); } + + #[test] + pub fn style() { + let source = Rc::new(SourceFile::with_content( + "".to_string(), + r#" +@@style.blockquote = { + "author_pos": "Before", + "format": ["{cite} by {author}", "Author: {author}", "From: {cite}"] +} +PRE +>[author=A, cite=B, url=C] Some entry +> contin**ued here +> ** +AFTER +"# + .to_string(), + None, + )); + let parser = LangParser::default(); + let (_, state) = parser.parse(ParserState::new(&parser, None), source, None); + + let style = state + .shared + .styles + .borrow() + .current(blockquote_style::STYLE_KEY) + .downcast_rc::() + .unwrap(); + + assert_eq!(style.author_pos, Before); + assert_eq!( + style.format, + [ + "{cite} by {author}".to_string(), + "Author: {author}".to_string(), + "From: {cite}".to_string() + ] + ); + } }