Ящик конфигурации Rust и полиморфные типы

#rust

#Ржавчина

Вопрос:

 #[derive(Debug, Deserialize)]
struct S3StorageConfig {
    url: String,
}

#[derive(Debug, Deserialize)]
struct LocalStorageConfig {
    root: std::path::PathBuf,
}

#[derive(Debug, Deserialize)]
struct StorageConfig {
    storage_type: String
}

#[derive(Debug, Deserialize)]
pub struct Config {
    storages: Vec<StorageConfig>
}

impl Config {
    pub fn new(path:Option<std::path::PathBuf>) -> Result<Self, config::ConfigError>  {
        let mut cfg = config::Config::default();

        if let Some(file_path) = path {
            cfg.merge(config::File::from(file_path)).unwrap();
        }

        cfg.merge(config::Environment::with_prefix("datastore"))?;


        cfg.try_into()
    }
}

 

Предположим, я хочу иметь конфигурацию, которая имеет

 [[storages]]
type: s3
url: ...
 

и

 [[storages]]
type: local
root: ...
 

И когда config это делает try_into , он может находить эти структуры и присваивать их правильным структурам по милости type поля.

Какую магию мне нужно сделать, чтобы это произошло?

Спасибо,

Комментарии:

1. Итак, в зависимости от type поля вы хотите try_into вернуть либо a S3StorageConfig , либо a LocalStorageConfig ? Из которого вы затем хотите вернуться Config::new ?

2. Вы смотрели на представления Enum в Serde docs?

Ответ №1:

Итак, я не уверен на 100%, чего вы пытаетесь достичь здесь, но вы можете сериализовать / десериализовать в нужные вам типы serde и использовать enum вместо этого.

Пример:

 // This enum takes the place of your 'S3StorageConfig' and 'LocalStorageConfig'.
#[derive( Serialize, Deserialize, Debug )]
#[serde( tag = "type" )]
enum Storage {
    Cloud{ url: String },
    Local{ root: PathBuf },
}

fn main( ) {
    let vec = vec![ 
        Storage::Cloud{ url: "www.youtube.com".to_string( ) },
        Storage::Local{ root: PathBuf::from( "C:\Windows\Fonts" ) },
    ];

    let storage = serde_json::to_string( amp;vec ).unwrap( );

    let vec: Vec<Storage> = serde_json::from_str( amp;storage ).unwrap( );
    println!( "{:#?}", vec );
}
 

Теперь вы вернете Storage enum вариант из своего Config класса.
Вам не нужно impl TryInto , если это направление, которое вы решили выбрать.

 impl Config {
    pub fn new( ) -> Result<Storage, config::ConfigError> {
        // Read in the file here and use 'serde' to deserialize the
        // content of the file into the correct enum variant that you
        // can now return.
    }
}
 

Комментарии:

1. Спасибо, это означает, что я не могу использовать ящик конфигурации, но я думаю, это нормально?