Download codex-rs/config/src/requirements_layers/stack.rs from SaylorTwift/codex: direct link, hf CLI and curl.
- Browser
- Download file 13.7 kB
-
https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/config/src/requirements_layers/stack.rs
- Command line
-
hf download hf://SaylorTwift/codex/codex-rs/config/src/requirements_layers/stack.rs
-
curl -L -o stack.rs https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/config/src/requirements_layers/stack.rs
13.7 kB
| //! Requirements layers are composed in the same order as config layers: lowest | |
| //! precedence first, highest precedence last. Most fields use the same | |
| //! TOML-level merge policy as config: lower-priority layers provide defaults, | |
| //! and higher-priority layers override scalar/list values while recursively | |
| //! extending tables. | |
| //! | |
| //! A few fields carry domain-specific meaning that raw TOML replacement would | |
| //! break: | |
| //! - `remote_sandbox_config` is evaluated within each layer before merging. | |
| //! - `rules.prefix_rules` append high-priority rules first. | |
| //! - `hooks` append high-priority event groups first while failing closed on | |
| //! active managed-dir conflicts. | |
| //! - `permissions.filesystem.deny_read` is a high-priority-first union across | |
| //! layers. | |
| //! - `auto_review.required_on_models` is a high-priority-first union across layers. | |
| use crate::ConfigRequirementsToml; | |
| use crate::ConfigRequirementsWithSources; | |
| use crate::RequirementSource; | |
| use crate::Sourced; | |
| use crate::merge::merge_toml_values; | |
| use std::cell::OnceCell; | |
| use std::io; | |
| use thiserror::Error; | |
| use toml::Value as TomlValue; | |
| use super::hooks::HookDirectoryField; | |
| use super::hooks::HookMergeState; | |
| use super::layer::ComposableRequirementsLayer; | |
| use super::layer::RequirementsLayerEntry; | |
| use super::models::AutoReviewModelsMergeState; | |
| use super::permissions::DenyReadMergeState; | |
| pub enum RequirementsCompositionError { | |
| Parse { | |
| layer_source: RequirementSource, | |
| message: String, | |
| }, | |
| ComposedParse { message: String }, | |
| Conflict { | |
| field: String, | |
| existing_source: RequirementSource, | |
| incoming_source: RequirementSource, | |
| message: String, | |
| }, | |
| } | |
| impl From<RequirementsCompositionError> for io::Error { | |
| fn from(error: RequirementsCompositionError) -> Self { | |
| io::Error::new(io::ErrorKind::InvalidData, error) | |
| } | |
| } | |
| pub fn compose_requirements( | |
| layers: impl IntoIterator<Item = RequirementsLayerEntry>, | |
| ) -> Result<Option<ConfigRequirementsWithSources>, RequirementsCompositionError> { | |
| compose_requirements_with_hostname_resolver(layers, crate::host_name) | |
| } | |
| /// Composes requirements using an explicitly supplied execution-host hostname. | |
| pub fn compose_requirements_for_hostname( | |
| layers: impl IntoIterator<Item = RequirementsLayerEntry>, | |
| hostname: Option<&str>, | |
| ) -> Result<Option<ConfigRequirementsWithSources>, RequirementsCompositionError> { | |
| let hostname = hostname.map(str::to_string); | |
| compose_requirements_with_hostname_resolver_and_hook_directory( | |
| layers, | |
| move || hostname.clone(), | |
| HookDirectoryField::current_platform(), | |
| ) | |
| } | |
| pub(super) fn compose_requirements_for_hostname_and_hook_directory( | |
| layers: impl IntoIterator<Item = RequirementsLayerEntry>, | |
| hostname: Option<&str>, | |
| hook_directory_field: HookDirectoryField, | |
| ) -> Result<Option<ConfigRequirementsWithSources>, RequirementsCompositionError> { | |
| let hostname = hostname.map(str::to_string); | |
| compose_requirements_with_hostname_resolver_and_hook_directory( | |
| layers, | |
| move || hostname.clone(), | |
| hook_directory_field, | |
| ) | |
| } | |
| fn compose_requirements_with_hostname_resolver( | |
| layers: impl IntoIterator<Item = RequirementsLayerEntry>, | |
| hostname_resolver: impl Fn() -> Option<String>, | |
| ) -> Result<Option<ConfigRequirementsWithSources>, RequirementsCompositionError> { | |
| compose_requirements_with_hostname_resolver_and_hook_directory( | |
| layers, | |
| hostname_resolver, | |
| HookDirectoryField::current_platform(), | |
| ) | |
| } | |
| fn compose_requirements_with_hostname_resolver_and_hook_directory( | |
| layers: impl IntoIterator<Item = RequirementsLayerEntry>, | |
| hostname_resolver: impl Fn() -> Option<String>, | |
| hook_directory_field: HookDirectoryField, | |
| ) -> Result<Option<ConfigRequirementsWithSources>, RequirementsCompositionError> { | |
| // Evaluate every layer in this composition against the same hostname while | |
| // keeping resolution lazy when no layer needs remote sandbox matching. | |
| let hostname = OnceCell::new(); | |
| let cached_hostname_resolver = || hostname.get_or_init(&hostname_resolver).clone(); | |
| let mut stack = RequirementsLayerStack::new(hook_directory_field); | |
| for layer in layers { | |
| stack.add_layer(layer, &cached_hostname_resolver)?; | |
| } | |
| stack.compose() | |
| } | |
| struct RequirementsLayerStack { | |
| layers: Vec<ComposableRequirementsLayer>, | |
| hook_directory_field: HookDirectoryField, | |
| } | |
| impl RequirementsLayerStack { | |
| fn new(hook_directory_field: HookDirectoryField) -> Self { | |
| Self { | |
| layers: Vec::new(), | |
| hook_directory_field, | |
| } | |
| } | |
| fn add_layer( | |
| &mut self, | |
| layer: RequirementsLayerEntry, | |
| hostname_resolver: &dyn Fn() -> Option<String>, | |
| ) -> Result<(), RequirementsCompositionError> { | |
| self.layers.push(ComposableRequirementsLayer::from_entry( | |
| layer, | |
| hostname_resolver, | |
| )?); | |
| Ok(()) | |
| } | |
| fn compose( | |
| self, | |
| ) -> Result<Option<ConfigRequirementsWithSources>, RequirementsCompositionError> { | |
| let Self { | |
| layers, | |
| hook_directory_field, | |
| } = self; | |
| let mut merged_toml = TomlValue::Table(toml::map::Map::new()); | |
| for layer in &layers { | |
| merge_toml_values(&mut merged_toml, &layer.regular_toml); | |
| } | |
| let requirements: ConfigRequirementsToml = | |
| merged_toml.try_into().map_err(|err: toml::de::Error| { | |
| RequirementsCompositionError::ComposedParse { | |
| message: err.to_string(), | |
| } | |
| })?; | |
| let mut output = ConfigRequirementsWithSources::default(); | |
| populate_merged_regular_fields_with_sources(&mut output, requirements, &layers); | |
| let mut rules = None; | |
| let mut hooks = HookMergeState::new(hook_directory_field); | |
| let mut hooks_output = None; | |
| let mut deny_read = DenyReadMergeState::default(); | |
| let mut auto_review_models = AutoReviewModelsMergeState::default(); | |
| // Regular TOML fields are folded low-to-high like config. These custom | |
| // fields append or union values, so process them high-to-low to keep | |
| // priority order visible in the output. | |
| for layer in layers.iter().rev() { | |
| let domain_fields = &layer.domain_fields; | |
| super::rules::merge(&mut rules, domain_fields.rules.clone(), &layer.source); | |
| hooks.merge( | |
| &mut hooks_output, | |
| domain_fields.hooks.clone(), | |
| &layer.source, | |
| )?; | |
| deny_read.merge(domain_fields.permissions.clone(), &layer.source); | |
| auto_review_models.merge(domain_fields.auto_review.clone(), &layer.source); | |
| } | |
| output.rules = rules; | |
| output.hooks = hooks_output; | |
| deny_read.apply_to(&mut output.permissions); | |
| auto_review_models.apply_to(&mut output.auto_review); | |
| let output_is_empty = output.clone().into_toml().is_empty(); | |
| Ok((!output_is_empty).then_some(output)) | |
| } | |
| } | |
| fn populate_merged_regular_fields_with_sources( | |
| output: &mut ConfigRequirementsWithSources, | |
| requirements: ConfigRequirementsToml, | |
| layers: &[ComposableRequirementsLayer], | |
| ) { | |
| macro_rules! set_sourced { | |
| ($field:ident, $keys:expr) => { | |
| if let Some(value) = $field { | |
| output.$field = Some(Sourced::new( | |
| value, | |
| source_for_top_level_keys(layers, $keys), | |
| )); | |
| } | |
| }; | |
| } | |
| // Destructure without `..` so every new requirements field must choose | |
| // whether it belongs in the regular TOML merge path or in a special merger. | |
| let ConfigRequirementsToml { | |
| allowed_login_methods, | |
| allowed_chatgpt_workspaces, | |
| cli_auth_credentials_store, | |
| chatgpt_base_url, | |
| sqlite_home, | |
| log_dir, | |
| model_catalog_json, | |
| model_provider, | |
| model_providers, | |
| check_for_update_on_startup, | |
| allow_login_shell, | |
| feedback, | |
| allowed_approval_policies, | |
| allowed_approvals_reviewers, | |
| allowed_sandbox_modes, | |
| allowed_permission_profiles, | |
| default_permissions, | |
| remote_sandbox_config: _, | |
| allowed_web_search_modes, | |
| allow_managed_hooks_only, | |
| allow_browser_and_computer_use, | |
| allow_appshots, | |
| allow_remote_control, | |
| computer_use, | |
| browser_use, | |
| in_app_browser, | |
| windows, | |
| feature_requirements, | |
| hooks: _, | |
| mcp_servers, | |
| plugins, | |
| marketplaces, | |
| apps, | |
| rules: _, | |
| enforce_residency, | |
| network, | |
| application, | |
| permissions, | |
| auto_review, | |
| models, | |
| additional_developer_instructions, | |
| guardian_policy_config, | |
| } = requirements; | |
| set_sourced!(allowed_login_methods, &["allowed_login_methods"]); | |
| set_sourced!(allowed_chatgpt_workspaces, &["allowed_chatgpt_workspaces"]); | |
| set_sourced!(cli_auth_credentials_store, &["cli_auth_credentials_store"]); | |
| set_sourced!(chatgpt_base_url, &["chatgpt_base_url"]); | |
| set_sourced!(sqlite_home, &["sqlite_home"]); | |
| set_sourced!(log_dir, &["log_dir"]); | |
| set_sourced!(model_catalog_json, &["model_catalog_json"]); | |
| set_sourced!(model_provider, &["model_provider"]); | |
| set_sourced!(model_providers, &["model_providers"]); | |
| set_sourced!( | |
| check_for_update_on_startup, | |
| &["check_for_update_on_startup"] | |
| ); | |
| set_sourced!(allow_login_shell, &["allow_login_shell"]); | |
| set_sourced!(feedback, &["feedback"]); | |
| set_sourced!(allowed_approval_policies, &["allowed_approval_policies"]); | |
| set_sourced!( | |
| allowed_approvals_reviewers, | |
| &["allowed_approvals_reviewers"] | |
| ); | |
| set_sourced!(allowed_sandbox_modes, &["allowed_sandbox_modes"]); | |
| set_sourced!( | |
| allowed_permission_profiles, | |
| &["allowed_permission_profiles"] | |
| ); | |
| set_sourced!(default_permissions, &["default_permissions"]); | |
| set_sourced!(allowed_web_search_modes, &["allowed_web_search_modes"]); | |
| set_sourced!(allow_managed_hooks_only, &["allow_managed_hooks_only"]); | |
| set_sourced!( | |
| allow_browser_and_computer_use, | |
| &["allow_browser_and_computer_use"] | |
| ); | |
| set_sourced!(allow_appshots, &["allow_appshots"]); | |
| set_sourced!(allow_remote_control, &["allow_remote_control"]); | |
| set_sourced!(auto_review, &["auto_review"]); | |
| set_sourced!(computer_use, &["computer_use"]); | |
| set_sourced!(browser_use, &["browser_use"]); | |
| set_sourced!(in_app_browser, &["in_app_browser"]); | |
| set_sourced!(windows, &["windows"]); | |
| set_sourced!(feature_requirements, &["features", "feature_requirements"]); | |
| set_sourced!(mcp_servers, &["mcp_servers"]); | |
| set_sourced!(plugins, &["plugins"]); | |
| set_sourced!(marketplaces, &["marketplaces"]); | |
| set_sourced!(apps, &["apps"]); | |
| set_sourced!(enforce_residency, &["enforce_residency"]); | |
| set_sourced!(network, &["experimental_network"]); | |
| set_sourced!(application, &["application"]); | |
| set_sourced!(permissions, &["permissions"]); | |
| set_sourced!(models, &["models"]); | |
| set_sourced!( | |
| additional_developer_instructions, | |
| &["additional_developer_instructions"] | |
| ); | |
| if let Some(guardian_policy_config) = | |
| guardian_policy_config.filter(|value| !value.trim().is_empty()) | |
| { | |
| output.guardian_policy_config = Some(Sourced::new( | |
| guardian_policy_config, | |
| source_for_top_level_keys(layers, &["guardian_policy_config"]), | |
| )); | |
| } | |
| } | |
| fn source_for_top_level_keys( | |
| layers: &[ComposableRequirementsLayer], | |
| keys: &[&str], | |
| ) -> RequirementSource { | |
| let matching_layers = layers | |
| .iter() | |
| .filter_map(|layer| { | |
| top_level_value_for_keys(&layer.regular_toml, keys).map(|value| (&layer.source, value)) | |
| }) | |
| .collect::<Vec<_>>(); | |
| let Some((winning_source, winning_value)) = matching_layers.last() else { | |
| return RequirementSource::Unknown; | |
| }; | |
| let winning_source = (*winning_source).clone(); | |
| if !winning_value.is_table() { | |
| return winning_source; | |
| } | |
| let table_sources = matching_layers | |
| .into_iter() | |
| .rev() | |
| .filter_map(|(source, value)| value.is_table().then_some(source.clone())) | |
| .collect::<Vec<_>>(); | |
| if table_sources.len() > 1 { | |
| RequirementSource::composite(table_sources) | |
| } else { | |
| winning_source | |
| } | |
| } | |
| fn top_level_value_for_keys<'a>(value: &'a TomlValue, keys: &[&str]) -> Option<&'a TomlValue> { | |
| let table = value.as_table()?; | |
| keys.iter().find_map(|key| table.get(*key)) | |
| } | |
| pub(super) fn merge_output_source(existing: &mut RequirementSource, incoming: &RequirementSource) { | |
| if existing != incoming { | |
| *existing = RequirementSource::composite([existing.clone(), incoming.clone()]); | |
| } | |
| } | |
| pub(super) fn composition_conflict( | |
| field: String, | |
| existing_source: RequirementSource, | |
| incoming_source: RequirementSource, | |
| message: impl Into<String>, | |
| ) -> RequirementsCompositionError { | |
| RequirementsCompositionError::Conflict { | |
| field, | |
| existing_source, | |
| incoming_source, | |
| message: message.into(), | |
| } | |
| } | |
| mod tests; | |