use crate::directive::{Directive, Processed}; use crate::page::PageMeta; use crate::site::Site; use log::trace; use std::collections::HashMap; use std::path::Path; #[derive(Debug, thiserror::Error)] pub enum WikitextError { #[error(transparent)] Directive(#[from] crate::directive::DirectiveError), #[error(transparent)] Site(#[from] crate::site::SiteError), } #[derive(Debug, Clone, Eq, PartialEq)] pub enum Snippet { Markdown(String), WikiLink(WikiLink), Directive(ParsedDirective), } impl Snippet { pub fn prepare(&self, site: &mut Site) -> Result<(), WikitextError> { trace!("prepare snippet {:?}", self); if let Snippet::Directive(p) = self { trace!("creating directive from parsed directive"); let e = Directive::try_from(p); if let Ok(d) = e { trace!("prepare directive {:?}", d); d.prepare(site)?; } else { trace!("failed to create directive: {}", e.unwrap_err()); } } Ok(()) } pub fn process( &self, site: &mut Site, meta: &mut PageMeta, ) -> Result { trace!("Snippet::process: self={:?}", self); let processed = match self { Snippet::Markdown(text) => Processed::Markdown(text.into()), Snippet::WikiLink(w) => { let resolved = site.resolve(meta.path(), Path::new(w.target()))?; trace!("resolved {} to {}", w.target(), resolved.display()); meta.add_link(&resolved); let link = format!("[{}]({})", w.link_text(), resolved.display()); Processed::Markdown(link) } Snippet::Directive(p) => { let e = Directive::try_from(p); if let Ok(d) = e { d.process(site, meta)? } else if let Some(shortcut) = site.shortcut(p.name()) { let arg = p.unnamed_args().first().unwrap().to_string(); let link = format!("[{}]({})", shortcut.desc(&arg), shortcut.url(&arg)); Processed::Markdown(link) } else { return Err(e.unwrap_err().into()); } } }; Ok(processed) } } #[derive(Debug, Clone, Eq, PartialEq)] pub struct WikiLink { link_text: String, target: String, } impl WikiLink { pub fn new(link_text: &str, target: &str) -> Self { Self { link_text: link_text.into(), target: target.into(), } } fn link_text(&self) -> &str { &self.link_text } fn target(&self) -> &str { &self.target } } #[derive(Debug, Clone, Eq, PartialEq)] pub struct ParsedDirective { name: String, args: HashMap, } impl ParsedDirective { pub fn new(name: &str, args: HashMap) -> Result { trace!("ParsedDirective::new: name={:?} args={:?}", name, args); Ok(Self { name: name.into(), args, }) } pub fn name(&self) -> &str { &self.name } pub fn args(&self) -> HashMap<&str, &str> { self.args .iter() .map(|(k, v)| (k.as_str(), v.as_str())) .collect() } pub fn unnamed_args(&self) -> Vec<&str> { self.args .iter() .filter_map(|(k, v)| if v.is_empty() { Some(k.as_str()) } else { None }) .collect() } }