1use std::{mem::discriminant, str::FromStr};
2
3use serde::{Deserialize, Serialize, Serializer};
4
5#[derive(Debug, Clone, Deserialize, Default)]
6pub enum Action {
7 #[default] Select,
9 Deselect,
10 Toggle,
11 CycleAll,
12 Accept,
13 Quit(Exit),
14
15 CyclePreview,
17 Preview(String), Help(String), SwitchPreview(Option<u8>), SetPreview(Option<u8>), ToggleWrap,
23 ToggleWrapPreview,
24
25 Execute(String),
27 Become(String),
28 Reload(String),
29 Print(String),
30
31 SetInput(String),
32 SetHeader(Option<String>),
33 SetFooter(Option<String>),
34 SetPrompt(Option<String>),
35 Column(usize),
36 CycleColumn,
37 HistoryUp,
39 HistoryDown,
40 ChangePrompt,
41 ChangeQuery,
42
43 ForwardChar,
45 BackwardChar,
46 ForwardWord,
47 BackwardWord,
48 DeleteChar,
49 DeleteWord,
50 DeleteLineStart,
51 DeleteLineEnd,
52 Cancel,
53 InputPos(i32),
54
55 Up(Count),
57 Down(Count),
58 PreviewUp(Count),
59 PreviewDown(Count),
60 PreviewHalfPageUp,
61 PreviewHalfPageDown,
62 Pos(i32),
63
64 Redraw,
66}
67
68impl serde::Serialize for Action {
69 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
70 where
71 S: serde::Serializer,
72 {
73 serializer.serialize_str(&self.to_string())
74 }
75}
76
77impl PartialEq for Action {
81 fn eq(&self, other: &Self) -> bool {
82 discriminant(self) == discriminant(other)
83 }
84}
85
86impl Eq for Action {}
87
88impl std::hash::Hash for Action {
89 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
90 discriminant(self).hash(state);
91 }
92}
93
94#[derive(Debug, Clone, PartialEq)]
95pub struct Actions(pub Vec<Action>);
96
97impl<const N: usize> From<[Action; N]> for Actions {
98 fn from(arr: [Action; N]) -> Self {
99 Actions(arr.into())
100 }
101}
102
103impl<'de> Deserialize<'de> for Actions {
104 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
105 where
106 D: serde::Deserializer<'de>,
107 {
108 #[derive(Debug, Deserialize)]
109 #[serde(untagged)]
110 enum Helper {
111 Single(String),
112 Multiple(Vec<String>),
113 }
114
115 let helper = Helper::deserialize(deserializer)?;
116 let strings = match helper {
117 Helper::Single(s) => vec![s],
118 Helper::Multiple(v) => v,
119 };
120
121 let mut actions = Vec::with_capacity(strings.len());
122 for s in strings {
123 let action = Action::from_str(&s).map_err(serde::de::Error::custom)?;
124 actions.push(action);
125 }
126
127 Ok(Actions(actions))
128 }
129}
130
131impl Serialize for Actions {
132 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
133 where
134 S: Serializer,
135 {
136 match self.0.len() {
137 1 => serializer.serialize_str(&self.0[0].to_string()),
138 _ => {
139 let strings: Vec<String> = self.0.iter().map(|a| a.to_string()).collect();
140 strings.serialize(serializer)
141 }
142 }
143 }
144}
145
146macro_rules! impl_display_and_from_str_enum {
147 ($enum:ident,
148 $($unit:ident),*;
149 $($tuple:ident),*;
150 $($tuple_default:ident),*;
151 $($tuple_option:ident),*;
152 $($tuple_string_default:ident),*
153 ) => {
154 impl std::fmt::Display for $enum {
155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 match self {
157 $( Self::$unit => write!(f, stringify!($unit)), )*
159
160 $( Self::$tuple(inner) => write!(f, concat!(stringify!($tuple), "({})"), inner), )*
162
163 $( Self::$tuple_default(inner) => {
165 if *inner == core::default::Default::default() {
166 write!(f, stringify!($tuple_default))
167 } else {
168 write!(f, concat!(stringify!($tuple_default), "({})"), inner)
169 }
170 }, )*
171
172 $( Self::$tuple_option(opt) => {
174 if let Some(inner) = opt {
175 write!(f, concat!(stringify!($tuple_option), "({})"), inner)
176 } else {
177 write!(f, stringify!($tuple_option))
178 }
179 }, )*
180
181 $( Self::$tuple_string_default(inner) => {
182 if inner.is_empty() {
183 write!(f, stringify!($tuple_string_default))
184 } else {
185 write!(f, concat!(stringify!($tuple_string_default), "({})"), inner)
186 }
187 }, )*
188 }
189 }
190 }
191
192 impl std::str::FromStr for $enum {
193 type Err = String;
194
195 fn from_str(s: &str) -> Result<Self, Self::Err> {
196 let (name, data) = if let Some(pos) = s.find('(') {
197 if s.ends_with(')') {
198 (&s[..pos], Some(&s[pos + 1..s.len() - 1]))
199 } else {
200 (s, None)
201 }
202 } else {
203 (s, None)
204 };
205
206 match name {
207 $( stringify!($unit) => Ok(Self::$unit), )*
208
209 $( stringify!($tuple) => {
210 let d = data
211 .ok_or_else(|| format!("Missing data for {}", stringify!($tuple)))?
212 .parse()
213 .map_err(|_| format!("Invalid data for {}", stringify!($tuple)))?;
214 Ok(Self::$tuple(d))
215 }, )*
216
217 $( stringify!($tuple_default) => {
218 let d = match data {
219 Some(val) => val.parse()
220 .map_err(|_| format!("Invalid data for {}", stringify!($tuple_default)))?,
221 None => Default::default(),
222 };
223 Ok(Self::$tuple_default(d))
224 }, )*
225
226 $( stringify!($tuple_option) => {
227 let d = match data {
228 Some(val) if !val.is_empty() => {
229 Some(val.parse().map_err(|_| format!("Invalid data for {}", stringify!($tuple_option)))?)
230 }
231 _ => None,
232 };
233 Ok(Self::$tuple_option(d))
234 }, )*
235
236 $( stringify!($tuple_string_default) => {
237 let d = match data {
238 Some(val) if !val.is_empty() => val.to_string(),
239 _ => String::new(),
240 };
241 Ok(Self::$tuple_string_default(d))
242 }, )*
243
244 _ => Err(format!("Unknown variant {}", name)),
245 }
246 }
247 }
248 };
249}
250
251impl_display_and_from_str_enum!(
253 Action,
254 Select, Deselect, Toggle, CycleAll, Accept, CyclePreview, CycleColumn,
255 PreviewHalfPageUp, PreviewHalfPageDown, HistoryUp, HistoryDown,
256 ChangePrompt, ChangeQuery, ToggleWrap, ToggleWrapPreview, ForwardChar,
257 BackwardChar, ForwardWord, BackwardWord, DeleteChar, DeleteWord,
258 DeleteLineStart, DeleteLineEnd, Cancel, Redraw;
259 Execute, Become, Reload, Print, Preview, SetInput, Column, Pos, InputPos;
261 Up, Down, PreviewUp, PreviewDown, Quit;
263 SwitchPreview, SetPreview, SetPrompt, SetHeader, SetFooter;
265 Help
267);
268
269use crate::{impl_int_wrapper};
270impl_int_wrapper!(Exit, i32, 1);
271impl_int_wrapper!(Count, u16, 1);
272