Skip to content

Commit 430f106

Browse files
committed
Drop the serde_json dependency
This implements an internal `Value` type which functions like `serde_json::Value`. Note that because this commit changes the `Error` type, this is an API-breaking change. Resolves: #1 Signed-off-by: Nathaniel McCallum <[email protected]>
1 parent 2d9ae14 commit 430f106

File tree

6 files changed

+569
-66
lines changed

6 files changed

+569
-66
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ maintenance = { status = "passively-maintained" }
1616

1717
[dependencies]
1818
serde = "1.0"
19-
serde_json = "1.0"
2019

2120
[dev-dependencies]
2221
serde_derive = "1.0"

src/error.rs

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
//! Module containing the error type returned by TinyTemplate if an error occurs.
22
33
use instruction::{path_to_str, PathSlice};
4-
use serde_json::Error as SerdeJsonError;
5-
use serde_json::Value;
4+
use value::Value;
5+
66
use std::error::Error as StdError;
77
use std::fmt;
88

9+
/// An opaque type representing a serialization error.
10+
#[derive(Debug)]
11+
pub struct SerializationError(super::value::Error);
12+
913
/// Enum representing the potential errors that TinyTemplate can encounter.
1014
#[derive(Debug)]
1115
#[non_exhaustive]
@@ -20,9 +24,7 @@ pub enum Error {
2024
line: usize,
2125
column: usize,
2226
},
23-
SerdeError {
24-
err: SerdeJsonError,
25-
},
27+
SerializationError(SerializationError),
2628
GenericError {
2729
msg: String,
2830
},
@@ -42,9 +44,9 @@ pub enum Error {
4244
column: usize,
4345
},
4446
}
45-
impl From<SerdeJsonError> for Error {
46-
fn from(err: SerdeJsonError) -> Error {
47-
Error::SerdeError { err }
47+
impl From<super::value::Error> for Error {
48+
fn from(err: super::value::Error) -> Error {
49+
Error::SerializationError(SerializationError(err))
4850
}
4951
}
5052
impl From<fmt::Error> for Error {
@@ -67,8 +69,8 @@ impl fmt::Display for Error {
6769
line, column, msg
6870
)
6971
}
70-
Error::SerdeError { err } => {
71-
write!(f, "Unexpected serde error while converting the context to a serde_json::Value. Error: {}", err)
72+
Error::SerializationError(err) => {
73+
write!(f, "Unexpected error during serialization. Error: {}", err.0)
7274
}
7375
Error::GenericError { msg } => {
7476
write!(f, "{}", msg)
@@ -108,7 +110,7 @@ impl StdError for Error {
108110
match self {
109111
Error::ParseError { .. } => "ParseError",
110112
Error::RenderError { .. } => "RenderError",
111-
Error::SerdeError { .. } => "SerdeError",
113+
Error::SerializationError { .. } => "SerializationError",
112114
Error::GenericError { msg } => &msg,
113115
Error::StdFormatError { .. } => "StdFormatError",
114116
Error::CalledTemplateError { .. } => "CalledTemplateError",
@@ -149,18 +151,6 @@ pub(crate) fn lookup_error(source: &str, step: &str, path: PathSlice, current: &
149151
}
150152
}
151153

152-
pub(crate) fn truthiness_error(source: &str, path: PathSlice) -> Error {
153-
let (line, column) = get_offset(source, path.last().unwrap());
154-
Error::RenderError {
155-
msg: format!(
156-
"Path '{}' produced a value which could not be checked for truthiness.",
157-
path_to_str(path)
158-
),
159-
line,
160-
column,
161-
}
162-
}
163-
164154
pub(crate) fn unprintable_error() -> Error {
165155
Error::GenericError {
166156
msg: "Expected a printable value but found array or object.".to_string(),

src/lib.rs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@
6363
//!
6464
6565
extern crate serde;
66-
extern crate serde_json;
6766

6867
#[cfg(test)]
6968
#[cfg_attr(test, macro_use)]
@@ -74,13 +73,14 @@ pub mod error;
7473
mod instruction;
7574
pub mod syntax;
7675
mod template;
76+
mod value;
7777

7878
use error::*;
7979
use serde::Serialize;
80-
use serde_json::Value;
8180
use std::collections::HashMap;
8281
use std::fmt::Write;
8382
use template::Template;
83+
use value::Value;
8484

8585
/// Type alias for closures which can be used as value formatters.
8686
pub type ValueFormatter = dyn Fn(&Value, &mut String) -> Result<()>;
@@ -119,20 +119,25 @@ pub fn escape(value: &str, output: &mut String) {
119119
/// Values are formatted as follows:
120120
///
121121
/// * `Value::Null` => the empty string
122-
/// * `Value::Bool` => true|false
123-
/// * `Value::Number` => the number, as formatted by `serde_json`.
122+
/// * `Value::Boolean` => true|false
123+
/// * `Value::Integer` => the integer
124+
/// * `Value::Float` => the float
124125
/// * `Value::String` => the string, HTML-escaped
125126
///
126127
/// Arrays and objects are not formatted, and attempting to do so will result in a rendering error.
127128
pub fn format(value: &Value, output: &mut String) -> Result<()> {
128129
match value {
129130
Value::Null => Ok(()),
130-
Value::Bool(b) => {
131+
Value::Boolean(b) => {
131132
write!(output, "{}", b)?;
132133
Ok(())
133134
}
134-
Value::Number(n) => {
135-
write!(output, "{}", n)?;
135+
Value::Integer(i) => {
136+
write!(output, "{}", i)?;
137+
Ok(())
138+
}
139+
Value::Float(f) => {
140+
write!(output, "{}", f)?;
136141
Ok(())
137142
}
138143
Value::String(s) => {
@@ -147,12 +152,16 @@ pub fn format(value: &Value, output: &mut String) -> Result<()> {
147152
pub fn format_unescaped(value: &Value, output: &mut String) -> Result<()> {
148153
match value {
149154
Value::Null => Ok(()),
150-
Value::Bool(b) => {
155+
Value::Boolean(b) => {
151156
write!(output, "{}", b)?;
152157
Ok(())
153158
}
154-
Value::Number(n) => {
155-
write!(output, "{}", n)?;
159+
Value::Integer(i) => {
160+
write!(output, "{}", i)?;
161+
Ok(())
162+
}
163+
Value::Float(f) => {
164+
write!(output, "{}", f)?;
156165
Ok(())
157166
}
158167
Value::String(s) => {
@@ -208,12 +217,12 @@ impl<'template> TinyTemplate<'template> {
208217
}
209218

210219
/// Render the template with the given name using the given context object. The context
211-
/// object must implement `serde::Serialize` as it will be converted to `serde_json::Value`.
220+
/// object must implement `serde::Serialize`.
212221
pub fn render<C>(&self, template: &str, context: &C) -> Result<String>
213222
where
214223
C: Serialize,
215224
{
216-
let value = serde_json::to_value(context)?;
225+
let value = Value::serialize_from(context)?;
217226
match self.templates.get(template) {
218227
Some(tmpl) => tmpl.render(
219228
&value,

src/syntax.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
//!
33
//! ### Context Types
44
//!
5-
//! TinyTemplate uses `serde_json`'s Value structure to represent the context. Therefore, any
5+
//! TinyTemplate uses a internal `Value` structure to represent the context. Therefore, any
66
//! `Serializable` structure can be used as a context. All values in such structures are mapped to
7-
//! their JSON representations - booleans, numbers, strings, arrays, objects and nulls.
7+
//! representations similar to JSON - booleans, integers, floats, strings, arrays, objects and nulls.
88
//!
99
//! ### Values
1010
//!

src/template.rs

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ use compiler::TemplateCompiler;
44
use error::Error::*;
55
use error::*;
66
use instruction::{Instruction, PathSlice, PathStep};
7-
use serde_json::Value;
87
use std::collections::HashMap;
98
use std::fmt::Write;
109
use std::slice;
10+
use value::Value;
1111
use ValueFormatter;
1212

1313
/// Enum defining the different kinds of records on the context stack.
@@ -64,17 +64,23 @@ impl<'render, 'template> RenderContext<'render, 'template> {
6464
let mut current = object;
6565
for step in path.iter() {
6666
if let PathStep::Index(_, n) = step {
67-
if let Some(next) = current.get(n) {
68-
current = next;
69-
continue;
67+
if let Value::Array(array) = current {
68+
if let Some(next) = array.get(*n) {
69+
current = next;
70+
continue;
71+
}
7072
}
7173
}
7274

7375
let step: &str = &*step;
7476

75-
match current.get(step) {
76-
Some(next) => current = next,
77-
None => return Err(lookup_error(self.original_text, step, path, current)),
77+
match &current {
78+
Value::Object(map) => match map.get(step) {
79+
Some(next) => current = next,
80+
None => return Err(lookup_error(self.original_text, step, path, current)),
81+
},
82+
83+
_ => return Err(lookup_error(self.original_text, step, path, current)),
7884
}
7985
}
8086
Ok(current)
@@ -222,12 +228,12 @@ impl<'template> Template<'template> {
222228
let (index, length) = render_context.lookup_index()?;
223229
index == (length - 1)
224230
}
225-
"@root" => self.value_is_truthy(render_context.lookup_root()?, path)?,
231+
"@root" => self.value_is_truthy(render_context.lookup_root()?),
226232
other => panic!("Unknown keyword {}", other), // This should have been caught by the parser.
227233
}
228234
} else {
229235
let value_to_render = render_context.lookup(path)?;
230-
self.value_is_truthy(value_to_render, path)?
236+
self.value_is_truthy(value_to_render)
231237
};
232238
if *negate {
233239
truthy = !truthy;
@@ -325,21 +331,16 @@ impl<'template> Template<'template> {
325331
Ok(())
326332
}
327333

328-
fn value_is_truthy(&self, value: &Value, path: PathSlice) -> Result<bool> {
329-
let truthy = match value {
334+
fn value_is_truthy(&self, value: &Value) -> bool {
335+
match value {
330336
Value::Null => false,
331-
Value::Bool(b) => *b,
332-
Value::Number(n) => match n.as_f64() {
333-
Some(float) => float != 0.0,
334-
None => {
335-
return Err(truthiness_error(self.original_text, path));
336-
}
337-
},
337+
Value::Boolean(b) => *b,
338+
Value::Integer(i) => *i != 0,
339+
Value::Float(f) => *f != 0.0,
338340
Value::String(s) => !s.is_empty(),
339341
Value::Array(arr) => !arr.is_empty(),
340342
Value::Object(_) => true,
341-
};
342-
Ok(truthy)
343+
}
343344
}
344345
}
345346

@@ -382,7 +383,8 @@ mod test {
382383
nested: NestedContext { value: 10 },
383384
escapes: "1:< 2:> 3:& 4:' 5:\"",
384385
};
385-
::serde_json::to_value(&ctx).unwrap()
386+
387+
Value::serialize_from(&ctx).unwrap()
386388
}
387389

388390
fn other_templates() -> HashMap<&'static str, Template<'static>> {
@@ -807,7 +809,7 @@ mod test {
807809
fn test_root_print() {
808810
let template = compile("{ @root }");
809811
let context = "Hello World!";
810-
let context = ::serde_json::to_value(&context).unwrap();
812+
let context = Value::serialize_from(&context).unwrap();
811813
let template_registry = other_templates();
812814
let formatter_registry = formatters();
813815
let string = template
@@ -825,7 +827,7 @@ mod test {
825827
fn test_root_branch() {
826828
let template = compile("{{ if @root }}Hello World!{{ endif }}");
827829
let context = true;
828-
let context = ::serde_json::to_value(&context).unwrap();
830+
let context = Value::serialize_from(&context).unwrap();
829831
let template_registry = other_templates();
830832
let formatter_registry = formatters();
831833
let string = template
@@ -843,7 +845,7 @@ mod test {
843845
fn test_root_iterate() {
844846
let template = compile("{{ for a in @root }}{ a }{{ endfor }}");
845847
let context = vec!["foo", "bar"];
846-
let context = ::serde_json::to_value(&context).unwrap();
848+
let context = Value::serialize_from(&context).unwrap();
847849
let template_registry = other_templates();
848850
let formatter_registry = formatters();
849851
let string = template
@@ -861,7 +863,7 @@ mod test {
861863
fn test_number_truthiness_zero() {
862864
let template = compile("{{ if @root }}truthy{{else}}not truthy{{ endif }}");
863865
let context = 0;
864-
let context = ::serde_json::to_value(&context).unwrap();
866+
let context = Value::serialize_from(&context).unwrap();
865867
let template_registry = other_templates();
866868
let formatter_registry = formatters();
867869
let string = template
@@ -879,7 +881,7 @@ mod test {
879881
fn test_number_truthiness_one() {
880882
let template = compile("{{ if @root }}truthy{{else}}not truthy{{ endif }}");
881883
let context = 1;
882-
let context = ::serde_json::to_value(&context).unwrap();
884+
let context = Value::serialize_from(&context).unwrap();
883885
let template_registry = other_templates();
884886
let formatter_registry = formatters();
885887
let string = template
@@ -902,7 +904,7 @@ mod test {
902904

903905
let template = compile("{ foo.1 }{ foo.0 }");
904906
let context = Context { foo: (123, 456) };
905-
let context = ::serde_json::to_value(&context).unwrap();
907+
let context = Value::serialize_from(&context).unwrap();
906908
let template_registry = other_templates();
907909
let formatter_registry = formatters();
908910
let string = template
@@ -928,7 +930,7 @@ mod test {
928930
foo.insert("0", 123);
929931
foo.insert("1", 456);
930932
let context = Context { foo };
931-
let context = ::serde_json::to_value(&context).unwrap();
933+
let context = Value::serialize_from(&context).unwrap();
932934
let template_registry = other_templates();
933935
let formatter_registry = formatters();
934936
let string = template

0 commit comments

Comments
 (0)