use crate::error::SiteError; use crate::page::PageMeta; use crate::site::Site; use crate::wikitext::ParsedDirective; use std::collections::HashSet; #[derive(Debug, Eq, PartialEq)] pub enum Directive { Simple, UnnamedArg, SimpleArg, QuotedArg, MultilineArg, BrokenLinks(BrokenLinks), Img(Img), Inline(Inline), Meta(Meta), PageStats(PageStats), Shortcut(Shortcut), Tag(Tag), Toc(Toc), } impl TryFrom for Directive { type Error = SiteError; fn try_from(p: ParsedDirective) -> Result { let d = match p.name() { "simple" => { Self::check_args(&p, &[], &[], false)?; Directive::Simple } "unnamedarg" => { Self::check_args(&p, &[], &[], true)?; Directive::UnnamedArg } "simplearg" => { Self::check_args(&p, &["foo"], &[], false)?; Directive::SimpleArg } "quotedarg" => { Self::check_args(&p, &["bar"], &[], false)?; Directive::QuotedArg } "multilinearg" => { Self::check_args(&p, &["yo"], &["then", "else"], false)?; Directive::MultilineArg } "brokenlinks" => { Self::check_args(&p, BrokenLinks::REQUIRED, BrokenLinks::ALLOWED, BrokenLinks::ALLOW_ANY_UNNAMED)?; Directive::BrokenLinks(BrokenLinks::from(p)) } "img" => { Self::check_args(&p, Img::REQUIRED, Img::ALLOWED, Img::ALLOW_ANY_UNNAMED)?; Directive::Img(Img::from(p)) } "inline" => { Self::check_args( &p, Inline::REQUIRED, Inline::ALLOWED, Inline::ALLOW_ANY_UNNAMED, )?; Directive::Inline(Inline::from(p)) } "meta" => { Self::check_args(&p, Meta::REQUIRED, Meta::ALLOWED, Meta::ALLOW_ANY_UNNAMED)?; Directive::Meta(Meta::from(p)) } "pagestats" => { Self::check_args( &p, PageStats::REQUIRED, PageStats::ALLOWED, PageStats::ALLOW_ANY_UNNAMED, )?; Directive::PageStats(PageStats::from(p)) } "shortcut" => { Self::check_args(&p, Shortcut::REQUIRED, Shortcut::ALLOWED, Shortcut::ALLOW_ANY_UNNAMED)?; Directive::Shortcut(Shortcut::from(p)) } "tag" => { Self::check_args(&p, Tag::REQUIRED, Tag::ALLOWED, Tag::ALLOW_ANY_UNNAMED)?; Directive::Tag(Tag::from(p)) } "toc" => { Self::check_args(&p, Toc::REQUIRED, Toc::ALLOWED, Toc::ALLOW_ANY_UNNAMED)?; Directive::Toc(Toc::from(p)) } _ => return Err(SiteError::UnknownDirective(p.name().into())), }; Ok(d) } } impl Directive { fn check_args( p: &ParsedDirective, required: &[&str], allowed: &[&str], allow_any_unnamed: bool, ) -> Result<(), SiteError> { let args = p.args(); for arg in required.iter() { if !args.contains_key(arg) { return Err(SiteError::DirectiveMissingArg( p.name().into(), arg.to_string(), )); } } let required: HashSet = required.iter().map(|arg| arg.to_string()).collect(); let allowed: HashSet = allowed.iter().map(|arg| arg.to_string()).collect(); let allowed: HashSet = required.union(&allowed).cloned().collect(); for (arg, value) in p.args().iter() { if allow_any_unnamed && value.is_empty() { } else if !allowed.contains(*arg) { return Err(SiteError::DirectiveUnknownArg( p.name().into(), arg.to_string(), )); } } Ok(()) } pub fn process(&self, site: &mut Site, meta: &mut PageMeta) -> Result { match self { Self::Simple | Self::UnnamedArg | Self::SimpleArg | Self::QuotedArg | Self::MultilineArg => { panic!("directive {:?} may only be used in parsing tests", self) } Self::BrokenLinks(x) => x.process(site, meta), Self::Img(x) => x.process(site, meta), Self::Inline(x) => x.process(site, meta), Self::Meta(x) => x.process(site, meta), Self::PageStats(x) => x.process(site, meta), Self::Shortcut(x) => x.process(site, meta), Self::Tag(x) => x.process(site, meta), Self::Toc(x) => x.process(site, meta), } } } mod brokenlinks; use brokenlinks::BrokenLinks; mod meta; use meta::Meta; mod img; pub use img::Img; mod inline; pub use inline::Inline; mod pagestats; pub use pagestats::PageStats; mod shortcut; pub use shortcut::Shortcut; mod tag; use tag::Tag; mod toc; use toc::Toc;