credence_lib/configuration/
credence.rs

1use super::{
2    super::{middleware::*, util::*},
3    caching::*,
4    encoding::*,
5    error::*,
6    files::*,
7    port::*,
8    render::*,
9    requests::*,
10    urls::*,
11};
12
13use {
14    compris::{annotate::*, normal::*, parse::*, resolve::*, *},
15    kutil::{
16        cli::depict::*,
17        http::{
18            cache::{Cache, CommonCacheKey},
19            tower::caching::*,
20            *,
21        },
22        std::immutable::*,
23    },
24    std::{collections::*, io, path::*},
25};
26
27//
28// CredenceConfiguration
29//
30
31/// Credence configuration.
32#[derive(Clone, Debug, Depict, Resolve)]
33pub struct CredenceConfiguration {
34    /// Definitions (ignored).
35    #[resolve]
36    #[depict(skip)]
37    pub definitions: Option<Variant<WithoutAnnotations>>,
38
39    /// Files.
40    #[resolve]
41    #[depict(as(depict))]
42    pub files: FilesConfiguration,
43
44    /// Ports.
45    #[resolve]
46    #[depict(iter(kv), key_style(number), as(depict))]
47    pub ports: BTreeMap<u16, Port>,
48
49    /// Requests.
50    #[resolve]
51    #[depict(as(depict))]
52    pub requests: RequestsConfiguration,
53
54    /// URLs.
55    #[resolve]
56    #[depict(as(depict))]
57    pub urls: UrlsConfiguration,
58
59    /// Render.
60    #[resolve]
61    #[depict(as(depict))]
62    pub render: RenderConfiguration,
63
64    /// Caching.
65    #[resolve]
66    #[depict(as(depict))]
67    pub caching: CachingConfiguration,
68
69    /// Encoding.
70    #[resolve]
71    #[depict(as(depict))]
72    pub encoding: EncodingConfiguration,
73}
74
75impl CredenceConfiguration {
76    /// Resolve.
77    pub fn read<ReadT>(reader: &mut ReadT, source: ByteString) -> Result<Self, ConfigurationError>
78    where
79        ReadT: io::Read,
80    {
81        let variant = with_annotations!(
82            Parser::new(Format::YAML)
83                .with_source(source)
84                .with_try_unsigned_integers(true)
85                .parse_reader(reader)
86                .map_err(io::Error::other)?
87        );
88
89        let mut errors = ResolveErrors::default();
90        let configuration = variant.resolve_with_errors(&mut errors).map_err(io::Error::other)?;
91        if errors.is_empty() { Ok(configuration.ok_or(ConfigurationError::None)?) } else { Err(errors.into()) }
92    }
93
94    /// Validate.
95    pub fn validate<PathT>(&mut self, base_path: PathT) -> Result<(), ConfigurationError>
96    where
97        PathT: AsRef<Path>,
98    {
99        for port in &mut self.ports.values_mut() {
100            port.validate(&base_path)?;
101        }
102
103        self.files.validate(base_path)
104    }
105
106    /// Caching layer
107    pub fn caching_layer<RequestBodyT, CacheT>(
108        &self,
109        cache: CacheT,
110    ) -> CachingLayer<RequestBodyT, CacheT, CommonCacheKey>
111    where
112        CacheT: Cache<CommonCacheKey>,
113    {
114        // For closure move
115        let skip_media_types = self.encoding.skip_media_types();
116
117        CachingLayer::default()
118            .cache(cache.clone())
119            .cacheable_by_default(self.caching.default)
120            .cache_key(|context| {
121                if let Some(socket) = context.request.extensions().get::<Socket>() {
122                    context.cache_key.host = Some(socket.host.clone());
123                }
124            })
125            .min_cacheable_body_size(self.caching.min_body_size.inner.into())
126            .max_cacheable_body_size(self.caching.max_body_size.inner.into())
127            .min_encodable_body_size(self.encoding.min_body_size.inner.into())
128            .encodable_by_default(self.encoding.default)
129            .encodable_by_response(move |context| match context.headers.content_type() {
130                Some(content_type) => !skip_media_types.contains(&content_type),
131                None => true,
132            })
133    }
134
135    /// Whether the URI path is hidden.
136    pub fn hide(&self, uri_path: &str) -> bool {
137        if uri_path_has_hidden_segment(uri_path) {
138            return true;
139        }
140
141        for hide in &self.urls.hide {
142            if hide.inner.is_match(uri_path) {
143                return true;
144            }
145        }
146
147        self.render.is_rendered_page(uri_path).is_some()
148    }
149
150    /// Rendered page URI path.
151    ///
152    /// "{path}" -> "{path}.r.yaml" or "{path}.r.*"
153    pub fn rendered_page_uri_path(&self, uri_path: &str) -> io::Result<Option<String>> {
154        let asset_path = self.files.asset(uri_path);
155        if let Some(base_file_name) = asset_path.file_name()
156            && let Some(parent) = asset_path.parent()
157            && parent.is_dir()
158        {
159            let base_file_name = base_file_name.to_string_lossy().into_owned() + &self.render.midfix;
160            for file_path in parent.read_dir()? {
161                let file_path = file_path?.path();
162                if let Some(file_name) = file_path.file_name() {
163                    let file_name = file_name.to_string_lossy();
164                    if file_name.starts_with(&base_file_name) {
165                        let extension = &file_name[base_file_name.len()..];
166                        return Ok(Some(String::from(uri_path) + &self.render.midfix + extension));
167                    }
168                }
169            }
170        }
171
172        Ok(None)
173    }
174}
175
176impl Default for CredenceConfiguration {
177    fn default() -> Self {
178        let mut port = Port::default();
179        port.name = "http".into();
180
181        Self {
182            files: Default::default(),
183            ports: BTreeMap::from([(8000, port)]),
184            requests: Default::default(),
185            caching: Default::default(),
186            encoding: Default::default(),
187            render: Default::default(),
188            urls: Default::default(),
189            definitions: None,
190        }
191    }
192}