diff options
author | Dirkjan Ochtman <dirkjan@ochtman.nl> | 2017-09-07 20:42:55 +0200 |
---|---|---|
committer | Dirkjan Ochtman <dirkjan@ochtman.nl> | 2017-09-07 20:42:55 +0200 |
commit | 36b06915177ccc8da1cdefa46fdde7ff44938ef8 (patch) | |
tree | 4b524e287223e7be41323861436673767bb89215 /askama_shared/src/generator.rs | |
parent | 34fcbf028aff02e02f0e9cfc9229a9f583d5bbad (diff) | |
download | askama-36b06915177ccc8da1cdefa46fdde7ff44938ef8.tar.gz askama-36b06915177ccc8da1cdefa46fdde7ff44938ef8.tar.bz2 askama-36b06915177ccc8da1cdefa46fdde7ff44938ef8.zip |
Reorder methods in Generator to aid readability
Diffstat (limited to 'askama_shared/src/generator.rs')
-rw-r--r-- | askama_shared/src/generator.rs | 900 |
1 files changed, 450 insertions, 450 deletions
diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs index 9ac6964..9ccbbbd 100644 --- a/askama_shared/src/generator.rs +++ b/askama_shared/src/generator.rs @@ -126,295 +126,260 @@ impl<'a> Generator<'a> { Self::new(locals, self.indent) } - /* Helper methods for writing to internal buffer */ - - fn indent(&mut self) { - self.indent += 1; - } - - fn dedent(&mut self) { - self.indent -= 1; - } - - fn write(&mut self, s: &str) { - if self.start { - for _ in 0..(self.indent * 4) { - self.buf.push(' '); + fn result(mut self, state: &'a State) -> String { + if !state.blocks.is_empty() { + if !state.derived { + self.define_trait(state); + } else { + let parent_type = get_parent_type(state.input.ast) + .expect("expected field '_parent' in extending template struct"); + self.deref_to_parent(state, parent_type); } - self.start = false; - } - self.buf.push_str(s); - } - fn writeln(&mut self, s: &str) { - if s.is_empty() { - return; + let trait_nodes = if !state.derived { Some(&state.nodes[..]) } else { None }; + self.impl_trait(state, trait_nodes); + self.impl_template_for_trait(state); + } else { + self.impl_template(state); } - if s == "}" { - self.dedent(); + self.impl_display(state); + if cfg!(feature = "iron") { + self.impl_modifier_response(state); } - self.write(s); - if s.ends_with('{') { - self.indent(); + if cfg!(feature = "rocket") { + self.impl_responder(state); } - self.buf.push('\n'); - self.start = true; + self.buf } - /* Helper methods for dealing with whitespace nodes */ - - fn flush_ws(&mut self, ws: &WS) { - if self.next_ws.is_some() && !ws.0 { - let val = self.next_ws.unwrap(); - if !val.is_empty() { - self.writeln(&format!("writer.write_str({:#?})?;", - val)); - } - } - self.next_ws = None; + // Implement `Template` for the given context struct. + fn impl_template(&mut self, state: &'a State) { + self.write_header(state, "::askama::Template", &[]); + self.writeln("fn render_into(&self, writer: &mut ::std::fmt::Write) -> \ + ::askama::Result<()> {"); + self.handle(state, state.nodes, AstLevel::Top); + self.flush_ws(&WS(false, false)); + self.writeln("Ok(())"); + self.writeln("}"); + self.writeln("}"); } - fn prepare_ws(&mut self, ws: &WS) { - self.skip_ws = ws.1; + // Implement `Display` for the given context struct. + fn impl_display(&mut self, state: &'a State) { + self.write_header(state, "::std::fmt::Display", &[]); + self.writeln("fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {"); + self.writeln("self.render_into(f).map_err(|_| ::std::fmt::Error {})"); + self.writeln("}"); + self.writeln("}"); } - fn handle_ws(&mut self, ws: &WS) { - self.flush_ws(ws); - self.prepare_ws(ws); + // Implement `Deref<Parent>` for an inheriting context struct. + fn deref_to_parent(&mut self, state: &'a State, parent_type: &syn::Ty) { + self.write_header(state, "::std::ops::Deref", &[]); + let mut tokens = Tokens::new(); + parent_type.to_tokens(&mut tokens); + self.writeln(&format!("type Target = {};", tokens.as_str())); + self.writeln("fn deref(&self) -> &Self::Target {"); + self.writeln("&self._parent"); + self.writeln("}"); + self.writeln("}"); } - /* Visitor methods for expression types */ - - fn visit_num_lit(&mut self, s: &str) -> DisplayWrap { - self.write(s); - DisplayWrap::Unwrapped - } + // Implement `TraitFromPathName` for the given context struct. + fn impl_trait(&mut self, state: &'a State, nodes: Option<&'a [Node]>) { + self.write_header(state, &state.trait_name, &[]); + self.write_block_defs(state); - fn visit_str_lit(&mut self, s: &str) -> DisplayWrap { - self.write(&format!("\"{}\"", s)); - DisplayWrap::Unwrapped - } + self.writeln("#[allow(unused_variables)]"); + self.writeln(&format!( + "fn render_trait_into(&self, timpl: &{}, writer: &mut ::std::fmt::Write) \ + -> ::askama::Result<()> {{", + state.trait_name)); - fn visit_var(&mut self, s: &str) -> DisplayWrap { - if self.locals.contains(s) { - self.write(s); + if let Some(nodes) = nodes { + self.handle(state, nodes, AstLevel::Top); + self.flush_ws(&WS(false, false)); } else { - self.write(&format!("self.{}", s)); + self.writeln("self._parent.render_trait_into(self, writer)?;"); } - DisplayWrap::Unwrapped - } - fn visit_attr(&mut self, obj: &Expr, attr: &str) -> DisplayWrap { - if let Expr::Var(name) = *obj { - if name == "loop" { - self.write("_loop_index"); - if attr == "index" { - self.write(" + 1"); - return DisplayWrap::Unwrapped; - } else if attr == "index0" { - return DisplayWrap::Unwrapped; - } else { - panic!("unknown loop variable"); - } - } - } - self.visit_expr(obj); - self.write(&format!(".{}", attr)); - DisplayWrap::Unwrapped + self.writeln("Ok(())"); + self.writeln("}"); + self.flush_ws(&WS(false, false)); + self.writeln("}"); } - fn _visit_filter_args(&mut self, args: &[Expr]) { - for (i, arg) in args.iter().enumerate() { - if i > 0 { - self.write(", &"); - } - self.visit_expr(arg); + // Implement `Template` for templates that implement a template trait. + fn impl_template_for_trait(&mut self, state: &'a State) { + self.write_header(state, "::askama::Template", &[]); + self.writeln("fn render_into(&self, writer: &mut ::std::fmt::Write) \ + -> ::askama::Result<()> {"); + if state.derived { + self.writeln("self._parent.render_trait_into(self, writer)?;"); + } else { + self.writeln("self.render_trait_into(self, writer)?;"); } + self.writeln("Ok(())"); + self.writeln("}"); + self.writeln("}"); } - fn _visit_format_filter(&mut self, args: &[Expr]) { - self.write("format!("); - self._visit_filter_args(args); - self.write(")"); + // Defines the `TraitFromPathName` trait. + fn define_trait(&mut self, state: &'a State) { + self.writeln(&format!("trait {} {{", state.trait_name)); + self.write_block_defs(state); + self.writeln(&format!( + "fn render_trait_into(&self, timpl: &{}, writer: &mut ::std::fmt::Write) \ + -> ::askama::Result<()>;", + state.trait_name)); + self.writeln("}"); } - // Force type coercion on first argument to `join` filter (see #39). - fn _visit_join_filter(&mut self, args: &[Expr]) { - self.write("::askama::filters::join((&"); - for (i, arg) in args.iter().enumerate() { - if i > 0 { - self.write(", &"); - } - self.visit_expr(arg); - if i == 0 { - self.write(").into_iter()"); - } - } - self.write(")?"); + // Implement iron's Modifier<Response> if enabled + fn impl_modifier_response(&mut self, state: &'a State) { + self.write_header(state, "::askama::iron::Modifier<::askama::iron::Response>", &[]); + self.writeln("fn modify(self, res: &mut ::askama::iron::Response) {"); + self.writeln("res.body = Some(Box::new(self.render().unwrap().into_bytes()));"); + self.writeln("}"); + self.writeln("}"); } - fn visit_filter(&mut self, name: &str, args: &[Expr]) -> DisplayWrap { - if name == "format" { - self._visit_format_filter(args); - return DisplayWrap::Unwrapped; - } else if name == "join" { - self._visit_join_filter(args); - return DisplayWrap::Unwrapped; - } - - if filters::BUILT_IN_FILTERS.contains(&name) { - self.write(&format!("::askama::filters::{}(&", name)); - } else { - self.write(&format!("filters::{}(&", name)); - } + // Implement Rocket's `Responder`. + fn impl_responder(&mut self, state: &'a State) { + self.write_header(state, "::askama::rocket::Responder<'r>", &["'r"]); + self.writeln("fn respond_to(self, _: &::askama::rocket::Request) \ + -> ::askama::rocket::Result<'r> {"); - self._visit_filter_args(args); - self.write(")?"); - if name == "safe" || name == "escape" || name == "e" || name == "json" { - DisplayWrap::Wrapped - } else { - DisplayWrap::Unwrapped - } - } + let ext = match state.input.path.extension() { + Some(s) => s.to_str().unwrap(), + None => "txt", + }; + self.writeln(&format!("::askama::rocket::respond(&self, {:?})", ext)); - fn visit_binop(&mut self, op: &str, left: &Expr, right: &Expr) -> DisplayWrap { - self.visit_expr(left); - self.write(&format!(" {} ", op)); - self.visit_expr(right); - DisplayWrap::Unwrapped + self.dedent(); + self.writeln("}"); + self.writeln("}"); } - fn visit_group(&mut self, inner: &Expr) -> DisplayWrap { - self.write("("); - self.visit_expr(inner); - self.write(")"); - DisplayWrap::Unwrapped - } + // Writes header for the `impl` for `TraitFromPathName` or `Template` + // for the given context struct. + fn write_header(&mut self, state: &'a State, target: &str, extra_anno: &[&str]) { + let mut full_anno = Tokens::new(); + let mut orig_anno = Tokens::new(); + let need_anno = !state.input.ast.generics.lifetimes.is_empty() || + !state.input.ast.generics.ty_params.is_empty() || + !extra_anno.is_empty(); + if need_anno { + full_anno.append("<"); + orig_anno.append("<"); + } - fn visit_method_call(&mut self, obj: &Expr, method: &str, args: &[Expr]) -> DisplayWrap { - self.visit_expr(obj); - self.write(&format!(".{}(", method)); - for (i, arg) in args.iter().enumerate() { - if i > 0 { - self.write(", "); + let (mut full_sep, mut orig_sep) = (false, false); + for lt in &state.input.ast.generics.lifetimes { + if full_sep { + full_anno.append(","); } - self.visit_expr(arg); + if orig_sep { + orig_anno.append(","); + } + lt.to_tokens(&mut full_anno); + lt.to_tokens(&mut orig_anno); + full_sep = true; + orig_sep = true; } - self.write(")"); - DisplayWrap::Unwrapped - } - fn visit_expr(&mut self, expr: &Expr) -> DisplayWrap { - match *expr { - Expr::NumLit(s) => self.visit_num_lit(s), - Expr::StrLit(s) => self.visit_str_lit(s), - Expr::Var(s) => self.visit_var(s), - Expr::Attr(ref obj, name) => self.visit_attr(obj, name), - Expr::Filter(name, ref args) => self.visit_filter(name, args), - Expr::BinOp(op, ref left, ref right) => - self.visit_binop(op, left, right), - Expr::Group(ref inner) => self.visit_group(inner), - Expr::MethodCall(ref obj, method, ref args) => - self.visit_method_call(obj, method, args), + for anno in extra_anno { + if full_sep { + full_anno.append(","); + } + full_anno.append(anno); + full_sep = true; } - } - fn visit_target_single<'t>(&mut self, name: &'t str) -> Vec<&'t str> { - vec![name] - } + for param in &state.input.ast.generics.ty_params { + if full_sep { + full_anno.append(","); + } + if orig_sep { + orig_anno.append(","); + } + let mut impl_param = param.clone(); + impl_param.default = None; + impl_param.to_tokens(&mut full_anno); + param.ident.to_tokens(&mut orig_anno); + full_sep = true; + orig_sep = true; + } - fn visit_target<'t>(&mut self, target: &'t Target) -> Vec<&'t str> { - match *target { - Target::Name(s) => { self.visit_target_single(s) }, + if need_anno { + full_anno.append(">"); + orig_anno.append(">"); } + + let mut where_clause = Tokens::new(); + state.input.ast.generics.where_clause.to_tokens(&mut where_clause); + self.writeln(&format!("impl{} {} for {}{}{} {{", + full_anno.as_str(), target, state.input.ast.ident.as_ref(), + orig_anno.as_str(), where_clause.as_str())); } /* Helper methods for handling node types */ - fn write_lit(&mut self, lws: &'a str, val: &str, rws: &'a str) { - assert!(self.next_ws.is_none()); - if !lws.is_empty() { - if self.skip_ws { - self.skip_ws = false; - } else if val.is_empty() { - assert!(rws.is_empty()); - self.next_ws = Some(lws); - } else { - self.writeln(&format!("writer.write_str({:#?})?;", - lws)); + fn handle(&mut self, state: &'a State, nodes: &'a [Node], level: AstLevel) { + for n in nodes { + match *n { + Node::Lit(lws, val, rws) => { self.write_lit(lws, val, rws); } + Node::Comment() => {}, + Node::Expr(ref ws, ref val) => { self.write_expr(state, ws, val); }, + Node::LetDecl(ref ws, ref var) => { self.write_let_decl(ws, var); }, + Node::Let(ref ws, ref var, ref val) => { self.write_let(ws, var, val); }, + Node::Cond(ref conds, ref ws) => { + self.write_cond(state, conds, ws); + }, + Node::Loop(ref ws1, ref var, ref iter, ref body, ref ws2) => { + self.write_loop(state, ws1, var, iter, body, ws2); + }, + Node::BlockDef(ref ws1, name, _, ref ws2) => { + if let AstLevel::Nested = level { + panic!("blocks ('{}') are only allowed at the top level", name); + } + self.write_block(ws1, name, ws2); + }, + Node::Include(ref ws, path) => { + self.handle_include(state, ws, path); + }, + Node::Call(ref ws, name, ref args) => self.write_call(state, ws, name, args), + Node::Macro(_, _) | + Node::Extends(_) => { + if let AstLevel::Nested = level { + panic!("macro or extend blocks only allowed at the top level"); + } + }, } } - if !val.is_empty() { - self.writeln(&format!("writer.write_str({:#?})?;", val)); - } - if !rws.is_empty() { - self.next_ws = Some(rws); - } - } - - fn write_expr(&mut self, state: &'a State, ws: &WS, s: &Expr) { - self.handle_ws(ws); - self.write("let askama_expr = &"); - let wrapped = self.visit_expr(s); - self.writeln(";"); - - use self::DisplayWrap::*; - use super::input::EscapeMode::*; - self.write("writer.write_fmt(format_args!(\"{}\", "); - self.write(match (wrapped, &state.input.meta.escaping) { - (Wrapped, &Html) | - (Wrapped, &None) | - (Unwrapped, &None) => "askama_expr", - (Unwrapped, &Html) => "&::askama::MarkupDisplay::from(askama_expr)", - }); - self.writeln("))?;"); } - fn write_call(&mut self, state: &'a State, ws: &WS, name: &str, args: &[Expr]) { - let def = state.macros.get(name).expect(&format!("macro '{}' not found", name)); - self.handle_ws(ws); - self.locals.push(); - self.writeln("{"); - self.prepare_ws(&def.ws1); - for (i, arg) in def.args.iter().enumerate() { - self.write(&format!("let {} = &", arg)); - self.locals.insert(arg); - self.visit_expr(args.get(i) - .expect(&format!("macro '{}' takes more than {} arguments", name, i))); - self.writeln(";"); - } - self.handle(state, &def.nodes, AstLevel::Nested); - self.flush_ws(&def.ws2); - self.writeln("}"); - self.locals.pop(); - } + fn write_block_defs(&mut self, state: &'a State) { + for b in &state.blocks { + if let Node::BlockDef(ref ws1, name, ref nodes, ref ws2) = **b { + self.writeln("#[allow(unused_variables)]"); + self.writeln(&format!( + "fn render_block_{}_into(&self, writer: &mut ::std::fmt::Write) \ + -> ::askama::Result<()> {{", + name)); + self.prepare_ws(ws1); - fn write_let_decl(&mut self, ws: &WS, var: &'a Target) { - self.handle_ws(ws); - self.write("let "); - match *var { - Target::Name(name) => { - self.locals.insert(name); - self.write(name); - }, - } - self.writeln(";"); - } + self.locals.push(); + self.handle(state, nodes, AstLevel::Nested); + self.locals.pop(); - fn write_let(&mut self, ws: &WS, var: &'a Target, val: &Expr) { - self.handle_ws(ws); - match *var { - Target::Name(name) => { - if !self.locals.contains(name) { - self.write("let "); - self.locals.insert(name); - } - self.write(name); - }, + self.flush_ws(ws2); + self.writeln("Ok(())"); + self.writeln("}"); + } else { + panic!("only block definitions allowed here"); + } } - self.write(" = "); - self.visit_expr(val); - self.writeln(";"); } fn write_cond(&mut self, state: &'a State, conds: &'a [Cond], ws: &WS) { @@ -464,33 +429,23 @@ impl<'a> Generator<'a> { self.locals.pop(); } - fn write_block(&mut self, ws1: &WS, name: &str, ws2: &WS) { - self.flush_ws(ws1); - self.writeln(&format!("timpl.render_block_{}_into(writer)?;", name)); - self.prepare_ws(ws2); - } - - fn write_block_defs(&mut self, state: &'a State) { - for b in &state.blocks { - if let Node::BlockDef(ref ws1, name, ref nodes, ref ws2) = **b { - self.writeln("#[allow(unused_variables)]"); - self.writeln(&format!( - "fn render_block_{}_into(&self, writer: &mut ::std::fmt::Write) \ - -> ::askama::Result<()> {{", - name)); - self.prepare_ws(ws1); - - self.locals.push(); - self.handle(state, nodes, AstLevel::Nested); - self.locals.pop(); - - self.flush_ws(ws2); - self.writeln("Ok(())"); - self.writeln("}"); - } else { - panic!("only block definitions allowed here"); - } + fn write_call(&mut self, state: &'a State, ws: &WS, name: &str, args: &[Expr]) { + let def = state.macros.get(name).expect(&format!("macro '{}' not found", name)); + self.handle_ws(ws); + self.locals.push(); + self.writeln("{"); + self.prepare_ws(&def.ws1); + for (i, arg) in def.args.iter().enumerate() { + self.write(&format!("let {} = &", arg)); + self.locals.insert(arg); + self.visit_expr(args.get(i) + .expect(&format!("macro '{}' takes more than {} arguments", name, i))); + self.writeln(";"); } + self.handle(state, &def.nodes, AstLevel::Nested); + self.flush_ws(&def.ws2); + self.writeln("}"); + self.locals.pop(); } fn handle_include(&mut self, state: &'a State, ws: &WS, path: &str) { @@ -507,235 +462,280 @@ impl<'a> Generator<'a> { self.flush_ws(ws); } - fn handle(&mut self, state: &'a State, nodes: &'a [Node], level: AstLevel) { - for n in nodes { - match *n { - Node::Lit(lws, val, rws) => { self.write_lit(lws, val, rws); } - Node::Comment() => {}, - Node::Expr(ref ws, ref val) => { self.write_expr(state, ws, val); }, - Node::LetDecl(ref ws, ref var) => { self.write_let_decl(ws, var); }, - Node::Let(ref ws, ref var, ref val) => { self.write_let(ws, var, val); }, - Node::Cond(ref conds, ref ws) => { - self.write_cond(state, conds, ws); - }, - Node::Loop(ref ws1, ref var, ref iter, ref body, ref ws2) => { - self.write_loop(state, ws1, var, iter, body, ws2); - }, - Node::BlockDef(ref ws1, name, _, ref ws2) => { - if let AstLevel::Nested = level { - panic!("blocks ('{}') are only allowed at the top level", name); - } - self.write_block(ws1, name, ws2); - }, - Node::Include(ref ws, path) => { - self.handle_include(state, ws, path); - }, - Node::Call(ref ws, name, ref args) => self.write_call(state, ws, name, args), - Node::Macro(_, _) | - Node::Extends(_) => { - if let AstLevel::Nested = level { - panic!("macro or extend blocks only allowed at the top level"); - } - }, - } + fn write_let_decl(&mut self, ws: &WS, var: &'a Target) { + self.handle_ws(ws); + self.write("let "); + match *var { + Target::Name(name) => { + self.locals.insert(name); + self.write(name); + }, } + self.writeln(";"); } - // Writes header for the `impl` for `TraitFromPathName` or `Template` - // for the given context struct. - fn write_header(&mut self, state: &'a State, target: &str, extra_anno: &[&str]) { - let mut full_anno = Tokens::new(); - let mut orig_anno = Tokens::new(); - let need_anno = !state.input.ast.generics.lifetimes.is_empty() || - !state.input.ast.generics.ty_params.is_empty() || - !extra_anno.is_empty(); - if need_anno { - full_anno.append("<"); - orig_anno.append("<"); + fn write_let(&mut self, ws: &WS, var: &'a Target, val: &Expr) { + self.handle_ws(ws); + match *var { + Target::Name(name) => { + if !self.locals.contains(name) { + self.write("let "); + self.locals.insert(name); + } + self.write(name); + }, } + self.write(" = "); + self.visit_expr(val); + self.writeln(";"); + } - let (mut full_sep, mut orig_sep) = (false, false); - for lt in &state.input.ast.generics.lifetimes { - if full_sep { - full_anno.append(","); - } - if orig_sep { - orig_anno.append(","); + fn write_block(&mut self, ws1: &WS, name: &str, ws2: &WS) { + self.flush_ws(ws1); + self.writeln(&format!("timpl.render_block_{}_into(writer)?;", name)); + self.prepare_ws(ws2); + } + + fn write_expr(&mut self, state: &'a State, ws: &WS, s: &Expr) { + self.handle_ws(ws); + self.write("let askama_expr = &"); + let wrapped = self.visit_expr(s); + self.writeln(";"); + + use self::DisplayWrap::*; + use super::input::EscapeMode::*; + self.write("writer.write_fmt(format_args!(\"{}\", "); + self.write(match (wrapped, &state.input.meta.escaping) { + (Wrapped, &Html) | + (Wrapped, &None) | + (Unwrapped, &None) => "askama_expr", + (Unwrapped, &Html) => "&::askama::MarkupDisplay::from(askama_expr)", + }); + self.writeln("))?;"); + } + + fn write_lit(&mut self, lws: &'a str, val: &str, rws: &'a str) { + assert!(self.next_ws.is_none()); + if !lws.is_empty() { + if self.skip_ws { + self.skip_ws = false; + } else if val.is_empty() { + assert!(rws.is_empty()); + self.next_ws = Some(lws); + } else { + self.writeln(&format!("writer.write_str({:#?})?;", + lws)); } - lt.to_tokens(&mut full_anno); - lt.to_tokens(&mut orig_anno); - full_sep = true; - orig_sep = true; } + if !val.is_empty() { + self.writeln(&format!("writer.write_str({:#?})?;", val)); + } + if !rws.is_empty() { + self.next_ws = Some(rws); + } + } - for anno in extra_anno { - if full_sep { - full_anno.append(","); - } - full_anno.append(anno); - full_sep = true; + /* Visitor methods for expression types */ + + fn visit_expr(&mut self, expr: &Expr) -> DisplayWrap { + match *expr { + Expr::NumLit(s) => self.visit_num_lit(s), + Expr::StrLit(s) => self.visit_str_lit(s), + Expr::Var(s) => self.visit_var(s), + Expr::Attr(ref obj, name) => self.visit_attr(obj, name), + Expr::Filter(name, ref args) => self.visit_filter(name, args), + Expr::BinOp(op, ref left, ref right) => + self.visit_binop(op, left, right), + Expr::Group(ref inner) => self.visit_group(inner), + Expr::MethodCall(ref obj, method, ref args) => + self.visit_method_call(obj, method, args), } + } - for param in &state.input.ast.generics.ty_params { - if full_sep { - full_anno.append(","); - } - if orig_sep { - orig_anno.append(","); - } - let mut impl_param = param.clone(); - impl_param.default = None; - impl_param.to_tokens(&mut full_anno); - param.ident.to_tokens(&mut orig_anno); - full_sep = true; - orig_sep = true; + fn visit_filter(&mut self, name: &str, args: &[Expr]) -> DisplayWrap { + if name == "format" { + self._visit_format_filter(args); + return DisplayWrap::Unwrapped; + } else if name == "join" { + self._visit_join_filter(args); + return DisplayWrap::Unwrapped; } - if need_anno { - full_anno.append(">"); - orig_anno.append(">"); + if filters::BUILT_IN_FILTERS.contains(&name) { + self.write(&format!("::askama::filters::{}(&", name)); + } else { + self.write(&format!("filters::{}(&", name)); } - let mut where_clause = Tokens::new(); - state.input.ast.generics.where_clause.to_tokens(&mut where_clause); - self.writeln(&format!("impl{} {} for {}{}{} {{", - full_anno.as_str(), target, state.input.ast.ident.as_ref(), - orig_anno.as_str(), where_clause.as_str())); + self._visit_filter_args(args); + self.write(")?"); + if name == "safe" || name == "escape" || name == "e" || name == "json" { + DisplayWrap::Wrapped + } else { + DisplayWrap::Unwrapped + } } - // Implement `Template` for the given context struct. - fn impl_template(&mut self, state: &'a State) { - self.write_header(state, "::askama::Template", &[]); - self.writeln("fn render_into(&self, writer: &mut ::std::fmt::Write) -> \ - ::askama::Result<()> {"); - self.handle(state, state.nodes, AstLevel::Top); - self.flush_ws(&WS(false, false)); - self.writeln("Ok(())"); - self.writeln("}"); - self.writeln("}"); + fn _visit_format_filter(&mut self, args: &[Expr]) { + self.write("format!("); + self._visit_filter_args(args); + self.write(")"); } - // Implement `Display` for the given context struct. - fn impl_display(&mut self, state: &'a State) { - self.write_header(state, "::std::fmt::Display", &[]); - self.writeln("fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {"); - self.writeln("self.render_into(f).map_err(|_| ::std::fmt::Error {})"); - self.writeln("}"); - self.writeln("}"); + // Force type coercion on first argument to `join` filter (see #39). + fn _visit_join_filter(&mut self, args: &[Expr]) { + self.write("::askama::filters::join((&"); + for (i, arg) in args.iter().enumerate() { + if i > 0 { + self.write(", &"); + } + self.visit_expr(arg); + if i == 0 { + self.write(").into_iter()"); + } + } + self.write(")?"); } - // Implement `Deref<Parent>` for an inheriting context struct. - fn deref_to_parent(&mut self, state: &'a State, parent_type: &syn::Ty) { - self.write_header(state, "::std::ops::Deref", &[]); - let mut tokens = Tokens::new(); - parent_type.to_tokens(&mut tokens); - self.writeln(&format!("type Target = {};", tokens.as_str())); - self.writeln("fn deref(&self) -> &Self::Target {"); - self.writeln("&self._parent"); - self.writeln("}"); - self.writeln("}"); + fn _visit_filter_args(&mut self, args: &[Expr]) { + for (i, arg) in args.iter().enumerate() { + if i > 0 { + self.write(", &"); + } + self.visit_expr(arg); + } } - // Implement `TraitFromPathName` for the given context struct. - fn impl_trait(&mut self, state: &'a State, nodes: Option<&'a [Node]>) { - self.write_header(state, &state.trait_name, &[]); - self.write_block_defs(state); - - self.writeln("#[allow(unused_variables)]"); - self.writeln(&format!( - "fn render_trait_into(&self, timpl: &{}, writer: &mut ::std::fmt::Write) \ - -> ::askama::Result<()> {{", - state.trait_name)); + fn visit_attr(&mut self, obj: &Expr, attr: &str) -> DisplayWrap { + if let Expr::Var(name) = *obj { + if name == "loop" { + self.write("_loop_index"); + if attr == "index" { + self.write(" + 1"); + return DisplayWrap::Unwrapped; + } else if attr == "index0" { + return DisplayWrap::Unwrapped; + } else { + panic!("unknown loop variable"); + } + } + } + self.visit_expr(obj); + self.write(&format!(".{}", attr)); + DisplayWrap::Unwrapped + } - if let Some(nodes) = nodes { - self.handle(state, nodes, AstLevel::Top); - self.flush_ws(&WS(false, false)); - } else { - self.writeln("self._parent.render_trait_into(self, writer)?;"); + fn visit_method_call(&mut self, obj: &Expr, method: &str, args: &[Expr]) -> DisplayWrap { + self.visit_expr(obj); + self.write(&format!(".{}(", method)); + for (i, arg) in args.iter().enumerate() { + if i > 0 { + self.write(", "); + } + self.visit_expr(arg); } + self.write(")"); + DisplayWrap::Unwrapped + } - self.writeln("Ok(())"); - self.writeln("}"); - self.flush_ws(&WS(false, false)); - self.writeln("}"); + fn visit_binop(&mut self, op: &str, left: &Expr, right: &Expr) -> DisplayWrap { + self.visit_expr(left); + self.write(&format!(" {} ", op)); + self.visit_expr(right); + DisplayWrap::Unwrapped } - // Implement `Template` for templates that implement a template trait. - fn impl_template_for_trait(&mut self, state: &'a State) { - self.write_header(state, "::askama::Template", &[]); - self.writeln("fn render_into(&self, writer: &mut ::std::fmt::Write) \ - -> ::askama::Result<()> {"); - if state.derived { - self.writeln("self._parent.render_trait_into(self, writer)?;"); + fn visit_group(&mut self, inner: &Expr) -> DisplayWrap { + self.write("("); + self.visit_expr(inner); + self.write(")"); + DisplayWrap::Unwrapped + } + + fn visit_var(&mut self, s: &str) -> DisplayWrap { + if self.locals.contains(s) { + self.write(s); } else { - self.writeln("self.render_trait_into(self, writer)?;"); + self.write(&format!("self.{}", s)); } - self.writeln("Ok(())"); - self.writeln("}"); - self.writeln("}"); + DisplayWrap::Unwrapped } - // Defines the `TraitFromPathName` trait. - fn define_trait(&mut self, state: &'a State) { - self.writeln(&format!("trait {} {{", state.trait_name)); - self.write_block_defs(state); - self.writeln(&format!( - "fn render_trait_into(&self, timpl: &{}, writer: &mut ::std::fmt::Write) \ - -> ::askama::Result<()>;", - state.trait_name)); - self.writeln("}"); + fn visit_str_lit(&mut self, s: &str) -> DisplayWrap { + self.write(&format!("\"{}\"", s)); + DisplayWrap::Unwrapped } - // Implement iron's Modifier<Response> if enabled - fn impl_modifier_response(&mut self, state: &'a State) { - self.write_header(state, "::askama::iron::Modifier<::askama::iron::Response>", &[]); - self.writeln("fn modify(self, res: &mut ::askama::iron::Response) {"); - self.writeln("res.body = Some(Box::new(self.render().unwrap().into_bytes()));"); - self.writeln("}"); - self.writeln("}"); + fn visit_num_lit(&mut self, s: &str) -> DisplayWrap { + self.write(s); + DisplayWrap::Unwrapped } - // Implement Rocket's `Responder`. - fn impl_responder(&mut self, state: &'a State) { - self.write_header(state, "::askama::rocket::Responder<'r>", &["'r"]); - self.writeln("fn respond_to(self, _: &::askama::rocket::Request) \ - -> ::askama::rocket::Result<'r> {"); + fn visit_target_single<'t>(&mut self, name: &'t str) -> Vec<&'t str> { + vec![name] + } - let ext = match state.input.path.extension() { - Some(s) => s.to_str().unwrap(), - None => "txt", - }; - self.writeln(&format!("::askama::rocket::respond(&self, {:?})", ext)); + fn visit_target<'t>(&mut self, target: &'t Target) -> Vec<&'t str> { + match *target { + Target::Name(s) => { self.visit_target_single(s) }, + } + } - self.dedent(); - self.writeln("}"); - self.writeln("}"); + /* Helper methods for dealing with whitespace nodes */ + + fn handle_ws(&mut self, ws: &WS) { + self.flush_ws(ws); + self.prepare_ws(ws); } - fn result(mut self, state: &'a State) -> String { - if !state.blocks.is_empty() { - if !state.derived { - self.define_trait(state); - } else { - let parent_type = get_parent_type(state.input.ast) - .expect("expected field '_parent' in extending template struct"); - self.deref_to_parent(state, parent_type); + fn flush_ws(&mut self, ws: &WS) { + if self.next_ws.is_some() && !ws.0 { + let val = self.next_ws.unwrap(); + if !val.is_empty() { + self.writeln(&format!("writer.write_str({:#?})?;", + val)); } + } + self.next_ws = None; + } - let trait_nodes = if !state.derived { Some(&state.nodes[..]) } else { None }; - self.impl_trait(state, trait_nodes); - self.impl_template_for_trait(state); - } else { - self.impl_template(state); + fn prepare_ws(&mut self, ws: &WS) { + self.skip_ws = ws.1; + } + + /* Helper methods for writing to internal buffer */ + + fn writeln(&mut self, s: &str) { + if s.is_empty() { + return; } - self.impl_display(state); - if cfg!(feature = "iron") { - self.impl_modifier_response(state); + if s == "}" { + self.dedent(); } - if cfg!(feature = "rocket") { - self.impl_responder(state); + self.write(s); + if s.ends_with('{') { + self.indent(); } - self.buf + self.buf.push('\n'); + self.start = true; + } + + fn write(&mut self, s: &str) { + if self.start { + for _ in 0..(self.indent * 4) { + self.buf.push(' '); + } + self.start = false; + } + self.buf.push_str(s); + } + + fn indent(&mut self) { + self.indent += 1; + } + + fn dedent(&mut self) { + self.indent -= 1; } } |