Serializer unable to serialize nested enums, Error:"top-level serializer supports only maps and structs"
I was trying to find a way to encode URL parameters into definite enums so there's no guessing if a parameter is valid or not, but the solution I figured out doesn't work :(
Here is the code and its output. As you can see, it works with the JSON serializer and formats the output into a map, but the url_encoder doesn't exhibit the same behaviour?
A possible (dirty) solution to my problem in particular would be to first serialize my custom type into a JSON value and then pass that into the URL serializer. There isn't really a problem with this solution besides being clunky and unintuitive, but I was wondering if the url_encoder could be expanded to include serialization targets that are able to be coerced into struct/map representations. Just like how the JSON output is a dictionary, so too could the url_encoder represent this case as a map.
I'm not super familiar with how serializers work, sorry 😞
use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_json::to_value;
#[derive(Serialize, Deserialize)]
enum Params {
EnumA(EnumA),
EnumB(EnumB),
}
trait IntoParam {
fn into_param(self) -> Params;
}
// trait FromParam {
// fn from_param(param: Params) -> Self;
// }
#[derive(Serialize, Deserialize)]
// #[serde(tag = "EnumA")]
enum EnumA {
VarA,
VarB,
}
impl IntoParam for EnumA {
fn into_param(self) -> Params {
Params::EnumA(self)
}
}
#[derive(Serialize, Deserialize)]
// #[serde(tag = "EnumB")]
enum EnumB {
VarC,
#[serde(untagged)]
Base(EnumA),
}
impl IntoParam for EnumB {
fn into_param(self) -> Params {
Params::EnumB(self)
}
}
fn main() -> () {
println!("EnumA::VarA.into_param(), {}", to_value(EnumA::VarA.into_param()).unwrap().to_string());
println!("EnumA::VarB.into_param(), {}", to_value(EnumA::VarB.into_param()).unwrap().to_string());
println!("EnumB::VarC.into_param(), {}", to_value(EnumB::VarC.into_param()).unwrap().to_string());
println!("EnumB::Base(EnumA::VarA).into_param(), {}", to_value(EnumB::Base(EnumA::VarA).into_param()).unwrap().to_string());
let client = Client::new();
let request = client.get("https://google.com").query(&EnumB::Base(EnumA::VarA).into_param()).build().unwrap();
println!("Reqwest url, {}", request.url())
}
EnumA::VarA.into_param(), {"EnumA":"VarA"} EnumA::VarB.into_param(), {"EnumA":"VarB"} EnumB::VarC.into_param(), {"EnumB":"VarC"} EnumB::Base(EnumA::VarA).into_param(), {"EnumB":"VarA"} thread 'main' panicked at src/scripts/analyze_endpoints.rs:58:104: called
Result::unwrap()on anErrvalue: reqwest::Error { kind: Builder, source: Custom("top-level serializer supports only maps and structs") } stack backtrace: 0: rust_begin_unwind at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/panicking.rs:645:5 1: core::panicking::panic_fmt at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/panicking.rs:72:14 2: core::result::unwrap_failed at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/result.rs:1654:5 3: core::result::Result<T,E>::unwrap at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/result.rs:1077:23 4: analyze_endpoints::main at ./src/scripts/analyze_endpoints.rs:58:17 5: core::ops::function::FnOnce::call_once at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/ops/function.rs:250:5 note: Some details are omitted, run withRUST_BACKTRACE=fullfor a verbose backtrace.
Note that the following executes with no errors and produces the indented output:
use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_json::to_value;
#[derive(Serialize, Deserialize)]
enum Params {
EnumA(EnumA),
EnumB(EnumB),
}
trait IntoParam {
fn into_param(self) -> Params;
}
// trait FromParam {
// fn from_param(param: Params) -> Self;
// }
#[derive(Serialize, Deserialize)]
// #[serde(tag = "EnumA")]
enum EnumA {
VarA,
VarB,
}
impl IntoParam for EnumA {
fn into_param(self) -> Params {
Params::EnumA(self)
}
}
#[derive(Serialize, Deserialize)]
// #[serde(tag = "EnumB")]
enum EnumB {
VarC,
#[serde(untagged)]
Base(EnumA),
}
impl IntoParam for EnumB {
fn into_param(self) -> Params {
Params::EnumB(self)
}
}
fn main() -> () {
println!("EnumA::VarA.into_param(), {}", to_value(EnumA::VarA.into_param()).unwrap().to_string());
println!("EnumA::VarB.into_param(), {}", to_value(EnumA::VarB.into_param()).unwrap().to_string());
println!("EnumB::VarC.into_param(), {}", to_value(EnumB::VarC.into_param()).unwrap().to_string());
println!("EnumB::Base(EnumA::VarA).into_param(), {}", to_value(EnumB::Base(EnumA::VarA).into_param()).unwrap().to_string());
let client = Client::new();
let request = client.get("https://google.com").query(&to_value(EnumB::Base(EnumA::VarA).into_param()).unwrap()).build().unwrap();
println!("Reqwest url, {}", request.url())
}
EnumA::VarA.into_param(), {"EnumA":"VarA"} EnumA::VarB.into_param(), {"EnumA":"VarB"} EnumB::VarC.into_param(), {"EnumB":"VarC"} EnumB::Base(EnumA::VarA).into_param(), {"EnumB":"VarA"} Reqwest url, https://google.com/?EnumB=VarA