linesmith_core/presets/
mod.rs1const MINIMAL: &str = include_str!("fixtures/minimal.toml");
9const DEVELOPER: &str = include_str!("fixtures/developer.toml");
10const POWER_USER: &str = include_str!("fixtures/power_user.toml");
11const COST_FOCUSED: &str = include_str!("fixtures/cost_focused.toml");
12const WORKTREE_HEAVY: &str = include_str!("fixtures/worktree_heavy.toml");
13
14#[derive(Debug, Clone, Copy)]
18struct Preset {
19 name: &'static str,
20 body: &'static str,
21}
22
23const PRESETS: &[Preset] = &[
25 Preset {
26 name: "minimal",
27 body: MINIMAL,
28 },
29 Preset {
30 name: "developer",
31 body: DEVELOPER,
32 },
33 Preset {
34 name: "power-user",
35 body: POWER_USER,
36 },
37 Preset {
38 name: "cost-focused",
39 body: COST_FOCUSED,
40 },
41 Preset {
42 name: "worktree-heavy",
43 body: WORKTREE_HEAVY,
44 },
45];
46
47pub fn names() -> impl Iterator<Item = &'static str> {
49 PRESETS.iter().map(|p| p.name)
50}
51
52#[must_use]
55pub fn body(name: &str) -> Option<&'static str> {
56 PRESETS.iter().find(|p| p.name == name).map(|p| p.body)
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 use crate::build_lines;
63 use crate::config::Config;
64 use std::str::FromStr;
65
66 fn segments_of(preset: &str) -> Vec<String> {
67 let body = body(preset).expect("preset registered");
68 let cfg = Config::from_str(body).expect("parse");
69 cfg.line
70 .expect("preset has [line]")
71 .segments
72 .into_iter()
73 .filter_map(|e| e.segment_id().map(str::to_string))
74 .collect()
75 }
76
77 fn lines_of(preset: &str) -> Vec<Vec<String>> {
84 let body = body(preset).expect("preset registered");
85 let cfg = Config::from_str(body).expect("parse");
86 let line = cfg.line.expect("preset has [line]");
87 let mut sorted: Vec<(u32, Vec<String>)> = line
88 .numbered
89 .into_iter()
90 .map(|(k, value)| {
91 let n: u32 = k.parse().expect("preset uses positive-integer line keys");
92 let table = value.as_table().expect("preset [line.N] is a table");
93 let segs: Vec<String> = table["segments"]
94 .as_array()
95 .expect("preset [line.N].segments is an array")
96 .iter()
97 .map(|v| v.as_str().expect("preset segment is a string").to_string())
98 .collect();
99 (n, segs)
100 })
101 .collect();
102 sorted.sort_by_key(|(n, _)| *n);
103 sorted.into_iter().map(|(_, segs)| segs).collect()
104 }
105
106 #[test]
107 fn registry_has_five_presets_in_stable_order() {
108 let got: Vec<&str> = names().collect();
109 assert_eq!(
110 got,
111 vec![
112 "minimal",
113 "developer",
114 "power-user",
115 "cost-focused",
116 "worktree-heavy",
117 ]
118 );
119 }
120
121 #[test]
122 fn every_preset_parses_without_warnings() {
123 for name in names() {
127 let body = body(name).expect("preset registered");
128 let cfg = Config::from_str(body)
129 .unwrap_or_else(|e| panic!("preset '{name}' failed to parse: {e}"));
130 let mut warnings: Vec<String> = Vec::new();
131 let _ = build_lines(Some(&cfg), None, |m: &str| warnings.push(m.to_string()));
132 assert!(
133 warnings.is_empty(),
134 "preset '{name}' emitted warnings: {warnings:?}"
135 );
136 }
137 }
138
139 #[test]
140 fn minimal_preset_segments_are_model_and_context_window() {
141 assert_eq!(segments_of("minimal"), vec!["model", "context_window"]);
142 }
143
144 #[test]
145 fn developer_preset_segments_match_spec() {
146 assert_eq!(
147 segments_of("developer"),
148 vec![
149 "model",
150 "workspace",
151 "context_window",
152 "cost",
153 "session_duration",
154 "rate_limit_5h",
155 "rate_limit_7d",
156 ]
157 );
158 }
159
160 #[test]
161 fn power_user_preset_is_multi_line_with_two_lines() {
162 let body = body("power-user").expect("preset registered");
167 let cfg = Config::from_str(body).expect("parse");
168 assert_eq!(
169 cfg.layout,
170 crate::config::LayoutMode::MultiLine,
171 "power-user must declare layout = \"multi-line\""
172 );
173 assert_eq!(
174 lines_of("power-user"),
175 vec![
176 vec!["model", "context_window", "workspace"],
177 vec![
178 "rate_limit_5h",
179 "rate_limit_7d",
180 "cost",
181 "effort",
182 "tokens_total",
183 ],
184 ]
185 );
186 }
187
188 #[test]
189 fn cost_focused_preset_segments_match_spec() {
190 assert_eq!(
191 segments_of("cost-focused"),
192 vec![
193 "model",
194 "context_window",
195 "cost",
196 "rate_limit_5h",
197 "rate_limit_7d",
198 ]
199 );
200 }
201
202 #[test]
203 fn worktree_heavy_preset_segments_match_spec() {
204 assert_eq!(
205 segments_of("worktree-heavy"),
206 vec!["model", "workspace", "context_window"]
207 );
208 }
209
210 #[test]
211 fn preset_names_are_unique() {
212 let mut seen = std::collections::HashSet::new();
215 for name in names() {
216 assert!(seen.insert(name), "duplicate preset name: {name}");
217 }
218 }
219
220 #[test]
221 fn body_returns_none_for_unknown_name() {
222 assert!(body("definitely-not-a-preset").is_none());
223 assert!(body("").is_none());
224 }
225
226 #[test]
227 fn body_lookup_is_case_sensitive() {
228 assert!(body("minimal").is_some());
229 assert!(body("Minimal").is_none());
230 assert!(body("MINIMAL").is_none());
231 assert!(body("Developer").is_none());
232 }
233}