Skip to content

Commit

Permalink
fix(plugins): Abstract plugin registry
Browse files Browse the repository at this point in the history
This will allow changing the implementation without impacting users.

BREAKING CHANGE: Changed from `HashMap` to `PluginRegistry` for filters,
tags, and blocks.
  • Loading branch information
epage committed Dec 5, 2018
1 parent eab6f40 commit ef4cabf
Show file tree
Hide file tree
Showing 19 changed files with 153 additions and 55 deletions.
6 changes: 3 additions & 3 deletions liquid-compiler/src/options.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashMap;
use liquid_interpreter::PluginRegistry;

use super::BoxedBlockParser;
use super::BoxedTagParser;
Expand All @@ -7,8 +7,8 @@ use super::NullInclude;

#[derive(Clone)]
pub struct LiquidOptions {
pub blocks: HashMap<&'static str, BoxedBlockParser>,
pub tags: HashMap<&'static str, BoxedTagParser>,
pub blocks: PluginRegistry<BoxedBlockParser>,
pub tags: PluginRegistry<BoxedTagParser>,
pub include_source: Box<Include>,
}

Expand Down
8 changes: 4 additions & 4 deletions liquid-compiler/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,11 +414,11 @@ impl<'a> Tag<'a> {
let position = name.as_span();
let name = name.as_str();

if options.tags.contains_key(name) {
options.tags[name].parse(name, tokens, options)
} else if options.blocks.contains_key(name) {
if let Some(plugin) = options.tags.get(name) {
plugin.parse(name, tokens, options)
} else if let Some(plugin) = options.blocks.get(name) {
let block = TagBlock::new(name, next_elements);
let renderables = options.blocks[name].parse(name, tokens, block, options)?;
let renderables = plugin.parse(name, tokens, block, options)?;
Ok(renderables)
} else {
let pest_error = ::pest::error::Error::new_from_span(
Expand Down
9 changes: 5 additions & 4 deletions liquid-interpreter/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use value::{Object, PathRef, Scalar, Value};
use super::Expression;
use super::Globals;
use super::{BoxedValueFilter, FilterValue};
use super::PluginRegistry;

/// Format an error for an unexpected value.
pub fn unexpected_value_error<S: ToString>(expected: &str, actual: Option<S>) -> Error {
Expand Down Expand Up @@ -291,7 +292,7 @@ impl<'g> Default for Stack<'g> {
/// Create processing context for a template.
pub struct ContextBuilder<'g> {
globals: Option<&'g Globals>,
filters: sync::Arc<HashMap<&'static str, BoxedValueFilter>>,
filters: sync::Arc<PluginRegistry<BoxedValueFilter>>,
}

impl<'g> ContextBuilder<'g> {
Expand All @@ -312,7 +313,7 @@ impl<'g> ContextBuilder<'g> {
/// Initialize the context with the given filters.
pub fn set_filters(
mut self,
filters: &sync::Arc<HashMap<&'static str, BoxedValueFilter>>,
filters: &sync::Arc<PluginRegistry<BoxedValueFilter>>,
) -> Self {
self.filters = sync::Arc::clone(filters);
self
Expand Down Expand Up @@ -349,7 +350,7 @@ pub struct Context<'g> {
cycles: CycleStateInner,
ifchanged: IfChangedState,

filters: sync::Arc<HashMap<&'static str, BoxedValueFilter>>,
filters: sync::Arc<PluginRegistry<BoxedValueFilter>>,
}

impl<'g> Context<'g> {
Expand All @@ -368,7 +369,7 @@ impl<'g> Context<'g> {
let f: &FilterValue = f;
f
}).ok_or_else(|| {
let available = itertools::join(self.filters.keys(), ", ");
let available = itertools::join(self.filters.plugin_names(), ", ");
Error::with_msg("Unknown filter")
.context("requested filter", name.to_owned())
.context("available filters", available)
Expand Down
2 changes: 2 additions & 0 deletions liquid-interpreter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ mod expression;
mod filter;
mod filter_chain;
mod globals;
mod registry;
mod renderable;
mod template;
mod text;
Expand All @@ -34,6 +35,7 @@ pub use self::expression::*;
pub use self::filter::*;
pub use self::filter_chain::*;
pub use self::globals::*;
pub use self::registry::*;
pub use self::renderable::*;
pub use self::template::*;
pub use self::text::*;
Expand Down
99 changes: 99 additions & 0 deletions liquid-interpreter/src/registry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use std::collections::hash_map;

type MapImpl<K, V> = hash_map::HashMap<K, V>;
type KeysImpl<'a, K, V> = hash_map::Keys<'a, K, V>;

/// Liquid language plugin registry.
pub struct PluginRegistry<P> {
plugins: MapImpl<&'static str, P>
}

impl<P> PluginRegistry<P> {
/// Create a new registry.
pub fn new() -> Self {
Self {
plugins: Default::default(),
}
}

/// Register a plugin
///
/// Generally this is used when setting up the program.
///
/// Returns whether this overrode an existing plugin.
pub fn register(&mut self, name: &'static str, plugin: P) -> bool {
let old = self.plugins.insert(name, plugin);
old.is_some()
}

/// Look up an existing plugin.
///
/// Generally this is used for running plugins.
pub fn get(&self, name: &str) -> Option<&P> {
self.plugins.get(name)
}

/// All available plugins
pub fn plugin_names(&self) -> PluginNames<P> {
PluginNames {
iter: self.plugins.keys(),
}
}
}

impl<P> Default for PluginRegistry<P> {
#[inline]
fn default() -> Self {
Self::new()
}
}

impl<P> Clone for PluginRegistry<P>
where
P: Clone
{
#[inline]
fn clone(&self) -> Self {
Self {
plugins: self.plugins.clone(),
}
}
}

//////////////////////////////////////////////////////////////////////////////

macro_rules! delegate_iterator {
(($name:ident $($generics:tt)*) => $item:ty) => {
impl $($generics)* Iterator for $name $($generics)* {
type Item = $item;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|s| *s)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}

impl $($generics)* ExactSizeIterator for $name $($generics)* {
#[inline]
fn len(&self) -> usize {
self.iter.len()
}
}
}
}

//////////////////////////////////////////////////////////////////////////////

/// Available plugins.
#[derive(Debug)]
pub struct PluginNames<'a, P>
where
P: 'a
{
iter: KeysImpl<'a, &'static str, P>,
}

delegate_iterator!((PluginNames<'a, P>) => &'static str);
15 changes: 7 additions & 8 deletions src/parser.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::Read;
use std::path;
Expand All @@ -14,9 +13,9 @@ use tags;

#[derive(Default, Clone)]
pub struct ParserBuilder {
blocks: HashMap<&'static str, compiler::BoxedBlockParser>,
tags: HashMap<&'static str, compiler::BoxedTagParser>,
filters: HashMap<&'static str, interpreter::BoxedValueFilter>,
blocks: interpreter::PluginRegistry<compiler::BoxedBlockParser>,
tags: interpreter::PluginRegistry<compiler::BoxedTagParser>,
filters: interpreter::PluginRegistry<interpreter::BoxedValueFilter>,
include_source: Option<Box<compiler::Include>>,
}

Expand Down Expand Up @@ -165,13 +164,13 @@ impl ParserBuilder {
name: &'static str,
block: B,
) -> Self {
self.blocks.insert(name, block.into());
self.blocks.register(name, block.into());
self
}

/// Inserts a new custom tag into the parser
pub fn tag<T: Into<compiler::BoxedTagParser>>(mut self, name: &'static str, tag: T) -> Self {
self.tags.insert(name, tag.into());
self.tags.register(name, tag.into());
self
}

Expand All @@ -181,7 +180,7 @@ impl ParserBuilder {
name: &'static str,
filter: F,
) -> Self {
self.filters.insert(name, filter.into());
self.filters.register(name, filter.into());
self
}

Expand Down Expand Up @@ -215,7 +214,7 @@ impl ParserBuilder {
#[derive(Default, Clone)]
pub struct Parser {
options: compiler::LiquidOptions,
filters: sync::Arc<HashMap<&'static str, interpreter::BoxedValueFilter>>,
filters: sync::Arc<interpreter::PluginRegistry<interpreter::BoxedValueFilter>>,
}

impl Parser {
Expand Down
6 changes: 3 additions & 3 deletions src/tags/assign_tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,13 @@ mod test {
let mut options = LiquidOptions::default();
options
.tags
.insert("assign", (assign_tag as compiler::FnParseTag).into());
.register("assign", (assign_tag as compiler::FnParseTag).into());
options
.blocks
.insert("if", (tags::if_block as compiler::FnParseBlock).into());
.register("if", (tags::if_block as compiler::FnParseBlock).into());
options
.blocks
.insert("for", (tags::for_block as compiler::FnParseBlock).into());
.register("for", (tags::for_block as compiler::FnParseBlock).into());
options
}

Expand Down
2 changes: 1 addition & 1 deletion src/tags/capture_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ mod test {
let mut options = LiquidOptions::default();
options
.blocks
.insert("capture", (capture_block as compiler::FnParseBlock).into());
.register("capture", (capture_block as compiler::FnParseBlock).into());
options
}

Expand Down
2 changes: 1 addition & 1 deletion src/tags/case_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ mod test {
let mut options = LiquidOptions::default();
options
.blocks
.insert("case", (case_block as compiler::FnParseBlock).into());
.register("case", (case_block as compiler::FnParseBlock).into());
options
}

Expand Down
2 changes: 1 addition & 1 deletion src/tags/comment_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ mod test {
let mut options = LiquidOptions::default();
options
.blocks
.insert("comment", (comment_block as compiler::FnParseBlock).into());
.register("comment", (comment_block as compiler::FnParseBlock).into());
options
}

Expand Down
2 changes: 1 addition & 1 deletion src/tags/cycle_tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ mod test {
let mut options = LiquidOptions::default();
options
.tags
.insert("cycle", (cycle_tag as compiler::FnParseTag).into());
.register("cycle", (cycle_tag as compiler::FnParseTag).into());
options
}

Expand Down
11 changes: 5 additions & 6 deletions src/tags/for_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,6 @@ pub fn tablerow_block(

#[cfg(test)]
mod test {
use std::collections::HashMap;
use std::sync;

use compiler;
Expand All @@ -491,14 +490,14 @@ mod test {
let mut options = LiquidOptions::default();
options
.blocks
.insert("for", (for_block as compiler::FnParseBlock).into());
options.blocks.insert(
.register("for", (for_block as compiler::FnParseBlock).into());
options.blocks.register(
"tablerow",
(tablerow_block as compiler::FnParseBlock).into(),
);
options
.tags
.insert("assign", (tags::assign_tag as compiler::FnParseTag).into());
.register("assign", (tags::assign_tag as compiler::FnParseTag).into());
options
}

Expand Down Expand Up @@ -796,8 +795,8 @@ mod test {
.map(interpreter::Template::new)
.unwrap();

let mut filters: HashMap<&'static str, interpreter::BoxedValueFilter> = HashMap::new();
filters.insert(
let mut filters = interpreter::PluginRegistry::<interpreter::BoxedValueFilter>::new();
filters.register(
"shout",
((|input, _args| Ok(Value::scalar(input.to_str().to_uppercase())))
as interpreter::FnFilterValue)
Expand Down
4 changes: 2 additions & 2 deletions src/tags/if_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,10 +398,10 @@ mod test {
let mut options = LiquidOptions::default();
options
.blocks
.insert("if", (if_block as compiler::FnParseBlock).into());
.register("if", (if_block as compiler::FnParseBlock).into());
options
.blocks
.insert("unless", (unless_block as compiler::FnParseBlock).into());
.register("unless", (unless_block as compiler::FnParseBlock).into());
options
}

Expand Down
6 changes: 3 additions & 3 deletions src/tags/ifchanged_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,16 @@ mod test {

fn options() -> LiquidOptions {
let mut options = LiquidOptions::default();
options.blocks.insert(
options.blocks.register(
"ifchanged",
(ifchanged_block as compiler::FnParseBlock).into(),
);
options
.blocks
.insert("for", (tags::for_block as compiler::FnParseBlock).into());
.register("for", (tags::for_block as compiler::FnParseBlock).into());
options
.blocks
.insert("if", (tags::if_block as compiler::FnParseBlock).into());
.register("if", (tags::if_block as compiler::FnParseBlock).into());
options
}

Expand Down

0 comments on commit ef4cabf

Please sign in to comment.