|
| 1 | +use config::{Config, File, FileStoredFormat, Format, Map, Value, ValueKind}; |
| 2 | +use std::io::{Error, ErrorKind}; |
| 3 | + |
| 4 | +/// The private and public key sources will be read into their associated variable: |
| 5 | +#[derive(serde::Deserialize, Clone, Debug)] |
| 6 | +pub struct Settings { |
| 7 | + pub private_key: Option<String>, |
| 8 | + pub public_key: Option<String>, |
| 9 | +} |
| 10 | + |
| 11 | +fn main() { |
| 12 | + // Sourcing from two separate files for the `Settings` struct,: |
| 13 | + let file_public_key = File::new("examples/custom_file_format/files/public.pem", PemFile); |
| 14 | + let file_private_key = File::new("examples/custom_file_format/files/private.pem", PemFile); |
| 15 | + |
| 16 | + // Provide the sources and build the config object: |
| 17 | + // Both are marked as optional to avoid failure if the file doesn't exist. |
| 18 | + let settings = Config::builder() |
| 19 | + .add_source(file_public_key.required(false)) |
| 20 | + .add_source(file_private_key.required(false)) |
| 21 | + .build() |
| 22 | + .unwrap(); |
| 23 | + |
| 24 | + // Deserialize the config object into your Settings struct: |
| 25 | + let settings: Settings = settings.try_deserialize().unwrap(); |
| 26 | + println!("{:#?}", settings); |
| 27 | +} |
| 28 | + |
| 29 | +#[derive(Debug, Clone)] |
| 30 | +pub struct PemFile; |
| 31 | + |
| 32 | +impl Format for PemFile { |
| 33 | + fn parse( |
| 34 | + &self, |
| 35 | + uri: Option<&String>, |
| 36 | + text: &str, |
| 37 | + ) -> Result<Map<String, config::Value>, Box<dyn std::error::Error + Send + Sync>> { |
| 38 | + // Store any valid keys into this map, they'll be merged with other sources into the final config map: |
| 39 | + let mut result = Map::new(); |
| 40 | + |
| 41 | + // Identify the PEM encoded data type by the first occurrence found: |
| 42 | + // NOTE: This example is kept simple, multiple or other encoded types are not handled. |
| 43 | + let key_type = vec!["PUBLIC", "PRIVATE"] |
| 44 | + .into_iter() |
| 45 | + .find(|s| text.contains(s)); |
| 46 | + let key = match key_type { |
| 47 | + Some("PRIVATE") => "private_key", |
| 48 | + Some("PUBLIC") => "public_key", |
| 49 | + // Otherwise fail with an error message (the filename is implicitly appended): |
| 50 | + _ => { |
| 51 | + return Err(Box::new(Error::new( |
| 52 | + ErrorKind::InvalidData, |
| 53 | + "PEM file did not contain a Private or Public key", |
| 54 | + ))) |
| 55 | + } |
| 56 | + }; |
| 57 | + |
| 58 | + result.insert( |
| 59 | + key.to_owned(), |
| 60 | + Value::new(uri, ValueKind::String(text.into())), |
| 61 | + ); |
| 62 | + |
| 63 | + Ok(result) |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 67 | +// A slice of extensions associated to this format, when an extension |
| 68 | +// is omitted from a file source, these will be tried implicitly: |
| 69 | +impl FileStoredFormat for PemFile { |
| 70 | + fn file_extensions(&self) -> &'static [&'static str] { |
| 71 | + &["pem"] |
| 72 | + } |
| 73 | +} |
0 commit comments