Custom Deserialize, choose Struct from value?

Below is a minimalized example of my problem. I have a Struct that has multiple Algorithm choices implemented by traits. I can serialize easily enough into a simple tuple of (algo_name, value), but I'm having difficulty coming up with a way to universally deserialize that string tuple into it's appropriate Algorithm bound Struct.

The implementation I've got below works fine when I already know the bound trait to use. I get it.. that's how everything is supposed to work, hooray!

But how do I choose an Algorithm from it's string representation? How do I map strings to Structs in a way that doesn't blow up trait bounds? I'm learning that Rust has a pretty strong wall between generics/bounds and values, and I fear I may have coded myself into a very nasty corner.

The options I see so far are:

  1. refactor the Algorithm trait into a big nasty enum, which in my case would be a pretty big rewrite I would like to avoid.
  2. write a custom, unattached to the Deserialize trait, function that does some sort of peek inside the serialized string before deserializing.

I'm hoping there's an elegant, idiomatic solution I'm not seeing.

use std::marker::PhantomData;
use serde::{Deserialize, Deserializer, Serialize, Serializer, ser::SerializeTuple, de::{SeqAccess, Visitor, Error}};
use serde_json::json; // 1.0.85

#[derive(Debug)] struct AlgoOne;
#[derive(Debug)] struct AlgoTwo;

trait Algorithm {
    const ALGORITHM: &'static str;
}

impl Algorithm for AlgoOne {
    const ALGORITHM: &'static str = "AlgoOne";
}

impl Algorithm for AlgoTwo {
    const ALGORITHM: &'static str = "AlgoTwo";
}

#[derive(Debug)]
struct Signature<T> {
    algo: PhantomData<T>,
    data: String
}

impl<T> Signature<T> where T: Algorithm {
    fn new(data: String) -> Self {
        Self {
            algo: PhantomData,
            data
        }    
    }
    
    fn algorithm(&self) -> &'static str {
        T::ALGORITHM
    }
}

impl<T> Serialize for Signature<T> where T: Algorithm {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
        let a = self.algorithm();
        
        let mut s = serializer.serialize_tuple(2)?;
        s.serialize_element(a)?;
        s.serialize_element(&self.data)?;
        
        s.end()
    }
}

struct SignatureVisitor;

impl<'de> Visitor<'de> for SignatureVisitor {
    type Value = (&'de str, &'de str);

    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        formatter.write_str("a tuple of two strings")
    }

    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
    where
        A: SeqAccess<'de>,
    {
        let l: &'de str = seq.next_element()?.unwrap();
        let r: &'de str = seq.next_element()?.unwrap();

        Ok((l, r))
    }
}

impl<'de, T> Deserialize<'de> for Signature<T>
where
    T: Algorithm,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let g = deserializer.deserialize_tuple(2, SignatureVisitor)?;
        
        if g.0 != T::ALGORITHM {
            Err(Error::custom("Invalid Tuple Type"))
        } else {
            Ok(Self::new(g.1.to_string()))
        }
    }
}
    
fn main() {
    let a: Signature<AlgoOne> = Signature::new("steve".to_owned());
    
    let j = serde_json::to_string(&a).unwrap();
    
    dbg!(j); // ["AlgoOne","steve"]
    
    let k = json!(("AlgoTwo", "horace")).to_string();
    
    dbg!(&k); // ["AlgoTwo","horace"]
    
    let r: Result<Signature<AlgoTwo>, serde_json::Error> = serde_json::from_str(&k);
    match r {
        Err(e) => println!("Error: {}", e.to_string()),
        Ok(r) => println!("{:?}", r),
    }
}

(Playground)

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.