Cleanest way to decode TOML string into PathBuf?

I'm using the toml crate to decode a TOML configuration file

I have a struct:

#[derive(RustcDecodable, Clone)]
pub struct Git {
    pub user: String,
    pub public_key: Option<String>,
    pub private_key: String,
    pub passphrase: Option<String>
}

For various reasons, I'd like to change it to:

#[derive(RustcDecodable, Clone)]
pub struct Git {
    pub user: String,
    pub public_key: Option<PathBuf>,
    pub private_key: PathBuf,
    pub passphrase: Option<String>
}

Except, when I do this, at runtime I get:

thread 'main' panicked at 'Couldn't deserialise config: DecodeError { field: Some("git.public_key"), kind: ExpectedType("array", "string") }', src/settings.rs:122

It appears that the implementation of Decodable for PathBuf reads an array, not a string.

Basically I think what I want would look something vaguely like:

impl Decodable for PathBuf {
    fn decode<D: Decoder>(d: &mut D) -> Result<PathBuf, D::Error> {
        match d.read_str() {
            Ok(s) => PathBuf::from?(s),
            _ => Err(d.error("Invalid value")),
        }
    }
}

(I actually want to extend this to use the shellexpand crate as well.)

Except of course PathBuf already implements Decodable and you can't override traits.

What's the cleanest way to do this? I guess I could implement Decodable manually on the Git struct, but I've got several structs where I'd like to do this and it seems a bit unclean.

Using rustc-serialize your best option would probably be to make a wrapper struct around PathBuf and implement Decodable for that. Serde can do things like this with the #[serde(deserialize_with = "some_function")] annotation.

2 Likes

Cheers! I actually just discovered serde's deserialize_with - looks like another good argument for us to move over to Serde. Hopefully Macros 1.1 stabilises in time for rust 1.15 so we don't have to use the stable codegen hacks...