3

I have been trying to setup the following configuration for the serialport crate in Rust with serde, so I can intuitively supply 7 in my config for data_bits, but it will be deserialized as serialport::DataBits::Seven. Unfortunately, it seemingly fails the moment I want it to be a number (7) and not a string (seven).

Test case

cargo.toml

[package] name = "serde_error" version = "0.1.0" authors = ["Jason Miller"] edition = "2018" [dependencies] serialport = "3.3.0" serde = { version = "1.0", features = ["derive"] } ron = "0.5.1" 

The following results in the error:

6:16: Expected identifier 

main.rs

use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] #[serde(remote = "serialport::DataBits")] pub enum DataBitsDef { #[serde(rename = "5")] Five, #[serde(rename = "6")] Six, #[serde(rename = "7")] Seven, #[serde(rename = "8")] Eight, } fn default_data_bits() -> serialport::DataBits { serialport::DataBits::Eight } #[derive(Debug, Serialize, Deserialize)] pub struct TransceiverSettings { pub vid: u16, pub pid: u16, pub baud_rate: u32, #[serde(default = "default_data_bits", with = "DataBitsDef")] pub data_bits: serialport::DataBits, } impl Default for TransceiverSettings { fn default() -> Self { Self { vid: 0x2341, pid: 0x0043, baud_rate: 115_200, data_bits: serialport::DataBits::Eight, } } } const TRX_CONFIG: &str = " ( vid: 0x2341, pid: 0x0043, baud_rate: 9600, data_bits: 7, ) "; fn main() { match ron::de::from_str::<TransceiverSettings>(&TRX_CONFIG) { Err(e) => eprintln!("{}", e), Ok(c) => println!("{:?}", c), } } 

Oddly enough, writing 7 as seven succeeds and returns:

TransceiverSettings { vid: 9025, pid: 67, baud_rate: 9600, data_bits: Seven } 

main.rs

use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] #[serde(remote = "serialport::DataBits")] pub enum DataBitsDef { #[serde(rename = "5")] Five, #[serde(rename = "6")] Six, #[serde(rename = "seven")] Seven, #[serde(rename = "8")] Eight, } fn default_data_bits() -> serialport::DataBits { serialport::DataBits::Eight } #[derive(Debug, Serialize, Deserialize)] pub struct TransceiverSettings { pub vid: u16, pub pid: u16, pub baud_rate: u32, #[serde(default = "default_data_bits", with = "DataBitsDef")] pub data_bits: serialport::DataBits, } impl Default for TransceiverSettings { fn default() -> Self { Self { vid: 0x2341, pid: 0x0043, baud_rate: 115_200, data_bits: serialport::DataBits::Eight, } } } const TRX_CONFIG: &str = " ( vid: 0x2341, pid: 0x0043, baud_rate: 9600, data_bits: seven, ) "; fn main() { match ron::de::from_str::<TransceiverSettings>(&TRX_CONFIG) { Err(e) => eprintln!("{}", e), Ok(c) => println!("{:?}", c), } } 

serde_repr

One of the given examples in the serde documentation seems relevant to my case, but I haven't managed to get it working with my setup.

Serialize enum as number
The serde_repr crate provides alternative derive macros that derive the same Serialize and Deserialize traits but delegate to the underlying representation of a C-like enum. This allows C-like enums to be formatted as integers rather than strings in JSON

#[derive(Serialize_repr, Deserialize_repr, PartialEq, Debug)] #[repr(u8)] enum SmallPrime { Two = 2, Three = 3, Five = 5, Seven = 7, } 
0

1 Answer 1

6

Support for #[serde(remote)] is not present in serde_repr 0.1.5. You will need to submit a pull request or issue to add support for it.

Instead, follow the advice in How to transform fields during deserialization using Serde? and How to transform fields during serialization using Serde?:

use serde::{Deserialize, Serialize}; mod shim { use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; use serialport::DataBits; pub fn serialize<S>(v: &DataBits, s: S) -> Result<S::Ok, S::Error> where S: Serializer, { use DataBits::*; let v: u8 = match v { Five => 5, Six => 6, Seven => 7, Eight => 8, }; v.serialize(s) } pub fn deserialize<'de, D>(d: D) -> Result<DataBits, D::Error> where D: Deserializer<'de>, { use DataBits::*; match u8::deserialize(d)? { 5 => Ok(Five), 6 => Ok(Six), 7 => Ok(Seven), 8 => Ok(Eight), o => Err(D::Error::custom(format_args!("Invalid value {}", o))), } } } #[derive(Debug, Serialize, Deserialize)] pub struct TransceiverSettings { pub vid: u16, pub pid: u16, pub baud_rate: u32, #[serde(default = "default_data_bits", with = "shim")] pub data_bits: serialport::DataBits, } 
Sign up to request clarification or add additional context in comments.

1 Comment

That's a wonderful solution! I've looked at serialize_with and deserialize_with and couldn't wrap my head around it until now. I could even extend this for the baud_rate now, with only select values being valid. Thank you!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.