1use crate::command::Command;
2use orfail::OrFail;
3use pagurus::event::KeyEvent;
4use pati::{Color, Point};
5use serde::{Deserialize, Serialize};
6use std::{collections::BTreeMap, path::Path};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct Config {
10 #[serde(default)]
11 pub key: KeyConfig,
12
13 #[serde(default)]
14 pub initial: InitialConfig,
15}
16
17impl Config {
18 pub fn load_config_file() -> orfail::Result<Option<Self>> {
19 let Ok(home_dir) = std::env::var("HOME") else {
20 return Ok(None);
21 };
22
23 let path = Path::new(&home_dir).join(".config").join("patica.json");
24 if !path.exists() {
25 return Ok(None);
26 }
27
28 let json = std::fs::read_to_string(&path)
29 .or_fail_with(|e| format!("Failed to read config file {}: {e}", path.display()))?;
30 serde_json::from_str(&json)
31 .or_fail_with(|e| {
32 format!(
33 "Failed to parse config file: path={}, reason={e}",
34 path.display()
35 )
36 })
37 .map(Some)
38 }
39}
40
41impl Default for Config {
42 fn default() -> Self {
43 serde_json::from_str(include_str!("../default-config.json")).expect("unreachable")
44 }
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct InitialConfig {
49 pub background_color: Color,
50 pub anchors: BTreeMap<String, Point>,
51}
52
53impl Default for InitialConfig {
54 fn default() -> Self {
55 Config::default().initial
56 }
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct KeyConfig(BTreeMap<Key, Vec<Command>>);
61
62impl KeyConfig {
63 pub fn get_commands(&self, key: KeyEvent) -> impl Iterator<Item = &Command> {
64 let key = Key(key);
65 self.0.get(&key).into_iter().flatten()
66 }
67}
68
69impl Default for KeyConfig {
70 fn default() -> Self {
71 Config::default().key
72 }
73}
74
75#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
76#[serde(try_from = "String", into = "String")]
77pub struct Key(KeyEvent);
78
79impl PartialOrd for Key {
80 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
81 Some(self.cmp(other))
82 }
83}
84
85impl Ord for Key {
86 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
87 self.to_string().cmp(&other.to_string())
88 }
89}
90
91impl std::fmt::Display for Key {
92 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93 if self.0.ctrl {
94 write!(f, "Ctrl+")?;
95 }
96 if self.0.alt {
97 write!(f, "Alt+")?;
98 }
99 match self.0.key {
100 pagurus::event::Key::Return => write!(f, "Enter"),
101 pagurus::event::Key::Left => write!(f, "Left"),
102 pagurus::event::Key::Right => write!(f, "Right"),
103 pagurus::event::Key::Up => write!(f, "Up"),
104 pagurus::event::Key::Down => write!(f, "Down"),
105 pagurus::event::Key::Backspace => write!(f, "Backspace"),
106 pagurus::event::Key::Delete => write!(f, "Delete"),
107 pagurus::event::Key::Tab => write!(f, "Tab"),
108 pagurus::event::Key::BackTab => write!(f, "BackTab"),
109 pagurus::event::Key::Esc => write!(f, "Esc"),
110 pagurus::event::Key::Char(c) => write!(f, "{}", c),
111 _ => unreachable!(),
112 }
113 }
114}
115
116impl From<Key> for String {
117 fn from(key: Key) -> Self {
118 key.to_string()
119 }
120}
121
122impl TryFrom<String> for Key {
123 type Error = orfail::Failure;
124
125 fn try_from(s: String) -> Result<Self, Self::Error> {
126 let mut ctrl = false;
127 let mut alt = false;
128 let mut tokens = if s == "+" {
129 vec!["+"]
131 } else {
132 s.split('+').collect::<Vec<_>>()
133 };
134
135 let last = tokens
136 .pop()
137 .or_fail_with(|()| "Empty key string".to_owned())?;
138 let key = match last {
139 "Enter" => pagurus::event::Key::Return,
140 "Left" => pagurus::event::Key::Left,
141 "Right" => pagurus::event::Key::Right,
142 "Up" => pagurus::event::Key::Up,
143 "Down" => pagurus::event::Key::Down,
144 "Backspace" => pagurus::event::Key::Backspace,
145 "Delete" => pagurus::event::Key::Delete,
146 "Tab" => pagurus::event::Key::Tab,
147 "BackTab" => pagurus::event::Key::BackTab,
148 "Esc" => pagurus::event::Key::Esc,
149 _ if last.chars().count() == 1 => match last.chars().next().or_fail()? {
150 c @ ('a'..='z'
151 | 'A'..='Z'
152 | '0'..='9'
153 | ' '
154 | '+'
155 | '-'
156 | '_'
157 | '('
158 | ')'
159 | '{'
160 | '}'
161 | '['
162 | ']'
163 | '<'
164 | '>'
165 | ';'
166 | ':'
167 | '='
168 | '.'
169 | ','
170 | '!'
171 | '?'
172 | '/'
173 | '@'
174 | '#'
175 | '$'
176 | '%'
177 | '^'
178 | '&'
179 | '*'
180 | '"'
181 | '\''
182 | '`'
183 | '~') => pagurus::event::Key::Char(c),
184 _ => return Err(orfail::Failure::new(format!("Unknown key: {last:?}"))),
185 },
186 _ => return Err(orfail::Failure::new(format!("Unknown key: {last:?}"))),
187 };
188 for token in tokens {
189 match token {
190 "Ctrl" => ctrl = true,
191 "Alt" => alt = true,
192 _ => {
193 return Err(orfail::Failure::new(format!(
194 "Unknown key modifier: {token:?}"
195 )))
196 }
197 }
198 }
199
200 Ok(Self(KeyEvent { key, ctrl, alt }))
201 }
202}