-
-
Notifications
You must be signed in to change notification settings - Fork 772
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 a deserialize_option_with
helper
#576
Comments
This is as close as I can get, minus the leftover cruft: pub fn deserialize_option_with<T, D, F>(d: &mut D, f: F)
-> result::Result<Option<T>, D::Error>
where T: serde::Deserialize,
D: serde::Deserializer,
F: for<D1: Deserializer> Fn(&mut D1) -> result::Result<T, D1::Error>
{
/// Declare an internal visitor type to handle our input.
struct OptVisitor<F> {
wrapped_fn: F,
}
impl<T, F> serde::de::Visitor for OptVisitor<F>
where T: serde::Deserialize,
F: for<D1: Deserializer> Fn(&mut D1) -> result::Result<T, D1::Error>
{
type Value = Option<T>;
fn visit_none<E>(&mut self) -> result::Result<Self::Value, E>
where E: serde::Error
{
Ok(None)
}
fn visit_some<D>(&mut self,
deserializer: &mut D)
-> result::Result<Self::Value, D::Error>
where D: serde::de::Deserializer
{
self.wrapped_fn(deserializer).map(Some)
}
}
d.deserialize_option(OptVisitor { wrapped_fn: f, phantom: PhantomData })
} Unfortunately, the syntax |
I'm finding it hard to work around this problem with other approaches. For example, if we restrict our /// Deserialize a type that we can parse using `std::str::FromStr`.
fn deserialize_parsable<D, T>(deserializer: &mut D) -> result::Result<T, D::Error>
where D: serde::Deserializer,
T: str::FromStr,
<T as str::FromStr>::Err: fmt::Display
{
try!(String::deserialize(deserializer))
.parse()
.map_err(|e| serde::Error::custom(format!("{}", e)))
}
/// Deserialize an `Option` wrapping a type that we can parse using
/// `std::str::FromStr`.
fn deserialize_parsable_opt<D, T>(deserializer: &mut D)
-> result::Result<Option<T>, D::Error>
where D: serde::Deserializer,
T: str::FromStr,
<T as str::FromStr>::Err: fmt::Display
{
/// Declare an internal visitor type to handle our input.
struct OptVisitor<T>(PhantomData<T>);
impl<T> serde::de::Visitor for OptVisitor<T>
where T: str::FromStr,
<T as str::FromStr>::Err: fmt::Display
{
type Value = Option<T>;
fn visit_none<E>(&mut self) -> result::Result<Self::Value, E>
where E: serde::Error
{
Ok(None)
}
fn visit_some<D>(&mut self,
deserializer: &mut D)
-> result::Result<Self::Value, D::Error>
where D: serde::de::Deserializer
{
deserialize_parsable(deserializer).map(Some)
}
}
deserializer.deserialize_option(OptVisitor(PhantomData))
} But this fails with:
…because we can apparently only implement visitor for I think I need to define a custom wrapper type. |
OK, this is the closest working approximation that I can find to a generic /// Deserialize a type that we can parse using `std::str::FromStr`.
fn deserialize_parsable<D, T>(deserializer: &mut D) -> result::Result<T, D::Error>
where D: serde::Deserializer,
T: str::FromStr,
<T as str::FromStr>::Err: fmt::Display
{
try!(String::deserialize(deserializer))
.parse()
.map_err(|e| serde::Error::custom(format!("{}", e)))
}
/// Deserialize an `Option` wrapping a type that we can parse using
/// `std::str::FromStr`.
fn deserialize_parsable_opt<D, T>(deserializer: &mut D)
-> result::Result<Option<T>, D::Error>
where D: serde::Deserializer,
T: str::FromStr,
<T as str::FromStr>::Err: fmt::Display
{
/// A wrapper type that allows us to declare `deserialize` for
/// something carrying an `Option<T>` value.
struct Wrap<T>(Option<T>);
#[allow(unused_qualifications)]
impl<T> serde::Deserialize for Wrap<T>
where T: str::FromStr,
<T as str::FromStr>::Err: fmt::Display
{
fn deserialize<D>(deserializer: &mut D) -> result::Result<Self, D::Error>
where D: serde::Deserializer
{
/// Declare an internal visitor type to handle our input.
struct OptVisitor<T>(PhantomData<T>);
impl<T> serde::de::Visitor for OptVisitor<T>
where T: str::FromStr,
<T as str::FromStr>::Err: fmt::Display
{
type Value = Wrap<T>;
fn visit_none<E>(&mut self) -> result::Result<Self::Value, E>
where E: serde::Error
{
Ok(Wrap(None))
}
fn visit_some<D>(&mut self,
deserializer: &mut D)
-> result::Result<Self::Value, D::Error>
where D: serde::de::Deserializer
{
deserialize_parsable(deserializer).map(|v| Wrap(Some(v)))
}
}
deserializer.deserialize_option(OptVisitor(PhantomData))
}
}
Wrap::deserialize(deserializer).map(|wrap| wrap.0)
} The extra warning: unnecessary qualification
--> src/project_config.in.rs:50:13
|
50 | impl<T> serde::Deserialize for Wrap<T>
| ^^^^^^^^^^^^^^^^^^
| Since it's relatively difficult to write even a partial workaround for something like |
I ran into a similar problem when I had a I'm not sure |
Yes we definitely want this for vecs and maps too - tracked in #723. @TedDriggs would you be interested in working on a PR for this? |
Interested? Yes. Unfortunately, looking at the code I think this might be beyond my Rust skills at the moment, and the needs of my work project ended up being better solved by defining a local type so my time to take this on would be more limited. |
I am folding this into #723 because we would like an approach that applies to sequence and map types as well as Option. |
I've been making heavy use of serde, and it's been a real help!
Use case
Imagine I have a custom deserialization function for type
semver::VersionReq
, which we can parse from a string:But when I want to use it, it's sometimes wrapped in
Option
:Possible implementation
I would like to be able to write another helper function:
…which calls a hypothetical
deserialize_option_with
that has the following signature:Unfortunately, I find it extremely hard to store type
F
inside aVisitor
implementation, so I can't provide an implementation.The text was updated successfully, but these errors were encountered: