-
Notifications
You must be signed in to change notification settings - Fork 14
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
Add support for "runtime" values #311
Add support for "runtime" values #311
Conversation
7bc47be
to
4dc727a
Compare
Finally fixed CI. Sorry for the noise. |
rinja/src/values.rs
Outdated
/// feature is enabled, it uses the [`BTreeMap`](alloc::collections::BTreeMap) type, otherwise | ||
/// it uses an empty type and does nothing. | ||
#[derive(Default)] | ||
pub struct Values(#[cfg(feature = "alloc")] HashMap<String, Box<dyn Any>>); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I kinda dislike the use of a HashMap in here. Unless there are quiet a lot key-value pairs, a simple slice &[(&str, &dyn Any)]
will be excessively much faster. My guess is that a HashMap with many intermediate allocations won't be faster for less than 20 values. (I did not benchmark my guess, though. :) )
Maybe Values
could be a trait instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not using a map sounds good to me. I suppose by making Values
a trait, we would let it up to users to implement it on whatever they want? But then the question is: should we implement on some types by default as well (like &(&str, &dyn Any)
or HashMap<String, Box<dyn Any>>
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be useful to implement it for some types, so it works "out of the box". But I would not seal the trait.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by "seal the trait"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mod private { // ← not `pub`, so you can't implement this trait outside of the crate
pub trait Sealed {}
}
pub mod MyTrait: private::Sealed {}
https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/#what-are-sealed-traits
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah so making the trait impossible to implement outside of the crate. I didn't know there was a name for that. TIL, thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Big question: making the Values
trait fully generic over the key creates a lot of issues:
- DynTemplate is not dyn-compatible anymore
- We get unsolvable errors like:
4 | #[derive(Template)] | ^^^^^^^^ | | | expected `&K`, found `&str` | arguments to this method are incorrect
Would it be ok to force the key to be AsRef<str>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think it's a good idea to force the key to be str'ish. Maybe K: Borrow<str>
would be the best solution? So the interface is the same as HashMap::get()
, and because Borrow<str>
implies that K
's hash is the same as the borrowed str
's hash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me.
I am not 100% sure that I see the advantage over adding a field |
Mostly because then, you don't need to add a |
Ah, yes, a csrf token sounds like a good use case. |
Turned I'm very curious to see if you will be able to see a way to get around them. Also, since |
86701f7
to
fb8a28f
Compare
15ee05e
to
f16dcef
Compare
I rebased the PR on the current master. I implemented the idea somewhat differently. Please feel free to unpick the newest commit if you don't like it! Instead of adding a |
f16dcef
to
8e899b5
Compare
rinja/src/lib.rs
Outdated
@@ -83,6 +84,7 @@ pub use rinja_derive::Template; | |||
pub use crate as shared; | |||
pub use crate::error::{Error, Result}; | |||
pub use crate::helpers::PrimitiveType; | |||
pub use crate::values::{NO_VALUES, Value, Values}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also reexport get_value
since people can always call rinja::get_value
in their templates or even in their code (who knows?).
@@ -1167,7 +1167,8 @@ impl<'a> Generator<'a, '_> { | |||
idx | |||
}; | |||
lines.write(format_args!( | |||
"(&&rinja::filters::Writable(expr{idx})).rinja_write(__rinja_writer)?;", | |||
"(&&&rinja::filters::Writable(expr{idx})).\ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why adding a new reference here btw?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the values as new argument to WriteWritable
. This way it gets automagically passed on to any {{ template }}
s. To make this work I need to tell Template
s other FastWritable
s apart.
8e899b5
to
98db127
Compare
I think it's a much simpler approach, love it! Shouldn't have tried to force too much the trait argument approach... My turn now (some docs to update and some edges I'd like to soften). Thanks! |
rinja_derive/src/generator/expr.rs
Outdated
@@ -584,6 +586,7 @@ impl<'a> Generator<'a, '_> { | |||
return Ok(DisplayWrap::Unwrapped); | |||
} | |||
} | |||
#[allow(clippy::literal_string_with_formatting_args)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh that's a new lint I added haha.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, I like the lint! I forgot more than once to surround the string with format!(…)
. :)
98db127
to
b6b1461
Compare
I'm happy that you like my approach, and I'm interested to see what you make out of it! :) |
Seems like we broke rustc. :o |
Haha, oops. Can you open an issue? I guess you have more experience with that than me. (And I just noticed that it's 20:40 already and that I have not eaten all day.) |
Off you go then. Enough computer for today for you! Eat well. And yes, gonna do that. :) |
While trying the new changes and in particular the new filter, I'm not a bit fan of the incoherence introduced: the argument is not argument an argument but a generic. What do you think if we instead added support for generics on filters? Or simpler (although I like the filter): we don't make this filter. |
Yes, having a type as a function argument is a bit odd. Something like I wouldn't like a free function |
I'd prefer to keep the syntax Rust compatible as much as possible (and also I already implemented to parsing of
Me neither. Using If it sounds good to you, I'll make the changes. |
Sure, go ahead! :) |
65e94e4
to
ac72367
Compare
Finally done! I special-cased the I think I did pretty much everything I thought was missing. Thanks a lot for your help getting it to the finish line! :) |
18fd30e
to
01ef507
Compare
Would it be difficult to extract the turbofish implementation into its own PR, and then rebase this PR on it? |
Hum, I think it should be mostly fine. The git history will be completely broken so I'm gonna have a good time. :') |
01ef507
to
d192ced
Compare
Much smaller change now. Now two commits only and just value related changes. :) |
rinja_derive/src/generator/expr.rs
Outdated
fn _visit_ty( | ||
&mut self, | ||
ctx: &Context<'_>, | ||
buf: &mut Buffer, | ||
ty: &WithSpan<'_, Expr<'a>>, | ||
) -> Result<(), CompileError> { | ||
match &**ty { | ||
Expr::Var(ty) => { | ||
buf.write(normalize_identifier(ty)); | ||
} | ||
Expr::Path(ty) => { | ||
self.visit_path(buf, ty); | ||
} | ||
Expr::Unary("&", rhs) => { | ||
buf.write('&'); | ||
self._visit_ty(ctx, buf, rhs.as_ref())?; | ||
} | ||
Expr::BinOp(token @ ("<" | ">"), lhs, rhs) => { | ||
self._visit_ty(ctx, buf, lhs.as_ref())?; | ||
buf.write(token); | ||
self._visit_ty(ctx, buf, rhs.as_ref())?; | ||
} | ||
_ => { | ||
return Err( | ||
ctx.generate_error("expected a type name, not an expression", ty.span()) | ||
); | ||
} | ||
} | ||
Ok(()) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fn _visit_ty( | |
&mut self, | |
ctx: &Context<'_>, | |
buf: &mut Buffer, | |
ty: &WithSpan<'_, Expr<'a>>, | |
) -> Result<(), CompileError> { | |
match &**ty { | |
Expr::Var(ty) => { | |
buf.write(normalize_identifier(ty)); | |
} | |
Expr::Path(ty) => { | |
self.visit_path(buf, ty); | |
} | |
Expr::Unary("&", rhs) => { | |
buf.write('&'); | |
self._visit_ty(ctx, buf, rhs.as_ref())?; | |
} | |
Expr::BinOp(token @ ("<" | ">"), lhs, rhs) => { | |
self._visit_ty(ctx, buf, lhs.as_ref())?; | |
buf.write(token); | |
self._visit_ty(ctx, buf, rhs.as_ref())?; | |
} | |
_ => { | |
return Err( | |
ctx.generate_error("expected a type name, not an expression", ty.span()) | |
); | |
} | |
} | |
Ok(()) | |
} |
The method is not used anymore.
I wonder why neither rustc nor clippy have a problem with the unused method? It calls itself, but nobody else calls it. Except for the one comment, it look great! |
d192ced
to
5257838
Compare
It's super strange indeed... Gonna report that. In the meantime, fixed it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
It's because it starts with an underscore. XD |
Fixes #48.
We can now provide values at runtime. It'll be useful for me for switching
mdBook
torinja
(oraskama
soon enough) as they need to allow to parse some files aftermdBook
is compiled if users provide more (not sure it'll be enough though, we'll see).Funnily enough, it forced me to support the
call::<>
syntax with references and all.