lib для обработки конфигурации эргономичным c способом - PullRequest
1 голос
/ 28 февраля 2020

Я пишу общую библиотеку для загрузки и обработки конфигурации для моих приложений,
, используя config crate .

Я пытаюсь сделать ее эргономичной c как возможно для пользователя, но не может понять это.

мой lib.rs:

impl RunEnv {
    fn to_string(&self) -> String {
        match self {
            RunEnv::Production => "prod".to_string(),
            RunEnv::Dev => "dev".to_string(),
            RunEnv::Staging => "stag".to_string(),
        }
    }
}

impl FromStr for RunEnv {
    type Err = String;

    fn from_str(s: &str) -> Result<RunEnv, String> {
        match s {
            "dev" => Ok(RunEnv::Dev),
            "stag" => Ok(RunEnv::Staging),
            "prod" => Ok(RunEnv::Production),
            _ => Err(format!("Could not parse {:?}", s)),
        }
    }
}

#[derive(Debug, StructOpt)]
#[structopt(name = "CLI Options", about = "Common CLI options for running applications")]
pub struct Arguments {
    /// Run in a specific environment mode: dev, stag, prod.
    // short and long flags (-e, --env) will be deduced from the field's name
    #[structopt(short, long, default_value = "dev")]
    pub env: RunEnv,
}

pub trait LoadConfig {
    fn new() -> Result<Config, ConfigError>{
        const PACKAGE_NAME: &'static str = env!("CARGO_PKG_NAME");
        let mut s = Config::new();
        let args = Arguments::from_args();

        let mut conf_path = String::new();
        match args.env {
            RunEnv::Production => {
                conf_path = format!("/path/to/config/{}/config.toml", PACKAGE_NAME);
            }
            RunEnv::Staging => {
                conf_path = format!("/path/to/config/{}/config.toml", PACKAGE_NAME);
            }
            RunEnv::Dev => {
                conf_path = "tests/config.toml".to_string();
            }
        }

        // Start off by merging in the "default" configuration file
        s.merge(File::with_name(&conf_path))?;

        // Add in the current environment
        // Default to 'dev' env  
        s.set("run_mode", args.env.to_string())?;

        Ok(s)
    }
}

В моем приложении:

setting.rs

#[derive(Debug, Deserialize)]
pub struct Server {
    port: u32,
    address: String,
}

#[derive(Debug, Deserialize)]
pub struct Settings {
   server: Server,
   run_mode: Option<String>,
}
impl LoadConfig for Settings {}

main.rs

fn main() {
    let conf: Settings = Settings::new().unwrap().try_into().unwrap();
    println!("{:?}", conf);

И вот моя проблема, Я пытаюсь «спрятать» часть .unwrap().try_into().unwrap();, чтобы пользователь lib нужно только определить его setting.rs и запустить let conf: Settings = Settings::new()

Если я перемещу .try_into() внутри черты, я получаю ошибку, я не могу найти способ go вокруг:

   |         s.try_into()
   |           ^^^^^^^^ the trait `_IMPL_DESERIALIZE_FOR_Configuration::_serde::Deserialize<'_>` is not implemented for `config::config::Config`

Я новичок в ржавчине и, возможно, скучаю по некоторым очевидным вещам

1 Ответ

1 голос
/ 02 марта 2020

По соглашению, функция new - это способ построения экземпляра, и он должен быть внутренним методом struct, методом, доступным непосредственно для типа.

В вашем примере вы пытаетесь определить new как метод черты по умолчанию. Если это вообще возможно, подпись должна быть:

pub trait LoadConfig {
  fn new() -> Self {

  }
}

Такой метод черты невозможно реализовать, потому что черта не знает ничего о Self конкретном типе.

Чтобы следовать этому В соответствии с соглашением лучше всего переименовать метод черты LoadConfig::new во что-то другое:

pub trait LoadConfig {
  fn load() -> Result<Config, ConfigError>;
}

Затем реализуйте новую функцию конструктора как встроенный метод, например:

impl Settings {

    fn new() -> Settings {
        let config = Settings::load().unwrap(); // TBD: manage errors

        let port: u32 = config.get("port").unwrap_or("8080").parse().unwrap();

        Settings {
            server: Server {
                port: port,
                address: config.get("address").unwrap_or("localhost").to_owned()
            },
            run_mode: None
        }
    }
}

Обратите внимание, что надежная реализация не должна unwrap, но должна обрабатывать более явные ошибки конфигурации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...