shiny_configuration/configuration_provider/
json5_provider.rs

1use crate::configuration_provider::{ConfigurationProvider};
2use crate::value::Value;
3use std::fs;
4use std::io;
5use std::path::{Path, PathBuf};
6use std::str::FromStr;
7use thiserror::Error;
8
9#[derive(Debug)]
10pub struct Json5Provider(Value);
11
12#[derive(Error, Debug)]
13pub enum CreateJson5ProviderFromFileError {
14    /// Failed to read file
15    ///
16    /// Example: File is missing
17    #[error("Failed to read configuration file [file_path = `{file_path}`]")]
18    FailedToReadFile {
19        file_path: PathBuf,
20        #[source]
21        error: io::Error,
22    },
23
24    /// Failed to parse file
25    ///
26    /// Example: Invalid formatting
27    #[error("Failed to parse configuration file [file_path = `{file_path}`]")]
28    FailedToDeserialize {
29        file_path: PathBuf,
30        #[source]
31        error: json5::Error,
32    },
33}
34
35#[derive(Error, Debug)]
36#[error(transparent)]
37pub struct DeserializeJson5Error(#[from] json5::Error);
38
39#[derive(Error, Debug)]
40pub enum InvalidConfigurationError {
41    #[error("Key is not a string")]
42    KeyIsNotAString,
43
44    #[error("Tags not supported")]
45    TagsNotSupported,
46
47    #[error("Internal error")]
48    InternalError,
49}
50
51impl Json5Provider {
52    pub fn from_path(file_path: impl AsRef<Path>) -> Result<Self, CreateJson5ProviderFromFileError> {
53        let configuration = match fs::read_to_string(file_path.as_ref()) {
54            Ok(v) => v,
55            Err(error) => {
56                let fp = file_path.as_ref().to_path_buf();
57                return Err(CreateJson5ProviderFromFileError::FailedToReadFile {
58                    file_path: fp.canonicalize().unwrap_or(fp),
59                    error,
60                });
61            }
62        };
63
64        Self::from_str(&configuration).map_err(|error| {
65            let file_path = file_path.as_ref().to_path_buf();
66            CreateJson5ProviderFromFileError::FailedToDeserialize { file_path, error: error.0 }
67        })
68    }
69}
70
71impl ConfigurationProvider for Json5Provider {
72    fn provide(&self) -> Value {
73        self.0.clone()
74    }
75}
76
77impl FromStr for Json5Provider {
78    type Err = DeserializeJson5Error;
79
80    fn from_str(config: &str) -> Result<Self, Self::Err> {
81        match json5::from_str::<Value>(config) {
82            Ok(value) => Ok(Json5Provider(value)),
83            Err(err) => Err(DeserializeJson5Error(err))
84        }
85    }
86}