Skip to content

Serializer unable to serialize nested enums, Error:"top-level serializer supports only maps and structs" #118

@Orkking2

Description

@Orkking2

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 an Err value: 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 with RUST_BACKTRACE=full for a verbose backtrace.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions