Skip to main content

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;