Skip to content

Commit

Permalink
Merge pull request #117 from Kijewski/pr-template-times-x
Browse files Browse the repository at this point in the history
derive: allow split up template attributes
  • Loading branch information
GuillaumeGomez authored Aug 7, 2024
2 parents ca01768 + f25dac9 commit ebcfff6
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 39 deletions.
89 changes: 56 additions & 33 deletions rinja_derive/src/input.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::borrow::Cow;
use std::collections::hash_map::{Entry, HashMap};
use std::fs::read_to_string;
use std::iter::FusedIterator;
use std::path::{Path, PathBuf};
use std::sync::{Arc, OnceLock};

Expand Down Expand Up @@ -290,47 +291,45 @@ pub(crate) struct TemplateArgs {
}

impl TemplateArgs {
pub(crate) fn new(ast: &'_ syn::DeriveInput) -> Result<Self, CompileError> {
// Check that an attribute called `template()` exists once and that it is
pub(crate) fn new(ast: &syn::DeriveInput) -> Result<Self, CompileError> {
// Check that an attribute called `template()` exists at least once and that it is
// the proper type (list).
let mut span = None;
let mut template_args = None;
for attr in &ast.attrs {
let path = &attr.path();
if !path.is_ident("template") {
continue;
}

span = Some(path.span());
match attr.parse_args_with(Punctuated::<syn::Meta, syn::Token![,]>::parse_terminated) {
Ok(args) if template_args.is_none() => template_args = Some(args),
Ok(_) => {
return Err(CompileError::no_file_info(
"duplicated 'template' attribute",
span,
));
}
Err(e) => {
return Err(CompileError::no_file_info(
let mut templates_attrs = ast
.attrs
.iter()
.filter(|attr| attr.path().is_ident("template"))
.peekable();
let mut args = match templates_attrs.peek() {
Some(attr) => Self {
template_span: Some(attr.path().span()),
..Self::default()
},
None => {
return Err(CompileError::no_file_info(
"no attribute `template` found",
None,
));
}
};
let attrs = templates_attrs
.map(|attr| {
type Attrs = Punctuated<syn::Meta, syn::Token![,]>;
match attr.parse_args_with(Attrs::parse_terminated) {
Ok(args) => Ok(args),
Err(e) => Err(CompileError::no_file_info(
format!("unable to parse template arguments: {e}"),
span,
));
Some(attr.path().span()),
)),
}
};
}

let template_args = template_args
.ok_or_else(|| CompileError::no_file_info("no attribute 'template' found", None))?;
})
.flat_map(ResultIter::from);

let mut args = Self {
template_span: span,
..Self::default()
};
// Loop over the meta attributes and find everything that we
// understand. Return a CompileError if something is not right.
// `source` contains an enum that can represent `path` or `source`.
for item in &template_args {
let pair = match item {
for item in attrs {
let pair = match item? {
syn::Meta::NameValue(pair) => pair,
v => {
return Err(CompileError::no_file_info(
Expand Down Expand Up @@ -427,6 +426,30 @@ impl TemplateArgs {
}
}

struct ResultIter<I, E>(Result<I, Option<E>>);

impl<I: IntoIterator, E> From<Result<I, E>> for ResultIter<I::IntoIter, E> {
fn from(value: Result<I, E>) -> Self {
Self(match value {
Ok(i) => Ok(i.into_iter()),
Err(e) => Err(Some(e)),
})
}
}

impl<I: Iterator, E> Iterator for ResultIter<I, E> {
type Item = Result<I::Item, E>;

fn next(&mut self) -> Option<Self::Item> {
match &mut self.0 {
Ok(iter) => Some(Ok(iter.next()?)),
Err(err) => Some(Err(err.take()?)),
}
}
}

impl<I: FusedIterator, E> FusedIterator for ResultIter<I, E> {}

fn source_or_path(
name: &syn::Ident,
value: &syn::ExprLit,
Expand Down
11 changes: 10 additions & 1 deletion testing/tests/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,10 +544,19 @@ struct TestI16ToU8 {
}

#[test]
#[allow(clippy::needless_borrows_for_generic_args)]
fn test_i16_to_u8() {
assert_eq!(TestI16ToU8 { input: 0 }.to_string(), "0 0 0");
assert_eq!(TestI16ToU8 { input: 0x7f00 }.to_string(), "0 0 0");
assert_eq!(TestI16ToU8 { input: 255 }.to_string(), "255 255 255");
assert_eq!(TestI16ToU8 { input: -12345 }.to_string(), "199 199 199");
}

#[derive(Template)]
#[template(source = "🙂")]
#[template(ext = "txt")]
struct SplitTemplateDeclaration;

#[test]
fn test_split_template_declaration() {
assert_eq!(SplitTemplateDeclaration.to_string(), "🙂")
}
8 changes: 4 additions & 4 deletions testing/tests/ui/duplicated_template_attribute.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: duplicated 'template' attribute
--> tests/ui/duplicated_template_attribute.rs:8:3
error: must specify `source` OR `path` exactly once
--> tests/ui/duplicated_template_attribute.rs:9:5
|
8 | #[template(
| ^^^^^^^^
9 | source = "🙃",
| ^^^^^^
2 changes: 1 addition & 1 deletion testing/tests/ui/no_template_attribute.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: no attribute 'template' found
error: no attribute `template` found
--> tests/ui/no_template_attribute.rs:4:8
|
4 | struct NoTemplate;
Expand Down

0 comments on commit ebcfff6

Please sign in to comment.