git_policy_cli/invocation_config.rs
1//! What:
2//! Load the policy configuration of the worktree one invocation runs in,
3//! and render
4//! the events configuration loading can produce.
5//! Why:
6//! Both wrapped Git commands and `git cli-git check`/`fix` need the same answer:
7//! what does the `cli-git.config.jsonc` of the worktree Git reported say.
8//!
9//! In TS you'd write (pseudocode):
10//! ```ts
11//! // const loaded = await loadIdentityConfig(location.identity);
12//! ```
13
14/// Import the sibling modules this lookup combines.
15use super::config_error::ConfigError;
16use super::config_file::{LoadedConfig, ignored_legacy_notice, load_repository_config};
17use super::config_schema::CliGitConfig;
18use super::diagnostics::{
19 EngineFailureCode, LEGACY_CONFIG_IGNORED_CODE, render_configuration_warning,
20 render_engine_failure,
21};
22use super::worktree_identity::{WorktreeIdentity, worktree_root};
23/// `PathBuf` is an owned filesystem path of raw OS bytes.
24use std::path::PathBuf;
25
26/// What:
27/// Load the configuration for the worktree Git reported for this invocation.
28/// `&WorktreeIdentity` borrows that answer;
29/// `Result<LoadedConfig, ConfigError>` is the settings or the rejection.
30/// Why:
31/// The caller already asked Git where the command runs,
32/// once,
33/// so this function
34/// starts no process.
35/// Outside a worktree (no repository,
36/// a bare repository,
37/// the
38/// inside of `.git`) there is no configuration file and the defaults apply.
39///
40/// In TS you'd write (pseudocode):
41/// ```ts
42/// async function loadIdentityConfig(identity: WorktreeIdentity): Promise<LoadedConfig>;
43/// ```
44pub fn load_identity_config(identity: &WorktreeIdentity) -> Result<LoadedConfig, ConfigError> {
45 // `let Some(root) = ... else { ... };` unwraps the worktree top level or exits.
46 let Some(root) = worktree_root(identity) else {
47 // `Ok(...)` is the success variant: no worktree means the defaults.
48 return Ok(LoadedConfig {
49 config: CliGitConfig::defaults(),
50 // `None` records that no file was read.
51 source: None,
52 // `Vec::new()` is the empty owned list.
53 ignored_legacy: Vec::<PathBuf>::new(),
54 });
55 };
56 return load_repository_config(root);
57}
58
59/// What:
60/// Render the `config-invalid` event for a rejected configuration.
61/// `String` is the owned,
62/// line-terminated JSON line.
63/// Why:
64/// Configuration failures are machine-readable events with exit status 2,
65/// on
66/// standard error for wrapped commands and standard output for direct commands.
67///
68/// In TS you'd write (pseudocode):
69/// ```ts
70/// function configInvalidEvent(error: ConfigError): string;
71/// ```
72pub fn config_invalid_event(error: &ConfigError) -> String {
73 return render_engine_failure(0, EngineFailureCode::ConfigInvalid, error.message.as_str());
74}
75
76/// What:
77/// Render one warning event per legacy file left beside the JSONC file.
78/// Why:
79/// The stale executable configuration is reported on every configuration load,
80/// never ignored silently;
81/// events are numbered from zero in file order.
82///
83/// In TS you'd write (pseudocode):
84/// ```ts
85/// function legacyWarningEvents(loaded: LoadedConfig): string;
86/// ```
87pub fn legacy_warning_events(loaded: &LoadedConfig) -> String {
88 // `String::new()` is empty owned text; `mut` allows appending.
89 let mut events: String = String::new();
90 // `if let Some(source) = &loaded.source` borrows the JSONC path when a file was read.
91 if let Some(source) = &loaded.source {
92 // `.iter().enumerate()` yields `(index, item)` pairs; `usize` is the index type.
93 for (index, legacy) in loaded.ignored_legacy.iter().enumerate() {
94 events.push_str(
95 render_configuration_warning(
96 // `as u64` widens the index to the event number type.
97 index as u64,
98 LEGACY_CONFIG_IGNORED_CODE,
99 ignored_legacy_notice(legacy.as_path(), source.as_path()).as_str(),
100 // `.to_string_lossy()` renders the path as text, replacing undecodable bytes.
101 legacy.to_string_lossy().as_ref(),
102 )
103 .as_str(),
104 );
105 }
106 }
107 return events;
108}
109
110/// Disposable-repository controls stay out of the release executable.
111#[cfg(test)]
112#[path = "invocation_config_tests.rs"]
113mod tests;