-
Notifications
You must be signed in to change notification settings - Fork 122
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
Replace build_json!
with a JSON builder API
#329
Replace build_json!
with a JSON builder API
#329
Conversation
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, nice! A builder pattern is a great solution, too. Thoughts:
- What about renaming
field
torequired
andfield_optional
to justoptional
? Easier to read, IMO. - Why not use
JsonBuilder::new
instead ofjson_builder
? That looks more like a regular builder pattern. - What if we went for a similar approach for the map version? Just for "consistency", and we could possibly use some magic that avoids all these
.map(|x| x.as_ref())
and.map(|x| x.as_deref())
. Not quite sure how that could be done, though, just an idea. Might not be worth it in the end, but we could explore it.
I only chose |
I mean, we're also losing |
Well, it would actually be possible to not affect performance. You just need to leverage type inference a bit: pub(crate) struct JsonBuilder<Len> {
map: serde_json::Map<String, serde_json::Value>,
len: PhantomData<fn() -> Len>,
}
impl<Len: ToUsize> JsonBuilder<Len> {
pub(crate) fn new() -> Self {
Self {
map: serde_json::Map::with_capacity(Len::to_usize()),
len: PhantomData,
}
}
}
impl<Len: ToUsize> JsonBuilder<S<Len>> {
pub(crate) fn required(mut self, name: &str, value: impl Serialize) -> JsonBuilder<Len> {
self.map
.insert(name.to_owned(), serde_json::to_value(value).unwrap());
JsonBuilder {
map: self.map,
len: PhantomData,
}
}
pub(crate) fn optional(self, name: &str, value: Option<impl Serialize>) -> JsonBuilder<Len> {
if let Some(value) = value {
self.required(name, value)
} else {
JsonBuilder {
map: self.map,
len: PhantomData,
}
}
}
}
impl JsonBuilder<Z> {
pub(crate) fn build(self) -> serde_json::Value {
serde_json::Value::Object(self.map)
}
}
pub(crate) struct Z;
pub(crate) struct S<T>(T);
pub(crate) trait ToUsize {
fn to_usize() -> usize;
}
impl ToUsize for Z {
fn to_usize() -> usize {
0
}
}
impl<T: ToUsize> ToUsize for S<T> {
fn to_usize() -> usize {
T::to_usize() + 1
}
} Do you think it's worth it? |
Hah, awesome, very haskell-y. I was thinking of just using const generics but it's even better like that. Is the I would say this is much better than the macro. Perhaps not as understandable if you aren't used to const generics, but macros are pretty weird, too. As long as it's nicely commented I would say it's great. I would also rename Props for such a neat design!! |
I use
I was inspired by Peano arithmetic, in which there is a constant
I think Now I’ve actually implemented it, and I named |
No worries. It especially makes sense in this case; I would say that a comment explaining the
Cool, I hadn't seen those in Rust yet. I think the naming is spot-on now. I also prefer it with associated constants. |
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.
Really nicely commented! It seems like this branch is over map-builder
, so we would need MapBuilder
implemented before merging this PR.
Sorry about forgetting about this one! Merge conflicts are gone now. |
No worries, thank you very much for the contribution! I'm merging this. |
Description
Instead of:
we now use:
I would’ve used an array-based API like in #328 but that doesn’t really work well here because the field value types can be any type rather than just strings.
Dependencies
None.
How has this been tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce.
Please also list any relevant details for your test configuration
cargo test --features env-file