jsonrustserde

How do check for Some vs None in multiple nested enums at once?


I am looking for a way to use a functional event-type mechanism in my project. Nothing is in production yet so this can even be rewritten from scratch (currently using Rocket).

I want to look for 2 things:

I've done the latter but I have some issues with the first one. I am receiving JSON requests that look like this:

{
    "source_id": "user:asdf",
    "key": "temperature",
    "value": 25
}

I've set up the following structs and enums:

use serde::Deserialize;

#[derive(Deserialize, Debug)]
pub struct EventPayload<T> {
    pub key: String,
    pub tag: Option<String>,
    pub value: Option<T>,
}

#[derive(Deserialize, Debug)]
#[serde(untagged)]
pub enum EventValue {
    String(EventPayload<String>),
    Float(EventPayload<f32>),
    Int(EventPayload<i32>),
    Bool(EventPayload<bool>),
}

#[derive(Deserialize, Debug)]
pub struct PublishSingleValueEventRequest {
    pub source_id: String,
    #[serde(flatten)]
    pub manifest: EventValue,
}

Problems appear when trying to access the values:

let event: PublishSingleValueEventRequest = ...;

/* This region does not compile
let evt_val: Option = match event.manifest {
    EventValue::String(x) => x.value,
    EventValue::Float(x) => x.value,
    EventValue::Int(x) => x.value,
    EventValue::Bool(x) => x.value
};

match evt_val {
    Some(x) => println!("Event value: {:?}", x),
    None => println!("No event value provided")
}
*/

println!("{:?}", event.manifest);
match event.manifest {
    EventValue::String(x) => {
        println!("String payload: {:?}", x.value);
    }

    EventValue::Float(x) => {
        println!("Float payload: {:?}", x.value);
    }

    EventValue::Int(x) => {
        println!("Int payload: {:?}", x.value);
    }
    
    EventValue::Bool(x) => {
        println!("Bool payload: {:?}", x.value);
    }
}

I would like this to be more like:

match event.manifest.value {
    Some(x) => ...,
    None => ...
}

instead of manually unwrapping the .value Option<> fields.


Solution

  • You can pattern match nested structurues:

    fn main() {
        let payload: EventPayload<i32> = EventPayload {
            key: "foo".to_string(),
            tag: None,
            value: Some(10),
        };
        let value = EventValue::Int(payload);
        
        match value {
            EventValue::Int(EventPayload {value: Some(x), ..}) => {
                println!("Some value: {}", x);
            }
            EventValue::String(EventPayload {value: Some(message), ..}) => {
                println!("Some message: {}", message);
            }
            _ => {
                println!("whatever");
            }
        }
    }
    

    Playground