diff --git a/book/src/template_syntax.md b/book/src/template_syntax.md index dc385766..ba4581cf 100644 --- a/book/src/template_syntax.md +++ b/book/src/template_syntax.md @@ -245,13 +245,11 @@ struct MyTemplate; ### Struct / trait implementations -Finally, we can invoke functions that are implementation methods of our -template struct, by referencing `Self` (note the uppercase `S`) as the path, -before calling our function: +Finally, we can call methods of our template struct: ```rust #[derive(Template)] -#[template(source = "{{ Self::foo(self, 123) }}", ext = "txt")] +#[template(source = "{{ foo(123) }}", ext = "txt")] struct MyTemplate { count: u32, }; @@ -263,9 +261,8 @@ impl MyTemplate { } ``` -If the implemented method requires a reference to the struct itself, -such as is demonstrated in the above example, we can pass `self` -(note the lowercase `s`) as the first argument. +You can also use `self.foo(123)`, or even `Self::foo(self, 123)`, as you see +fit. Similarly, using the `Self` path, we can also call any method belonging to a trait that has been implemented for our template struct: @@ -286,6 +283,32 @@ impl Hello for MyTemplate { } ``` +If you want to call a closure which is a field, you'll need to follow Rust's +syntax by surrounding the call with parens: + +```rust +#[derive(Template)] +#[template(source = "{{ (closure)(12) }}", ext = "txt")] +struct MyTemplate { + closure: fn(i32) -> i32, +} +``` + +## Calling functions + +If you only provide a function name, `rinja` will assume it's a method. If +you want to call a method, you will need to use a path instead: + +```jinja +{# This is the equivalent of `self.method()`. #} +{{ method() }} +{# This is the equivalent of `self::function()`, which will call the +`function` function from the current module. #} +{{ self::function() }} +{# This is the equivalent of `super::b::f()`. #} +{{ super::b::f() }} +``` + ## Template inheritance Template inheritance allows you to build a base template with common diff --git a/rinja_derive/src/generator.rs b/rinja_derive/src/generator.rs index 30563307..9d069f23 100644 --- a/rinja_derive/src/generator.rs +++ b/rinja_derive/src/generator.rs @@ -1622,7 +1622,7 @@ impl<'a> Generator<'a> { match sub_left { Expr::Var(name) => match self.locals.resolve(name) { Some(resolved) => buf.write(resolved), - None => buf.write(format_args!("(&self.{})", normalize_identifier(name))), + None => buf.write(format_args!("self.{}", normalize_identifier(name))), }, _ => { self.visit_expr(ctx, buf, left)?; diff --git a/testing/tests/calls.rs b/testing/tests/calls.rs index 5a2ae0bb..3dea2fed 100644 --- a/testing/tests/calls.rs +++ b/testing/tests/calls.rs @@ -1,19 +1,21 @@ use rinja::Template; #[derive(Template)] -#[template(source = "{{ func(value) }}", ext = "txt")] +#[template(source = "{{ b(value) }}", ext = "txt")] struct OneFunction { - func: fn(&i32) -> i32, - value: i32, + value: u32, +} + +impl OneFunction { + fn b(&self, x: &u32) -> u32 { + self.value + x + } } #[test] fn test_one_func() { - let t = OneFunction { - func: |&i| 2 * i, - value: 123, - }; - assert_eq!(t.render().unwrap(), "246"); + let t = OneFunction { value: 10 }; + assert_eq!(t.render().unwrap(), "20"); } #[derive(Template)] @@ -127,3 +129,44 @@ fn test_do_not_move_fields() { }; assert_eq!(x.render().unwrap(), "a"); } + +#[derive(Template)] +#[template(source = "{{ (func)(value) }}", ext = "txt")] +struct ClosureField { + func: fn(&i32) -> i32, + value: i32, +} + +#[test] +fn test_closure_field() { + let t = ClosureField { + func: |&i| 2 * i, + value: 123, + }; + assert_eq!(t.render().unwrap(), "246"); +} + +fn single() -> &'static str { + "a" +} + +mod sub_mod { + pub fn sub_fn(v: i32) -> i32 { + v * 2 + } +} + +#[derive(Template)] +#[template( + source = " +{{- self::single() -}} +{{- sub_mod::sub_fn(3) -}} +", + ext = "txt" +)] +struct NotMethod; + +#[test] +fn test_not_method() { + assert_eq!(NotMethod.render().unwrap(), "a6"); +} diff --git a/testing/tests/simple.rs b/testing/tests/simple.rs index 8d4b895b..2e845593 100644 --- a/testing/tests/simple.rs +++ b/testing/tests/simple.rs @@ -304,15 +304,17 @@ fn test_slice_literal() { #[derive(Template)] #[template(source = "Hello, {{ world(\"123\", 4) }}!", ext = "txt")] -struct FunctionRefTemplate { - world: fn(s: &str, v: u8) -> String, +struct FunctionRefTemplate; + +impl FunctionRefTemplate { + fn world(&self, s: &str, v: u8) -> String { + format!("world({s}, {v})") + } } #[test] fn test_func_ref_call() { - let t = FunctionRefTemplate { - world: |s, r| format!("world({s}, {r})"), - }; + let t = FunctionRefTemplate; assert_eq!(t.render().unwrap(), "Hello, world(123, 4)!"); } diff --git a/testing/tests/try.rs b/testing/tests/try.rs index 8f6efe30..4fbf44fb 100644 --- a/testing/tests/try.rs +++ b/testing/tests/try.rs @@ -28,14 +28,22 @@ fn test_int_parser() { #[derive(Template)] #[template(source = "{{ value()? }}", ext = "txt")] struct FailFmt { - value: fn() -> Result<&'static str, std::fmt::Error>, + inner: Option<&'static str>, +} + +impl FailFmt { + fn value(&self) -> Result<&'static str, std::fmt::Error> { + if let Some(inner) = self.inner { + Ok(inner) + } else { + Err(std::fmt::Error) + } + } } #[test] fn fail_fmt() { - let template = FailFmt { - value: || Err(std::fmt::Error), - }; + let template = FailFmt { inner: None }; assert!(matches!(template.render(), Err(rinja::Error::Custom(_)))); assert_eq!( format!("{}", &template.render().unwrap_err()), @@ -43,7 +51,7 @@ fn fail_fmt() { ); let template = FailFmt { - value: || Ok("hello world"), + inner: Some("hello world"), }; assert_eq!(template.render().unwrap(), "hello world"); } @@ -51,19 +59,25 @@ fn fail_fmt() { #[derive(Template)] #[template(source = "{{ value()? }}", ext = "txt")] struct FailStr { - value: fn() -> Result<&'static str, &'static str>, + value: bool, +} + +impl FailStr { + fn value(&self) -> Result<&'static str, &'static str> { + if !self.value { + Err("FAIL") + } else { + Ok("hello world") + } + } } #[test] fn fail_str() { - let template = FailStr { - value: || Err("FAIL"), - }; + let template = FailStr { value: false }; assert!(matches!(template.render(), Err(rinja::Error::Custom(_)))); assert_eq!(format!("{}", &template.render().unwrap_err()), "FAIL"); - let template = FailStr { - value: || Ok("hello world"), - }; + let template = FailStr { value: true }; assert_eq!(template.render().unwrap(), "hello world"); }