systemprompt_cli/commands/admin/config/rate_limits/
diff.rs1use anyhow::{Context, Result, bail};
7use std::fs;
8use std::path::Path;
9use systemprompt_config::ProfileBootstrap;
10use systemprompt_logging::CliService;
11use systemprompt_models::profile::RateLimitsConfig;
12
13use super::DiffArgs;
14use crate::CliConfig;
15use crate::cli_settings::OutputFormat;
16use crate::shared::{CommandOutput, render_result};
17
18use super::super::types::{DiffEntry, DiffOutput};
19
20pub(super) fn execute_diff(args: &DiffArgs, config: &CliConfig) -> Result<()> {
21 let profile = ProfileBootstrap::get()?;
22 let current = &profile.rate_limits;
23
24 let (compare_with, source) = if args.defaults {
25 (RateLimitsConfig::default(), "defaults".to_owned())
26 } else if let Some(file_path) = &args.file {
27 let content = fs::read_to_string(file_path)
28 .with_context(|| format!("Failed to read file: {}", file_path))?;
29
30 let is_json = Path::new(file_path)
31 .extension()
32 .is_some_and(|ext| ext.eq_ignore_ascii_case("json"));
33
34 let limits: RateLimitsConfig = if is_json {
35 serde_json::from_str(&content)
36 .with_context(|| format!("Failed to parse JSON from: {}", file_path))?
37 } else {
38 serde_yaml::from_str(&content)
39 .with_context(|| format!("Failed to parse YAML from: {}", file_path))?
40 };
41
42 (limits, file_path.clone())
43 } else {
44 bail!("Must specify --defaults or --file");
45 };
46
47 let differences = collect_differences(current, &compare_with);
48 let count = differences.len();
49
50 let output = DiffOutput {
51 source,
52 identical: differences.is_empty(),
53 differences,
54 };
55
56 render_result(
57 &CommandOutput::table_of(vec!["field", "current", "other"], &output.differences)
58 .with_title("Rate Limits Diff"),
59 config,
60 );
61
62 if config.output_format() == OutputFormat::Table {
63 if count == 0 {
64 CliService::success("No differences found");
65 } else {
66 CliService::info(&format!("{count} difference(s) found"));
67 }
68 }
69
70 Ok(())
71}
72
73pub fn collect_differences(
74 current: &RateLimitsConfig,
75 compare_with: &RateLimitsConfig,
76) -> Vec<DiffEntry> {
77 let mut differences: Vec<DiffEntry> = Vec::new();
78
79 add_diff_if_different(
80 &mut differences,
81 "disabled",
82 ¤t.disabled,
83 &compare_with.disabled,
84 );
85 collect_rate_differences(current, compare_with, &mut differences);
86 add_diff_if_different(
87 &mut differences,
88 "burst_multiplier",
89 ¤t.burst_multiplier,
90 &compare_with.burst_multiplier,
91 );
92
93 differences
94}
95
96fn collect_rate_differences(
97 current: &RateLimitsConfig,
98 compare_with: &RateLimitsConfig,
99 diffs: &mut Vec<DiffEntry>,
100) {
101 let rates = [
102 (
103 "oauth_public_per_second",
104 current.oauth_public_per_second,
105 compare_with.oauth_public_per_second,
106 ),
107 (
108 "oauth_auth_per_second",
109 current.oauth_auth_per_second,
110 compare_with.oauth_auth_per_second,
111 ),
112 (
113 "contexts_per_second",
114 current.contexts_per_second,
115 compare_with.contexts_per_second,
116 ),
117 (
118 "tasks_per_second",
119 current.tasks_per_second,
120 compare_with.tasks_per_second,
121 ),
122 (
123 "artifacts_per_second",
124 current.artifacts_per_second,
125 compare_with.artifacts_per_second,
126 ),
127 (
128 "agent_registry_per_second",
129 current.agent_registry_per_second,
130 compare_with.agent_registry_per_second,
131 ),
132 (
133 "agents_per_second",
134 current.agents_per_second,
135 compare_with.agents_per_second,
136 ),
137 (
138 "mcp_registry_per_second",
139 current.mcp_registry_per_second,
140 compare_with.mcp_registry_per_second,
141 ),
142 (
143 "mcp_per_second",
144 current.mcp_per_second,
145 compare_with.mcp_per_second,
146 ),
147 (
148 "stream_per_second",
149 current.stream_per_second,
150 compare_with.stream_per_second,
151 ),
152 (
153 "content_per_second",
154 current.content_per_second,
155 compare_with.content_per_second,
156 ),
157 ];
158
159 for (field, current_val, other_val) in rates {
160 add_diff_if_different(diffs, field, ¤t_val, &other_val);
161 }
162}
163
164fn add_diff_if_different<T: std::fmt::Display + PartialEq>(
165 diffs: &mut Vec<DiffEntry>,
166 field: &str,
167 current: &T,
168 compare: &T,
169) {
170 if current != compare {
171 diffs.push(DiffEntry {
172 field: field.to_owned(),
173 current: current.to_string(),
174 other: compare.to_string(),
175 });
176 }
177}