Skip to main content

tuwunel_core/config/regenerate/
overlay.rs

1use std::sync::LazyLock;
2
3use figment::{
4	Figment, Profile, Provider, Source,
5	value::{Dict, Value},
6};
7use itertools::Itertools as _;
8use regex::RegexSet;
9
10use super::super::KNOWN_KEYS;
11use crate::{Err, Result, err};
12
13static NONCONFIG_KEYS: LazyLock<Result<RegexSet, regex::Error>> =
14	LazyLock::new(|| RegexSet::new(KNOWN_KEYS));
15
16pub(super) fn validate_file_overlay(figment: &Figment) -> Result {
17	let profiles = Provider::data(figment)?;
18	let unsupported = profiles
19		.iter()
20		.filter(|(profile, values)| {
21			!values.is_empty() && **profile != Profile::Default && **profile != Profile::Global
22		})
23		.map(|(profile, _)| profile.as_str())
24		.join(", ");
25
26	if !unsupported.is_empty() {
27		return Err!("Unsupported configuration profiles: {unsupported}.");
28	}
29
30	let valid = profiles
31		.values()
32		.flat_map(Dict::values)
33		.all(|value| file_tree(figment, value));
34
35	if !valid {
36		return Err!("The regeneration overlay contains a value not sourced from a file.");
37	}
38
39	Ok(())
40}
41
42fn file_tree(figment: &Figment, value: &Value) -> bool {
43	match value {
44		| Value::Dict(_, values) => values
45			.values()
46			.all(|value| file_tree(figment, value)),
47		| Value::Array(_, values) => values
48			.iter()
49			.all(|value| file_tree(figment, value)),
50		| _ => is_file_value(figment, value),
51	}
52}
53
54pub(super) fn is_file_value(figment: &Figment, value: &Value) -> bool {
55	figment
56		.get_metadata(value.tag())
57		.and_then(|metadata| metadata.source.as_ref())
58		.and_then(Source::file_path)
59		.is_some()
60}
61
62pub(super) fn filter_nonconfig_environment(figment: &Figment, values: &mut Dict) -> Result {
63	let known = NONCONFIG_KEYS
64		.as_ref()
65		.map_err(|error| err!("Invalid known configuration key expression: {error}"))?;
66
67	values.retain(|key, value| !known.is_match(key) || is_file_value(figment, value));
68
69	Ok(())
70}