use std::fmt;
use std::iter::FusedIterator;
use std::sync::{Arc, OnceLock};
use serde::de::DeserializeOwned;
use serde_json::Value;
use super::super::error::ConfigurationError;
use super::super::parameter_key_tuple::ParameterKeyTuple;
use super::super::parameter_path::ParameterPath;
use super::super::parameter_tree::{lookup_path, reconstruct};
use super::WorkloadConfigurationInner;
use super::reconstruction;
pub struct ResolvedConfiguration {
inner: Arc<WorkloadConfigurationInner>,
ordinal: u64,
resolved: OnceLock<Value>,
}
impl Clone for ResolvedConfiguration {
fn clone(&self) -> Self {
Self {
inner: Arc::clone(&self.inner),
ordinal: self.ordinal,
resolved: OnceLock::new(),
}
}
}
impl ResolvedConfiguration {
pub(super) fn new(inner: Arc<WorkloadConfigurationInner>, ordinal: u64) -> Self {
Self {
inner,
ordinal,
resolved: OnceLock::new(),
}
}
pub fn ordinal(&self) -> u64 {
self.ordinal
}
pub fn component(&self) -> &str {
&self.inner.component
}
pub fn workload(&self) -> &str {
&self.inner.workload
}
pub fn global_ordinal(&self) -> u64 {
self.inner.scope_ordinal(self.ordinal, 0)
}
pub fn component_ordinal(&self) -> u64 {
self.inner.scope_ordinal(self.ordinal, 1)
}
pub fn workload_ordinal(&self) -> u64 {
self.inner.scope_ordinal(self.ordinal, 2)
}
pub fn value(&self, key: &str) -> Option<&Value> {
let path = ParameterPath::parse(key)?;
if let Some(leaf) = self.inner.fixed_leaf(&path) {
return Some(&leaf.value);
}
if let Some(leaf) = self
.inner
.selected_leaves(self.ordinal)
.find(|leaf| leaf.path == path)
{
return Some(&leaf.value);
}
lookup_path(self.resolved_document(), &path)
}
pub fn require_value(&self, key: &str) -> Result<&Value, ConfigurationError> {
self.value(key)
.ok_or_else(|| ConfigurationError::UnknownConfigurationValue {
ordinal: self.ordinal,
key: key.to_owned(),
})
}
pub fn decode_value<T>(&self, key: &str) -> Result<T, ConfigurationError>
where
T: DeserializeOwned,
{
let Some(path) = ParameterPath::parse(key) else {
return Err(ConfigurationError::UnknownConfigurationValue {
ordinal: self.ordinal,
key: key.to_owned(),
});
};
if let Some(value) = self.exact_leaf(&path) {
return T::deserialize(value).map_err(|source| {
ConfigurationError::DecodeConfigurationValue {
ordinal: self.ordinal,
key: key.to_owned(),
source,
}
});
}
let subtree = reconstruct(
self.inner
.fixed_leaves()
.chain(self.inner.selected_leaves(self.ordinal))
.filter(|leaf| path.is_ancestor_of(&leaf.path)),
);
let Some(value) = lookup_path(&subtree, &path) else {
return Err(ConfigurationError::UnknownConfigurationValue {
ordinal: self.ordinal,
key: key.to_owned(),
});
};
T::deserialize(value).map_err(|source| ConfigurationError::DecodeConfigurationValue {
ordinal: self.ordinal,
key: key.to_owned(),
source,
})
}
pub fn decode_values<Values, Keys>(&self, keys: Keys) -> Result<Values, ConfigurationError>
where
Keys: ParameterKeyTuple<Values>,
{
keys.decode(self)
}
pub fn contains(&self, key: &str) -> bool {
let Some(path) = ParameterPath::parse(key) else {
return false;
};
self.inner
.fixed_leaves()
.any(|leaf| leaf.path == path || path.is_ancestor_of(&leaf.path))
|| self
.inner
.selected_leaves(self.ordinal)
.any(|leaf| leaf.path == path || path.is_ancestor_of(&leaf.path))
}
pub fn len(&self) -> usize {
self.inner.fixed_leaves().count() + self.inner.selected_leaves(self.ordinal).count()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn keys(&self) -> impl Iterator<Item = &str> {
self.inner
.fixed_leaves()
.map(|leaf| leaf.path.identifier())
.chain(
self.inner
.selected_leaves(self.ordinal)
.map(|leaf| leaf.path.identifier()),
)
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
self.inner
.fixed_leaves()
.map(|leaf| (leaf.path.identifier(), &leaf.value))
.chain(
self.inner
.selected_leaves(self.ordinal)
.map(|leaf| (leaf.path.identifier(), &leaf.value)),
)
}
pub fn to_json(&self) -> String {
self.resolved_document().to_string()
}
pub fn to_json_value(&self) -> Value {
self.resolved_document().clone()
}
fn resolved_document(&self) -> &Value {
self.resolved
.get_or_init(|| reconstruction::document(&self.inner, self.ordinal))
}
pub(crate) fn resolved_object(&self) -> &serde_json::Map<String, Value> {
self.resolved_document()
.as_object()
.expect("resolved configurations always form a JSON object")
}
fn exact_leaf(&self, path: &ParameterPath) -> Option<&Value> {
if let Some(leaf) = self.inner.fixed_leaf(path) {
return Some(&leaf.value);
}
self.inner
.selected_leaves(self.ordinal)
.find(|leaf| &leaf.path == path)
.map(|leaf| &leaf.value)
}
}
impl fmt::Debug for ResolvedConfiguration {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ResolvedConfiguration")
.field("component", &self.component())
.field("workload", &self.workload())
.field("ordinal", &self.ordinal)
.field("values", &self.len())
.finish_non_exhaustive()
}
}
#[derive(Clone)]
pub struct ConfigurationIter {
inner: Arc<WorkloadConfigurationInner>,
next: u64,
end: u64,
}
impl ConfigurationIter {
pub(super) fn new(inner: Arc<WorkloadConfigurationInner>, end: u64) -> Self {
Self {
inner,
next: 0,
end,
}
}
}
impl Iterator for ConfigurationIter {
type Item = ResolvedConfiguration;
fn next(&mut self) -> Option<Self::Item> {
if self.next == self.end {
return None;
}
let ordinal = self.next;
self.next += 1;
Some(ResolvedConfiguration::new(Arc::clone(&self.inner), ordinal))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.end - self.next;
match usize::try_from(remaining) {
Ok(remaining) => (remaining, Some(remaining)),
Err(_) => (usize::MAX, None),
}
}
}
impl FusedIterator for ConfigurationIter {}
impl fmt::Debug for ConfigurationIter {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ConfigurationIter")
.field("next", &self.next)
.field("end", &self.end)
.finish_non_exhaustive()
}
}