Codex Handbook
config/src/cloud_config_layers.rs 151 lines
//! Conversion from cloud-delivered config TOML fragments into config stack layers.//!//! Backend fragments arrive in backend priority order. This module parses each//! fragment, resolves relative path fields against the cloud config base//! directory, and returns layers in `ConfigLayerStack` order.use crate::ConfigLayerEntry;use crate::ConfigLayerSource;use crate::TomlValue;use crate::config_toml::ConfigToml;use crate::loader::resolve_relative_paths_in_config_toml;use crate::strict_config::config_error_from_ignored_toml_value_fields_for_source_name;use codex_utils_absolute_path::AbsolutePathBuf;use codex_utils_absolute_path::AbsolutePathBufGuard;use serde::Deserialize;use serde::Serialize;use std::fmt;use std::io;use thiserror::Error;/// Config fragment delivered by the cloud config bundle.////// The bundle orders fragments from highest precedence to lowest precedence./// This module returns config layers in stack order, so callers can append the/// result between system and user config without re-sorting.#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]pub struct CloudConfigFragment {    pub id: String,    pub name: String,    pub contents: String,}impl CloudConfigFragment {    fn source_ref(&self) -> CloudConfigFragmentSource {        CloudConfigFragmentSource {            id: self.id.clone(),            name: self.name.clone(),        }    }}#[derive(Clone, Debug, PartialEq, Eq)]pub struct CloudConfigFragmentSource {    pub id: String,    pub name: String,}impl fmt::Display for CloudConfigFragmentSource {    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {        write!(f, "{} ({})", self.name, self.id)    }}#[derive(Debug, Error, PartialEq, Eq)]pub enum CloudConfigLayerError {    #[error("failed to parse cloud config fragment {fragment}: {message}")]    Parse {        fragment: CloudConfigFragmentSource,        message: String,    },    #[error("invalid cloud config fragment {fragment}: {message}")]    Invalid {        fragment: CloudConfigFragmentSource,        message: String,    },}pub fn cloud_config_layers_from_fragments(    fragments: impl IntoIterator<Item = CloudConfigFragment>,    base_dir: &AbsolutePathBuf,) -> Result<Vec<ConfigLayerEntry>, CloudConfigLayerError> {    cloud_config_layers_from_fragments_impl(fragments, base_dir, /*strict_config*/ false)}pub(crate) fn cloud_config_layers_from_fragments_strict(    fragments: impl IntoIterator<Item = CloudConfigFragment>,    base_dir: &AbsolutePathBuf,) -> Result<Vec<ConfigLayerEntry>, CloudConfigLayerError> {    cloud_config_layers_from_fragments_impl(fragments, base_dir, /*strict_config*/ true)}fn cloud_config_layers_from_fragments_impl(    fragments: impl IntoIterator<Item = CloudConfigFragment>,    base_dir: &AbsolutePathBuf,    strict_config: bool,) -> Result<Vec<ConfigLayerEntry>, CloudConfigLayerError> {    let mut layers = Vec::new();    for fragment in fragments {        let source_ref = fragment.source_ref();        let raw_toml = fragment.contents;        let value: TomlValue =            toml::from_str(&raw_toml).map_err(|err| CloudConfigLayerError::Parse {                fragment: source_ref.clone(),                message: err.to_string(),            })?;        if strict_config {            validate_fragment_strictly(&source_ref, &raw_toml, &value, base_dir)?;        }        let resolved =            resolve_relative_paths_in_config_toml(value, base_dir.as_path()).map_err(|err| {                CloudConfigLayerError::Invalid {                    fragment: source_ref.clone(),                    message: err.to_string(),                }            })?;        layers.push(ConfigLayerEntry::new_with_raw_toml(            ConfigLayerSource::EnterpriseManaged {                id: fragment.id,                name: fragment.name,            },            resolved,            raw_toml,            base_dir.clone(),        ));    }    // Bundle fragments arrive highest-priority first, while ConfigLayerStack    // folds lowest-priority to highest-priority.    layers.reverse();    Ok(layers)}fn validate_fragment_strictly(    source_ref: &CloudConfigFragmentSource,    raw_toml: &str,    value: &TomlValue,    base_dir: &AbsolutePathBuf,) -> Result<(), CloudConfigLayerError> {    let _guard = AbsolutePathBufGuard::new(base_dir.as_path());    if let Some(config_error) = config_error_from_ignored_toml_value_fields_for_source_name::<        ConfigToml,    >(&source_ref.to_string(), raw_toml, value.clone())    {        return Err(CloudConfigLayerError::Invalid {            fragment: source_ref.clone(),            message: config_error.message,        });    }    Ok(())}impl From<CloudConfigLayerError> for io::Error {    fn from(error: CloudConfigLayerError) -> Self {        io::Error::new(io::ErrorKind::InvalidData, error)    }}#[cfg(test)]#[path = "cloud_config_layers_tests.rs"]mod tests;