-
Notifications
You must be signed in to change notification settings - Fork 4
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
Feature: nested struct shall be considered too #4
Comments
Hi @0cv, thanks for the feature request. Yes, flattened field names would be a killer feature for sure. I myself use this crate mostly for constructing queries for document databases like mongodb and elasticsearch. Both allow nested field updates with the Unfortunately I kind of shot myself in the foot when I decided to make My vision would be something like this: #[derive(FieldNamesAsArray)]
struct StructA {
a: String,
#[field_names_as_array(flatten)]
b: StructB
}
#[derive(FieldNamesAsArray)]
struct StructB {
c: String,
d: String
}
fn main() {
assert_eq!(StructA::FIELD_NAMES_AS_ARRAY, ["a", "b.c", "b.d"]);
} |
There is also a similar issue and some options discussed here https://stackoverflow.com/questions/71175052/how-to-parse-nested-struct-member-variables-in-rust-proc-macro |
I've started working on enabling nesting, but unfortunately I can't get around a problem I did not foresee. Right now it is impossible to iterate over a constant array, something we need to map the field names of |
Some updates here. I've a solution (for my needs), but it has some breaking changes, also some other features of that crate would not work anymore, so I'm just posting this solution here in case it's helpful to anyone: Replace derive_field_names_as_array of that crate by the following function
Then the previous example works:
Please note, that it's now a function, so it's a little bit different ... |
Thanks for your solution! It is what I meant by:
We can do the flattening at runtime, but not in a constant context or constant function. I really want |
Ok, I understand what you mean. I suppose a PR in that crate does not make sense, it's indeed not as good. In my use case, I've 100s (if not 1000s) of fields which far outweigh the little bit of overhead, and it's a cron job whose performance is very much secondary. If anyone finds this code useful, then that will be here anyway |
I will stop the spam here, but here is the final (fixed) version to support an Optional struct. Not sure whether there is not something better, because suddenly this was definitely not straightforward anymore
|
Again, thanks for sharing your code freely. Much appreciated! This is my personal opinion, but I think once we start considering generic type parameters like I think once we want to perform powerful operations like flattening types that are generic parameters and/or are wrapped in an enum, we need to fully commit to the idea that the field names are computed at runtime and for instances, not types. For example, I believe that this code is better at semantically expressing what the field names of #[derive(FieldNamesAsArray)]
struct StructA {
a: String,
#[field_names_as_array(flatten)]
b: Option<StructB>,
}
#[derive(FieldNamesAsArray)]
struct StructB {
c: String,
d: String,
}
fn main() {
let foo = StructA {
a: "hello".to_owned(),
b: Some(StructB {
c: "world".to_owned(),
d: "!".to_owned(),
}),
};
let bar = StructA { a: "hello".to_owned(), b: None };
assert_eq!(foo.field_names_as_array(), ["a", "b.c", "b.d"]);
assert_eq!(bar.field_names_as_array(), ["a"]); // b is None and None has no fields
} If we were to do field name computation at runtime like this I actually believe that we can and probably should implement this functionality in a serde |
I see, that makes sense for serializing So to give a bit of background as to why I need(ed) this, I use this during the Deserialization phase. Actually, I'm querying a database (Salesforce) which may return null fields and except some ID and system fields, almost all other fields are potentially null, including the fields of (non system) relationship struct - hence |
Interesting. May I ask how exactly you are using the field names to deserialize a struct? Like I said above, I originally developed this crate to create database queries. I'd love to hear more about your use-case to better understand the possible applications of this crate. |
Actually it's pretty simple. I use the same struct for building my query and retrieving the results:
I want to query all fields, but there is usually no guarantee on whether fields are non null, incl. relationship. Also, I've simplified various aspects, but that's at high level roughly how it works. |
Thanks for explaining your use-case to me. That is pretty close to my original use-case. Only difference I see is that you use the fields to retrieve data from an SQL database whereas I originally used the fields array to update documents in a document database. The more I think about it, the more I'm wondering whether there is a better way to construct queries like this statically from Rust types. But what about support for multiple queries with different fields from the same struct? And at which point would this crate be yet another query builder? And what about completely different use-cases beyond query construction? I'm not sure how other people use this crate and what their expectations are. All I know is that I like the simplicity. It is a small crate solving a small problem, namely some sort of weak runtime introspection capabilities into the field names of a named struct. While I love the idea of supporting nested field names, I'm not so sure about handling special cases like |
Yes, that's clear that one crate can't support all kind of different systems how they retrieve, and update records. For example on Salesforce, there is no such thing as a SQL update statement to update records. Instead it's a REST API with a JSON payload, so in that situation I simply use the Serde Serializer and it's just right. That crate is ideal if one doesn't need nested structs. I've published the code I've written above in another crate. If you find a magical way to support (optional) nested struct on compilation, I will deprecate my crate but .. it's probably not a high priority, I think my crate is just solving an edge case of an edge case 😃 |
given
We shall have this:
assert_eq!(StructA::FIELD_NAMES_AS_ARRAY, ["a", "b.c", "b.d"]);
But at the moment we have:
assert_eq!(StructA::FIELD_NAMES_AS_ARRAY, ["a", "b"]);
The text was updated successfully, but these errors were encountered: