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

Drop the serde_json dependency #24

Open
wants to merge 4 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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
- stable
- beta
- nightly
- 1.36.0 # MSRV
- 1.46.0 # MSRV

steps:
- uses: actions/checkout@v2
Expand Down
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ maintenance = { status = "passively-maintained" }

[dependencies]
serde = "1.0"
serde_json = "1.0"

[dev-dependencies]
serde_derive = "1.0"
Expand Down
44 changes: 15 additions & 29 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
//! Module containing the error type returned by TinyTemplate if an error occurs.

use instruction::{path_to_str, PathSlice};
use serde_json::Error as SerdeJsonError;
use serde_json::Value;
use value::Value;

use std::error::Error as StdError;
use std::fmt;

/// An opaque type representing a serialization error.
#[derive(Debug)]
pub struct SerializationError(super::value::Error);

/// Enum representing the potential errors that TinyTemplate can encounter.
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
ParseError {
msg: String,
Expand All @@ -19,9 +24,7 @@ pub enum Error {
line: usize,
column: usize,
},
SerdeError {
err: SerdeJsonError,
},
SerializationError(SerializationError),
GenericError {
msg: String,
},
Expand All @@ -40,13 +43,10 @@ pub enum Error {
line: usize,
column: usize,
},

#[doc(hidden)]
__NonExhaustive,
}
impl From<SerdeJsonError> for Error {
fn from(err: SerdeJsonError) -> Error {
Error::SerdeError { err }
impl From<super::value::Error> for Error {
fn from(err: super::value::Error) -> Error {
Error::SerializationError(SerializationError(err))
}
}
impl From<fmt::Error> for Error {
Expand All @@ -69,8 +69,8 @@ impl fmt::Display for Error {
line, column, msg
)
}
Error::SerdeError { err } => {
write!(f, "Unexpected serde error while converting the context to a serde_json::Value. Error: {}", err)
Error::SerializationError(err) => {
write!(f, "Unexpected error during serialization. Error: {}", err.0)
}
Error::GenericError { msg } => {
write!(f, "{}", msg)
Expand Down Expand Up @@ -102,7 +102,6 @@ impl fmt::Display for Error {
name, line, column, err
)
}
Error::__NonExhaustive => unreachable!(),
}
}
}
Expand All @@ -111,12 +110,11 @@ impl StdError for Error {
match self {
Error::ParseError { .. } => "ParseError",
Error::RenderError { .. } => "RenderError",
Error::SerdeError { .. } => "SerdeError",
Error::GenericError { msg } => &msg,
Error::SerializationError { .. } => "SerializationError",
Error::GenericError { msg } => msg,
Error::StdFormatError { .. } => "StdFormatError",
Error::CalledTemplateError { .. } => "CalledTemplateError",
Error::CalledFormatterError { .. } => "CalledFormatterError",
Error::__NonExhaustive => unreachable!(),
}
}
}
Expand Down Expand Up @@ -153,18 +151,6 @@ pub(crate) fn lookup_error(source: &str, step: &str, path: PathSlice, current: &
}
}

pub(crate) fn truthiness_error(source: &str, path: PathSlice) -> Error {
let (line, column) = get_offset(source, path.last().unwrap());
Error::RenderError {
msg: format!(
"Path '{}' produced a value which could not be checked for truthiness.",
path_to_str(path)
),
line,
column,
}
}

pub(crate) fn unprintable_error() -> Error {
Error::GenericError {
msg: "Expected a printable value but found array or object.".to_string(),
Expand Down
33 changes: 21 additions & 12 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@
//!

extern crate serde;
extern crate serde_json;

#[cfg(test)]
#[cfg_attr(test, macro_use)]
Expand All @@ -74,13 +73,14 @@ pub mod error;
mod instruction;
pub mod syntax;
mod template;
mod value;

use error::*;
use serde::Serialize;
use serde_json::Value;
use std::collections::HashMap;
use std::fmt::Write;
use template::Template;
use value::Value;

/// Type alias for closures which can be used as value formatters.
pub type ValueFormatter = dyn Fn(&Value, &mut String) -> Result<()>;
Expand Down Expand Up @@ -119,20 +119,25 @@ pub fn escape(value: &str, output: &mut String) {
/// Values are formatted as follows:
///
/// * `Value::Null` => the empty string
/// * `Value::Bool` => true|false
/// * `Value::Number` => the number, as formatted by `serde_json`.
/// * `Value::Boolean` => true|false
/// * `Value::Integer` => the integer
/// * `Value::Float` => the float
/// * `Value::String` => the string, HTML-escaped
///
/// Arrays and objects are not formatted, and attempting to do so will result in a rendering error.
pub fn format(value: &Value, output: &mut String) -> Result<()> {
match value {
Value::Null => Ok(()),
Value::Bool(b) => {
Value::Boolean(b) => {
write!(output, "{}", b)?;
Ok(())
}
Value::Number(n) => {
write!(output, "{}", n)?;
Value::Integer(i) => {
write!(output, "{}", i)?;
Ok(())
}
Value::Float(f) => {
write!(output, "{}", f)?;
Ok(())
}
Value::String(s) => {
Expand All @@ -147,12 +152,16 @@ pub fn format(value: &Value, output: &mut String) -> Result<()> {
pub fn format_unescaped(value: &Value, output: &mut String) -> Result<()> {
match value {
Value::Null => Ok(()),
Value::Bool(b) => {
Value::Boolean(b) => {
write!(output, "{}", b)?;
Ok(())
}
Value::Number(n) => {
write!(output, "{}", n)?;
Value::Integer(i) => {
write!(output, "{}", i)?;
Ok(())
}
Value::Float(f) => {
write!(output, "{}", f)?;
Ok(())
}
Value::String(s) => {
Expand Down Expand Up @@ -208,12 +217,12 @@ impl<'template> TinyTemplate<'template> {
}

/// Render the template with the given name using the given context object. The context
/// object must implement `serde::Serialize` as it will be converted to `serde_json::Value`.
/// object must implement `serde::Serialize`.
pub fn render<C>(&self, template: &str, context: &C) -> Result<String>
where
C: Serialize,
{
let value = serde_json::to_value(context)?;
let value = Value::serialize_from(context)?;
match self.templates.get(template) {
Some(tmpl) => tmpl.render(
&value,
Expand Down
4 changes: 2 additions & 2 deletions src/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
//!
//! ### Context Types
//!
//! TinyTemplate uses `serde_json`'s Value structure to represent the context. Therefore, any
//! TinyTemplate uses a internal `Value` structure to represent the context. Therefore, any
//! `Serializable` structure can be used as a context. All values in such structures are mapped to
//! their JSON representations - booleans, numbers, strings, arrays, objects and nulls.
//! representations similar to JSON - booleans, integers, floats, strings, arrays, objects and nulls.
//!
//! ### Values
//!
Expand Down
58 changes: 30 additions & 28 deletions src/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ use compiler::TemplateCompiler;
use error::Error::*;
use error::*;
use instruction::{Instruction, PathSlice, PathStep};
use serde_json::Value;
use std::collections::HashMap;
use std::fmt::Write;
use std::slice;
use value::Value;
use ValueFormatter;

/// Enum defining the different kinds of records on the context stack.
Expand Down Expand Up @@ -64,17 +64,23 @@ impl<'render, 'template> RenderContext<'render, 'template> {
let mut current = object;
for step in path.iter() {
if let PathStep::Index(_, n) = step {
if let Some(next) = current.get(n) {
current = next;
continue;
if let Value::Array(array) = current {
if let Some(next) = array.get(*n) {
current = next;
continue;
}
}
}

let step: &str = &*step;

match current.get(step) {
Some(next) => current = next,
None => return Err(lookup_error(self.original_text, step, path, current)),
match &current {
Value::Object(map) => match map.get(step) {
Some(next) => current = next,
None => return Err(lookup_error(self.original_text, step, path, current)),
},

_ => return Err(lookup_error(self.original_text, step, path, current)),
}
}
Ok(current)
Expand Down Expand Up @@ -222,12 +228,12 @@ impl<'template> Template<'template> {
let (index, length) = render_context.lookup_index()?;
index == (length - 1)
}
"@root" => self.value_is_truthy(render_context.lookup_root()?, path)?,
"@root" => self.value_is_truthy(render_context.lookup_root()?),
other => panic!("Unknown keyword {}", other), // This should have been caught by the parser.
}
} else {
let value_to_render = render_context.lookup(path)?;
self.value_is_truthy(value_to_render, path)?
self.value_is_truthy(value_to_render)
};
if *negate {
truthy = !truthy;
Expand Down Expand Up @@ -325,21 +331,16 @@ impl<'template> Template<'template> {
Ok(())
}

fn value_is_truthy(&self, value: &Value, path: PathSlice) -> Result<bool> {
let truthy = match value {
fn value_is_truthy(&self, value: &Value) -> bool {
match value {
Value::Null => false,
Value::Bool(b) => *b,
Value::Number(n) => match n.as_f64() {
Some(float) => float != 0.0,
None => {
return Err(truthiness_error(self.original_text, path));
}
},
Value::Boolean(b) => *b,
Value::Integer(i) => *i != 0,
Value::Float(f) => *f != 0.0,
Value::String(s) => !s.is_empty(),
Value::Array(arr) => !arr.is_empty(),
Value::Object(_) => true,
};
Ok(truthy)
}
}
}

Expand Down Expand Up @@ -382,7 +383,8 @@ mod test {
nested: NestedContext { value: 10 },
escapes: "1:< 2:> 3:& 4:' 5:\"",
};
::serde_json::to_value(&ctx).unwrap()

Value::serialize_from(&ctx).unwrap()
}

fn other_templates() -> HashMap<&'static str, Template<'static>> {
Expand Down Expand Up @@ -807,7 +809,7 @@ mod test {
fn test_root_print() {
let template = compile("{ @root }");
let context = "Hello World!";
let context = ::serde_json::to_value(&context).unwrap();
let context = Value::serialize_from(&context).unwrap();
let template_registry = other_templates();
let formatter_registry = formatters();
let string = template
Expand All @@ -825,7 +827,7 @@ mod test {
fn test_root_branch() {
let template = compile("{{ if @root }}Hello World!{{ endif }}");
let context = true;
let context = ::serde_json::to_value(&context).unwrap();
let context = Value::serialize_from(&context).unwrap();
let template_registry = other_templates();
let formatter_registry = formatters();
let string = template
Expand All @@ -843,7 +845,7 @@ mod test {
fn test_root_iterate() {
let template = compile("{{ for a in @root }}{ a }{{ endfor }}");
let context = vec!["foo", "bar"];
let context = ::serde_json::to_value(&context).unwrap();
let context = Value::serialize_from(&context).unwrap();
let template_registry = other_templates();
let formatter_registry = formatters();
let string = template
Expand All @@ -861,7 +863,7 @@ mod test {
fn test_number_truthiness_zero() {
let template = compile("{{ if @root }}truthy{{else}}not truthy{{ endif }}");
let context = 0;
let context = ::serde_json::to_value(&context).unwrap();
let context = Value::serialize_from(&context).unwrap();
let template_registry = other_templates();
let formatter_registry = formatters();
let string = template
Expand All @@ -879,7 +881,7 @@ mod test {
fn test_number_truthiness_one() {
let template = compile("{{ if @root }}truthy{{else}}not truthy{{ endif }}");
let context = 1;
let context = ::serde_json::to_value(&context).unwrap();
let context = Value::serialize_from(&context).unwrap();
let template_registry = other_templates();
let formatter_registry = formatters();
let string = template
Expand All @@ -902,7 +904,7 @@ mod test {

let template = compile("{ foo.1 }{ foo.0 }");
let context = Context { foo: (123, 456) };
let context = ::serde_json::to_value(&context).unwrap();
let context = Value::serialize_from(&context).unwrap();
let template_registry = other_templates();
let formatter_registry = formatters();
let string = template
Expand All @@ -928,7 +930,7 @@ mod test {
foo.insert("0", 123);
foo.insert("1", 456);
let context = Context { foo };
let context = ::serde_json::to_value(&context).unwrap();
let context = Value::serialize_from(&context).unwrap();
let template_registry = other_templates();
let formatter_registry = formatters();
let string = template
Expand Down
Loading