use crate::db::DbPool;
use crate::error::AppResult;
use crate::runtime_config::descriptor::{
RuntimeConfigGeneratedValue, RuntimeConfigRegistry, RuntimeConfigVisibilityCondition,
};
use crate::runtime_config::snapshot::RuntimeConfigSnapshot;
use crate::runtime_config::store::upsert_value_if_missing;
use serde_json::Value;
use std::collections::BTreeMap;
use std::fmt::Write as _;
pub async fn initialize_generated_values(
pool: &DbPool,
registry: &RuntimeConfigRegistry,
service_key: &str,
stored: &BTreeMap<(String, String), Value>,
actor: Option<&str>,
) -> AppResult<Vec<(String, String)>> {
let snapshot = RuntimeConfigSnapshot::resolve(registry, service_key, stored);
let mut generated = Vec::new();
for descriptor in registry.iter() {
let descriptor_service = descriptor.scope.as_service_key();
if descriptor_service != "*" && descriptor_service != service_key {
continue;
}
let Some(RuntimeConfigGeneratedValue::Secret { bytes, when }) = &descriptor.generated
else {
continue;
};
if !condition_matches(when, service_key, &snapshot) {
continue;
}
let value = serde_json::json!(generate_secret(*bytes));
if upsert_value_if_missing(pool, descriptor_service, &descriptor.key, &value, actor)
.await?
.is_some()
{
generated.push((descriptor_service.to_owned(), descriptor.key.to_owned()));
}
}
Ok(generated)
}
fn condition_matches(
condition: &RuntimeConfigVisibilityCondition,
service_key: &str,
snapshot: &RuntimeConfigSnapshot,
) -> bool {
match condition {
RuntimeConfigVisibilityCondition::Equals {
service,
key,
value,
} if *service == "*" || *service == service_key => {
snapshot.raw(key).is_some_and(|current| current == value)
}
RuntimeConfigVisibilityCondition::Equals { .. } => false,
}
}
fn generate_secret(bytes: usize) -> String {
let mut raw = vec![0_u8; bytes];
getrandom::fill(&mut raw).expect("OS randomness should be available");
let mut out = String::with_capacity(bytes * 2);
for byte in raw {
let _ = write!(&mut out, "{byte:02x}");
}
out
}