//! Generated configuration trees. use crate::{ConfigValue, ConfigViolation, FieldDescriptor, FieldId, FieldWriteError}; /// One recursively composable configuration tree. /// /// `#[derive(kaptan_config::Config)]` implements this contract for both scalar /// records and records composed from other configuration trees. Validation, /// metadata, field routing, and schema identity therefore come from one source. pub trait Config { /// Number of scalar fields in the flattened tree. const FIELD_COUNT: usize; /// Fingerprint generated from the complete declared schema. const SCHEMA_ID: [u8; 16]; /// Returns a descriptor from the flattened declaration order. fn descriptor(index: usize) -> Option<&'static FieldDescriptor>; /// Returns one scalar by stable field identity. fn field_value(&self, id: FieldId) -> Option; /// Applies one scalar only when its owning record accepts the local value. /// Whole-tree relational validation is intentionally deferred until the /// candidate is validated or committed. fn set_field(&mut self, id: FieldId, value: ConfigValue) -> Result<(), FieldWriteError>; /// Validates every generated rule and declared post-validation hook. fn validate(&self) -> Result<(), ConfigViolation>; } /// Detects duplicate flattened field identities in a generated tree. #[doc(hidden)] pub fn __unique_field_ids() -> bool { let mut index = 0; while index < C::FIELD_COUNT { let Some(descriptor) = C::descriptor(index) else { return false; }; let mut other = index + 1; while other < C::FIELD_COUNT { let Some(other_descriptor) = C::descriptor(other) else { return false; }; if descriptor.id == other_descriptor.id { return false; } other += 1; } index += 1; } C::descriptor(C::FIELD_COUNT).is_none() } /// Checks display-key uniqueness across one recursively composed schema. #[doc(hidden)] pub fn __unique_display_keys() -> bool { let mut index = 0; while index < C::FIELD_COUNT { let Some(descriptor) = C::descriptor(index) else { return false; }; let mut other = index + 1; while other < C::FIELD_COUNT { let Some(other_descriptor) = C::descriptor(other) else { return false; }; if descriptor.display_key == other_descriptor.display_key { return false; } other += 1; } index += 1; } true } /// Validates the bounded, self-describing enum portion of generated metadata. #[doc(hidden)] pub fn __valid_enum_options(descriptors: &[FieldDescriptor]) -> bool { for descriptor in descriptors { if descriptor.enum_options.len() > crate::MAX_ENUM_OPTIONS_PER_FIELD { return false; } for (index, option) in descriptor.enum_options.iter().enumerate() { if option.1.is_empty() || option.1.len() > crate::MAX_ENUM_OPTION_LABEL_BYTES || descriptor.kind != crate::ValueKind::U32 { return false; } if descriptor.enum_options[..index] .iter() .any(|previous| previous.0 == option.0) { return false; } } } true } const FINGERPRINT_LEFT_OFFSET: u64 = 0xcbf2_9ce4_8422_2325; const FINGERPRINT_RIGHT_OFFSET: u64 = 0x6c62_272e_07bb_0142; const FINGERPRINT_LEFT_PRIME: u64 = 0x0000_0100_0000_01b3; const FINGERPRINT_RIGHT_PRIME: u64 = 0x9e37_79b1_85eb_ca87; const fn hash_byte(mut state: [u64; 2], byte: u8) -> [u64; 2] { state[0] = (state[0] ^ byte as u64).wrapping_mul(FINGERPRINT_LEFT_PRIME); state[1] = (state[1] ^ byte as u64).wrapping_mul(FINGERPRINT_RIGHT_PRIME); state } const fn hash_u64(mut state: [u64; 2], value: u64) -> [u64; 2] { let bytes = value.to_le_bytes(); let mut index = 0; while index < bytes.len() { state = hash_byte(state, bytes[index]); index += 1; } state } const fn hash_str(mut state: [u64; 2], value: &str) -> [u64; 2] { state = hash_u64(state, value.len() as u64); let bytes = value.as_bytes(); let mut index = 0; while index < bytes.len() { state = hash_byte(state, bytes[index]); index += 1; } state } const fn hash_value(mut state: [u64; 2], value: Option) -> [u64; 2] { let Some(value) = value else { return hash_byte(state, 0); }; state = hash_byte(state, value.kind() as u8); hash_u64(state, value.raw_bits()) } const fn finish_fingerprint(state: [u64; 2]) -> [u8; 16] { let left = state[0].to_le_bytes(); let right = state[1].to_le_bytes(); [ left[0], left[1], left[2], left[3], left[4], left[5], left[6], left[7], right[0], right[1], right[2], right[3], right[4], right[5], right[6], right[7], ] } /// Produces the stable schema fingerprint for one scalar record. #[doc(hidden)] pub const fn __schema_id(descriptors: &[FieldDescriptor]) -> [u8; 16] { let mut state = [FINGERPRINT_LEFT_OFFSET, FINGERPRINT_RIGHT_OFFSET]; state = hash_u64(state, descriptors.len() as u64); let mut index = 0; while index < descriptors.len() { let descriptor = descriptors[index]; state = hash_u64(state, descriptor.id.get() as u64); state = hash_str(state, descriptor.display_key); state = hash_byte(state, descriptor.kind as u8); state = hash_value(state, descriptor.minimum); state = hash_value(state, descriptor.maximum); state = hash_u64(state, descriptor.enum_options.len() as u64); let mut option = 0; while option < descriptor.enum_options.len() { state = hash_u64(state, descriptor.enum_options[option].0 as u64); state = hash_str(state, descriptor.enum_options[option].1); option += 1; } state = hash_str(state, descriptor.unit); index += 1; } finish_fingerprint(state) } /// Produces a parent fingerprint from ordered child fingerprints. /// /// A one-child wrapper is deliberately transparent, so adding or removing a /// redundant structural wrapper does not invalidate persisted values. #[doc(hidden)] pub const fn __combine_schema_ids(children: &[[u8; 16]]) -> [u8; 16] { if children.len() == 1 { return children[0]; } let mut state = [FINGERPRINT_LEFT_OFFSET, FINGERPRINT_RIGHT_OFFSET]; state = hash_u64(state, children.len() as u64); let mut child = 0; while child < children.len() { let mut byte = 0; while byte < children[child].len() { state = hash_byte(state, children[child][byte]); byte += 1; } child += 1; } finish_fingerprint(state) }