Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use smartstring for handlebars path segments #567

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pest = "2.1.0"
pest_derive = "2.1.0"
serde = "1.0.0"
serde_json = "1.0.39"
smartstring = "1"
walkdir = { version = "2.2.3", optional = true }
rhai = { version = "1.6", optional = true, features = ["sync", "serde"] }
rust-embed = { version = "6.3.0", optional = true, features = ["include-exclude"] }
Expand Down
17 changes: 11 additions & 6 deletions src/block.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use std::collections::BTreeMap;

use serde_json::value::Value as Json;
use smartstring::alias::String as LazyCompactString;

use crate::error::RenderError;
use crate::local_vars::LocalVars;

#[derive(Clone, Debug)]
pub enum BlockParamHolder {
// a reference to certain context value
Path(Vec<String>),
Path(Vec<LazyCompactString>),
// an actual value holder
Value(Json),
}
Expand All @@ -18,7 +19,7 @@ impl BlockParamHolder {
BlockParamHolder::Value(v)
}

pub fn path(r: Vec<String>) -> BlockParamHolder {
pub fn path(r: Vec<LazyCompactString>) -> BlockParamHolder {
BlockParamHolder::Path(r)
}
}
Expand All @@ -37,7 +38,11 @@ impl<'reg> BlockParams<'reg> {

/// Add a path reference as the parameter. The `path` is a vector of path
/// segments the relative to current block's base path.
pub fn add_path(&mut self, k: &'reg str, path: Vec<String>) -> Result<(), RenderError> {
pub fn add_path(
&mut self,
k: &'reg str,
path: Vec<LazyCompactString>,
) -> Result<(), RenderError> {
self.data.insert(k, BlockParamHolder::path(path));
Ok(())
}
Expand All @@ -58,7 +63,7 @@ impl<'reg> BlockParams<'reg> {
#[derive(Debug, Clone, Default)]
pub struct BlockContext<'rc> {
/// the base_path of current block scope
base_path: Vec<String>,
base_path: Vec<LazyCompactString>,
/// the base_value of current block scope, when the block is using a
/// constant or derived value as block base
base_value: Option<Json>,
Expand Down Expand Up @@ -91,12 +96,12 @@ impl<'rc> BlockContext<'rc> {

/// borrow a reference to current scope's base path
/// all paths inside this block will be relative to this path
pub fn base_path(&self) -> &Vec<String> {
pub fn base_path(&self) -> &Vec<LazyCompactString> {
&self.base_path
}

/// borrow a mutable reference to the base path
pub fn base_path_mut(&mut self) -> &mut Vec<String> {
pub fn base_path_mut(&mut self) -> &mut Vec<LazyCompactString> {
&mut self.base_path
}

Expand Down
15 changes: 8 additions & 7 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::collections::{HashMap, VecDeque};

use serde::Serialize;
use serde_json::value::{to_value, Map, Value as Json};
use smartstring::alias::String as LazyCompactString;

use crate::block::{BlockContext, BlockParamHolder};
use crate::error::RenderError;
Expand All @@ -23,13 +24,13 @@ pub struct Context {
enum ResolvedPath<'a> {
// FIXME: change to borrowed when possible
// full path
AbsolutePath(Vec<String>),
AbsolutePath(Vec<LazyCompactString>),
// relative path and path root
RelativePath(Vec<String>),
RelativePath(Vec<LazyCompactString>),
// relative path against block param value
BlockParamValue(Vec<String>, &'a Json),
BlockParamValue(Vec<LazyCompactString>, &'a Json),
// relative path against derived value,
LocalValue(Vec<String>, &'a Json),
LocalValue(Vec<LazyCompactString>, &'a Json),
}

fn parse_json_visitor<'a>(
Expand Down Expand Up @@ -127,7 +128,7 @@ fn get_data<'a>(d: Option<&'a Json>, p: &str) -> Result<Option<&'a Json>, Render
fn get_in_block_params<'a>(
block_contexts: &'a VecDeque<BlockContext<'_>>,
p: &str,
) -> Option<(&'a BlockParamHolder, &'a Vec<String>)> {
) -> Option<(&'a BlockParamHolder, &'a Vec<LazyCompactString>)> {
for bc in block_contexts {
let v = bc.get_block_param(p);
if v.is_some() {
Expand Down Expand Up @@ -406,7 +407,7 @@ mod test {
});
let ctx = Context::wraps(&m).unwrap();
let mut block = BlockContext::new();
*block.base_path_mut() = ["a".to_owned(), "b".to_owned()].to_vec();
*block.base_path_mut() = ["a".into(), "b".into()].to_vec();

let mut blocks = VecDeque::new();
blocks.push_front(block);
Expand All @@ -430,7 +431,7 @@ mod test {
let ctx = Context::wraps(&m).unwrap();
let mut block_params = BlockParams::new();
block_params
.add_path("z", ["0".to_owned(), "a".to_owned()].to_vec())
.add_path("z", ["0".into(), "a".into()].to_vec())
.unwrap();
block_params.add_value("t", json!("good")).unwrap();

Expand Down
23 changes: 18 additions & 5 deletions src/helpers/helper_each.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use serde_json::value::Value as Json;
use smartstring::alias::String as LazyCompactString;

use super::block_util::create_block;
use crate::block::{BlockContext, BlockParams};
Expand All @@ -13,8 +14,8 @@ use crate::util::copy_on_push_vec;

fn update_block_context(
block: &mut BlockContext<'_>,
base_path: Option<&Vec<String>>,
relative_path: String,
base_path: Option<&Vec<LazyCompactString>>,
relative_path: LazyCompactString,
is_first: bool,
value: &Json,
) {
Expand All @@ -32,7 +33,7 @@ fn update_block_context(
fn set_block_param<'rc>(
block: &mut BlockContext<'rc>,
h: &Helper<'rc>,
base_path: Option<&Vec<String>>,
base_path: Option<&Vec<LazyCompactString>>,
k: &Json,
v: &Json,
) -> Result<(), RenderError> {
Expand Down Expand Up @@ -100,7 +101,13 @@ impl HelperDef for EachHelper {
block.set_local_var("last", to_json(is_last));
block.set_local_var("index", index.clone());

update_block_context(block, array_path, i.to_string(), is_first, v);
update_block_context(
block,
array_path,
i.to_string().into(),
is_first,
v,
);
set_block_param(block, h, array_path, &index, v)?;
}

Expand Down Expand Up @@ -130,7 +137,13 @@ impl HelperDef for EachHelper {
block.set_local_var("last", to_json(is_last));
block.set_local_var("key", key.clone());

update_block_context(block, obj_path, k.to_string(), is_first, v);
update_block_context(
block,
obj_path,
k.to_string().into(),
is_first,
v,
);
set_block_param(block, h, obj_path, &key, v)?;
}

Expand Down
2 changes: 1 addition & 1 deletion src/helpers/helper_log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl HelperDef for LogHelper {
if let Ok(log_level) = Level::from_str(level) {
log!(log_level, "{}", param_to_log)
} else {
return Err(RenderError::new(&format!(
return Err(RenderError::new(format!(
"Unsupported logging level {}",
level
)));
Expand Down
25 changes: 13 additions & 12 deletions src/json/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ use std::iter::Peekable;

use pest::iterators::Pair;
use pest::Parser;
use smartstring::alias::String as LazyCompactString;

use crate::error::RenderError;
use crate::grammar::{HandlebarsParser, Rule};

#[derive(PartialEq, Eq, Clone, Debug)]
pub enum PathSeg {
Named(String),
Named(LazyCompactString),
Ruled(Rule),
}

Expand All @@ -18,16 +19,16 @@ pub enum PathSeg {
/// or a normal relative path like `a/b/c`.
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Path {
Relative((Vec<PathSeg>, String)),
Local((usize, String, String)),
Relative((Vec<PathSeg>, LazyCompactString)),
Local((usize, LazyCompactString, LazyCompactString)),
}

impl Path {
pub(crate) fn new(raw: &str, segs: Vec<PathSeg>) -> Path {
if let Some((level, name)) = get_local_path_and_level(&segs) {
Path::Local((level, name, raw.to_owned()))
Path::Local((level, name, raw.into()))
} else {
Path::Relative((segs, raw.to_owned()))
Path::Relative((segs, raw.into()))
}
}

Expand All @@ -49,16 +50,16 @@ impl Path {
}

pub(crate) fn current() -> Path {
Path::Relative((Vec::with_capacity(0), "".to_owned()))
Path::Relative((Vec::with_capacity(0), "".into()))
}

// for test only
pub(crate) fn with_named_paths(name_segs: &[&str]) -> Path {
let segs = name_segs
.iter()
.map(|n| PathSeg::Named((*n).to_string()))
.map(|n| PathSeg::Named((*n).into()))
.collect();
Path::Relative((segs, name_segs.join("/")))
Path::Relative((segs, name_segs.join("/").into()))
}

// for test only
Expand All @@ -70,7 +71,7 @@ impl Path {
}
}

fn get_local_path_and_level(paths: &[PathSeg]) -> Option<(usize, String)> {
fn get_local_path_and_level(paths: &[PathSeg]) -> Option<(usize, LazyCompactString)> {
paths.get(0).and_then(|seg| {
if seg == &PathSeg::Ruled(Rule::path_local) {
let mut level = 0;
Expand Down Expand Up @@ -112,7 +113,7 @@ where
Rule::path_id | Rule::path_raw_id => {
let name = n.as_str();
if name != "this" {
path_stack.push(PathSeg::Named(name.to_string()));
path_stack.push(PathSeg::Named(name.into()));
}
}
_ => {}
Expand All @@ -124,11 +125,11 @@ where
path_stack
}

pub(crate) fn merge_json_path(path_stack: &mut Vec<String>, relative_path: &[PathSeg]) {
pub(crate) fn merge_json_path(path_stack: &mut Vec<LazyCompactString>, relative_path: &[PathSeg]) {
for seg in relative_path {
match seg {
PathSeg::Named(ref s) => {
path_stack.push(s.to_owned());
path_stack.push(s.clone());
}
PathSeg::Ruled(Rule::path_root) => {}
PathSeg::Ruled(Rule::path_up) => {}
Expand Down
7 changes: 4 additions & 3 deletions src/json/value.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use serde::Serialize;
use serde_json::value::{to_value, Value as Json};
use smartstring::alias::String as LazyCompactString;

pub(crate) static DEFAULT_VALUE: Json = Json::Null;

Expand All @@ -14,7 +15,7 @@ pub enum ScopedJson<'rc> {
Constant(&'rc Json),
Derived(Json),
// represents a json reference to context value, its full path
Context(&'rc Json, Vec<String>),
Context(&'rc Json, Vec<LazyCompactString>),
Missing,
}

Expand Down Expand Up @@ -42,7 +43,7 @@ impl<'rc> ScopedJson<'rc> {
ScopedJson::Derived(v.clone())
}

pub fn context_path(&self) -> Option<&Vec<String>> {
pub fn context_path(&self) -> Option<&Vec<LazyCompactString>> {
match self {
ScopedJson::Context(_, ref p) => Some(p),
_ => None,
Expand Down Expand Up @@ -79,7 +80,7 @@ impl<'rc> PathAndJson<'rc> {
}

/// Returns full path to this value if any
pub fn context_path(&self) -> Option<&Vec<String>> {
pub fn context_path(&self) -> Option<&Vec<LazyCompactString>> {
self.value.context_path()
}

Expand Down
6 changes: 4 additions & 2 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use smartstring::alias::String as LazyCompactString;

#[inline]
pub(crate) fn copy_on_push_vec<T>(input: &[T], el: T) -> Vec<T>
where
Expand All @@ -10,8 +12,8 @@ where
}

#[inline]
pub(crate) fn extend(base: &mut Vec<String>, slice: &[String]) {
pub(crate) fn extend(base: &mut Vec<LazyCompactString>, slice: &[LazyCompactString]) {
for i in slice {
base.push(i.to_owned());
base.push(i.clone());
}
}