-
Notifications
You must be signed in to change notification settings - Fork 524
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
WIP: Add json (serde) support #1081
base: master
Are you sure you want to change the base?
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.
This is impressive work and this feature is on the project roadmap (#624). However this PR is way too big to review. I did scroll through the code and ask some questions that came to mind.
Can you split this work into smaller PRs?
pub trait Sealed {} | ||
} | ||
|
||
pub trait AnyValue: CoreAny + Message + private::Sealed { |
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.
Can you explain what any v2 is and why it is better?
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.
This basically tries to address the "improved Any" bulletpoint in #624. The new Any implementation provides support for JSON and "cross" serialization (JSON <-> Binary).
The summary for that change is basically going from:
struct OldAny {
type_path: String,
data: Vec<u8>,
}
to
struct NewAny {
type_path: String,
kind: enum {
Protobuf(Vec<u8>),
Json(JsonValue),
Dyn(Box<dyn AnyValue>),
}
}
Yeah, it's actually on the TODO list. I just still haven't gotten to actually complete this as a whole yet. |
@@ -172,6 +173,32 @@ impl Field { | |||
_ => None, | |||
} | |||
} | |||
|
|||
pub fn json(&self) -> Option<Option<&Json>> { |
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.
Wouldn't it make more sense to flatten this into Option<&Json>
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.
Conceptionally yes, but this is an actual tri-state kind of thing, because (iirc) we are skipping code generation for Self::OneOf
fields. But we could also make this more flat and just add another check for an oneof field at the callsite 🤷♂️.
@@ -107,7 +111,7 @@ impl fmt::Display for Duration { | |||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |||
let mut d = *self; | |||
d.normalize(); | |||
if self.seconds < 0 && self.nanos < 0 { | |||
if self.seconds < 0 && self.nanos <= 0 || self.seconds <= 0 && self.nanos < 0 { |
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.
This seems like an unrelated fix that can ship on its own.
Any news on this? This would be incredibly useful. |
I've done a quick rebase and made few adjustments to pass the conformance tests again. It also seems like I need to do some tweaks to fix the CI for
I am still way too busy with other things. What this PR needs is a split and a bit of slight documentation work. I will probably get to that at the end of December perhaps. Thanks for your patience! |
@@ -14,7 +14,8 @@ proc-macro = true | |||
|
|||
[dependencies] | |||
anyhow = "1.0.1" | |||
itertools = ">=0.10.1, <=0.13" | |||
heck = "0.4.1" |
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.
0.5 was released 9 months ago. Any reason to use a version from 2 years ago?
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 sure anymore but I guess this PR or at least my first local impl predated this commit 2b6140c.
But yeh, we can certainly bump this.
@@ -69,6 +69,10 @@ impl Duration { | |||
result.normalize(); | |||
result | |||
} | |||
|
|||
pub fn is_valid(&self) -> bool { | |||
self.seconds >= -315_576_000_000 && self.seconds <= 315_576_000_000 |
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.
Sorry if I'm a tourist asking silly questions here, but what does it mean to be a duration consisting of a negative number of seconds?
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.
aiui, Chrono calls it a TimeDelta for this reason
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.
There was a conformance test that checked this behavior... It's basically an api contract / invariant, that is mentioned here: https://github.com/protocolbuffers/protobuf/blob/2ad21af8406316d2b170230f93db7d10c36108dd/src/google/protobuf/duration.proto#L103.
} | ||
} | ||
|
||
// FIXME: Make `T` contravariant, not covariant, by changing the `T` in `PhantomData` to |
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.
looks like this was done since you have PhantomData<fn() -> T>
@caspermeijn has asked for this to be split into smaller PRs: Reviewing this, I would also expect to see example messages which get extended and validating that before and after changes to the proto file, it works. e.g. message TestMessage {
string name = 1;
} Then a json payload that satisfies that is then stuck into: message TestMessage {
string name = 1;
int32 age = 2;
} Or is there an existing test framework and this is using it as another pass with serde enabled and I just don't see how it fits together. I'm not a committer so take my suggestion with a grain of salt. You've already done an impressive bit of work here! |
Yeah, I've mentioned this in my earlier update comment as well. It's definitely on my to-do list! :)
I haven't written any specific tests for this yet. That said, I'm not overly concerned because the conformance test suite already exercises this functionality by feeding data (with unknown/missing fields) for decoding/deserialization during our roundtrip conversions. However, having some dedicated tests in our own codebase wouldn't hurt either. For now, I'll likely prioritize other tasks in the near future. However, I'd love to collaborate on this, if you have any ideas for tests, feel free to share :D |
The most recent commits should address almost all conformance failures. There is just one remaining, but that is a rather uninteresting one ( I think I am approaching a state where I can actually focus on the other TODOs mentioned in my other comments above. |
I started this work because I'm also working on adding Connect RPC support to tonic. So having proper protobuf json support is the first step into achieving that goal.
This (unfortunately very big) PR adds json/serde support to prost. This is still a WIP effort but it is almost feature complete and passes all conformance tests except tests related to fieldmasks.
A non-exhaustive list of TODOs:
Any
implementationSo I am just putting this up here to get a general vibe check...