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 .collect()
74 }
75
76 fn lines_of(preset: &str) -> Vec<Vec<String>> {
83 let body = body(preset).expect("preset registered");
84 let cfg = Config::from_str(body).expect("parse");
85 let line = cfg.line.expect("preset has [line]");
86 let mut sorted: Vec<(u32, Vec<String>)> = line
87 .numbered
88 .into_iter()
89 .map(|(k, value)| {
90 let n: u32 = k.parse().expect("preset uses positive-integer line keys");
91 let table = value.as_table().expect("preset [line.N] is a table");
92 let segs: Vec<String> = table["segments"]
93 .as_array()
94 .expect("preset [line.N].segments is an array")
95 .iter()
96 .map(|v| v.as_str().expect("preset segment is a string").to_string())
97 .collect();
98 (n, segs)
99 })
100 .collect();
101 sorted.sort_by_key(|(n, _)| *n);
102 sorted.into_iter().map(|(_, segs)| segs).collect()
103 }
104
105 #[test]
106 fn registry_has_five_presets_in_stable_order() {
107 let got: Vec<&str> = names().collect();
108 assert_eq!(
109 got,
110 vec![
111 "minimal",
112 "developer",
113 "power-user",
114 "cost-focused",
115 "worktree-heavy",
116 ]
117 );
118 }
119
120 #[test]
121 fn every_preset_parses_without_warnings() {
122 for name in names() {
126 let body = body(name).expect("preset registered");
127 let cfg = Config::from_str(body)
128 .unwrap_or_else(|e| panic!("preset '{name}' failed to parse: {e}"));
129 let mut warnings: Vec<String> = Vec::new();
130 let _ = build_lines(Some(&cfg), None, |m: &str| warnings.push(m.to_string()));
131 assert!(
132 warnings.is_empty(),
133 "preset '{name}' emitted warnings: {warnings:?}"
134 );
135 }
136 }
137
138 #[test]
139 fn minimal_preset_segments_are_model_and_context_window() {
140 assert_eq!(segments_of("minimal"), vec!["model", "context_window"]);
141 }
142
143 #[test]
144 fn developer_preset_segments_match_spec() {
145 assert_eq!(
146 segments_of("developer"),
147 vec![
148 "model",
149 "workspace",
150 "context_window",
151 "cost",
152 "session_duration",
153 "rate_limit_5h",
154 "rate_limit_7d",
155 ]
156 );
157 }
158
159 #[test]
160 fn power_user_preset_is_multi_line_with_two_lines() {
161 let body = body("power-user").expect("preset registered");
166 let cfg = Config::from_str(body).expect("parse");
167 assert_eq!(
168 cfg.layout,
169 crate::config::LayoutMode::MultiLine,
170 "power-user must declare layout = \"multi-line\""
171 );
172 assert_eq!(
173 lines_of("power-user"),
174 vec![
175 vec!["model", "context_window", "workspace"],
176 vec![
177 "rate_limit_5h",
178 "rate_limit_7d",
179 "cost",
180 "effort",
181 "tokens_total",
182 ],
183 ]
184 );
185 }
186
187 #[test]
188 fn cost_focused_preset_segments_match_spec() {
189 assert_eq!(
190 segments_of("cost-focused"),
191 vec![
192 "model",
193 "context_window",
194 "cost",
195 "rate_limit_5h",
196 "rate_limit_7d",
197 ]
198 );
199 }
200
201 #[test]
202 fn worktree_heavy_preset_segments_match_spec() {
203 assert_eq!(
204 segments_of("worktree-heavy"),
205 vec!["model", "workspace", "context_window"]
206 );
207 }
208
209 #[test]
210 fn preset_names_are_unique() {
211 let mut seen = std::collections::HashSet::new();
214 for name in names() {
215 assert!(seen.insert(name), "duplicate preset name: {name}");
216 }
217 }
218
219 #[test]
220 fn body_returns_none_for_unknown_name() {
221 assert!(body("definitely-not-a-preset").is_none());
222 assert!(body("").is_none());
223 }
224
225 #[test]
226 fn body_lookup_is_case_sensitive() {
227 assert!(body("minimal").is_some());
228 assert!(body("Minimal").is_none());
229 assert!(body("MINIMAL").is_none());
230 assert!(body("Developer").is_none());
231 }
232}