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

derive: allow split up template attributes #117

Merged
merged 1 commit into from
Aug 7, 2024
Merged
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
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