1use std::collections::HashSet;
6
7use forge_primitives::permission::PermissionMode;
8use forge_primitives::{ModeInfo, ModeState};
9
10const CANONICAL_ORDER: [PermissionMode; 6] = [
12 PermissionMode::Ask,
13 PermissionMode::AcceptEdits,
14 PermissionMode::Plan,
15 PermissionMode::DontAsk,
16 PermissionMode::Auto,
17 PermissionMode::BypassPermissions,
18];
19
20pub fn supported_mode_ids_filtered(
27 current_model_supports_auto_mode: bool,
28 launched_into_bypass: bool,
29 current_mode: Option<PermissionMode>,
30 runtime_unavailable_mode_ids: &[PermissionMode],
31) -> Vec<PermissionMode> {
32 let mut seen: HashSet<PermissionMode> = CANONICAL_ORDER[..4].iter().copied().collect();
33 if current_model_supports_auto_mode {
34 seen.insert(PermissionMode::Auto);
35 }
36 if launched_into_bypass {
37 seen.insert(PermissionMode::BypassPermissions);
38 }
39 if let Some(mode) = current_mode {
40 seen.insert(mode);
41 }
42 CANONICAL_ORDER
43 .into_iter()
44 .filter(|m| seen.contains(m))
45 .filter(|m| current_mode == Some(*m) || !runtime_unavailable_mode_ids.contains(m))
46 .collect()
47}
48
49pub fn build_mode_state_from_supported(
52 mode: PermissionMode,
53 supported_mode_ids: &[PermissionMode],
54) -> ModeState {
55 ModeState {
56 current_mode_id: mode.as_wire().to_owned(),
57 current_mode_name: mode.display_name().to_owned(),
58 available_modes: supported_mode_ids
59 .iter()
60 .map(|m| ModeInfo {
61 id: m.as_wire().to_owned(),
62 name: m.display_name().to_owned(),
63 description: None,
64 })
65 .collect(),
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn base_supported_modes_exclude_bypass() {
75 let supported = supported_mode_ids_filtered(false, false, None, &[]);
76 assert_eq!(
77 supported,
78 vec![
79 PermissionMode::Ask,
80 PermissionMode::AcceptEdits,
81 PermissionMode::Plan,
82 PermissionMode::DontAsk
83 ]
84 );
85 }
86
87 #[test]
88 fn bypass_offered_when_launched_into_it() {
89 let supported = supported_mode_ids_filtered(false, true, None, &[]);
90 assert!(supported.contains(&PermissionMode::BypassPermissions));
91 }
92
93 #[test]
94 fn auto_mode_appears_when_current_model_supports_it() {
95 let supported = supported_mode_ids_filtered(true, false, None, &[]);
96 assert!(supported.contains(&PermissionMode::Auto));
97 }
98
99 #[test]
100 fn current_mode_survives_runtime_unavailable_filter() {
101 let supported = supported_mode_ids_filtered(
102 false,
103 false,
104 Some(PermissionMode::Plan),
105 &[PermissionMode::Plan],
106 );
107 assert!(supported.contains(&PermissionMode::Plan));
108 }
109
110 #[test]
111 fn build_mode_state_from_supported_uses_supported_list() {
112 let supported = vec![
113 PermissionMode::Ask,
114 PermissionMode::AcceptEdits,
115 PermissionMode::Plan,
116 PermissionMode::DontAsk,
117 ];
118 let state = build_mode_state_from_supported(PermissionMode::AcceptEdits, &supported);
119 assert_eq!(state.current_mode_id, "acceptEdits");
120 assert_eq!(state.current_mode_name, "Accept Edits");
121 assert_eq!(state.available_modes.len(), 4);
122 assert_eq!(state.available_modes[0].id, "default");
123 }
124}