1use crate::{
4 error::Result,
5 types::{Cell, ModalItem, RemoteCommand},
6};
7use parking_lot::Mutex;
8use serde::Deserialize;
9use std::{collections::HashMap, sync::Arc};
10
11#[derive(Debug)]
17pub struct PluginSharedState {
18 pub cells: Vec<Cell>,
20 pub cols: u16,
22 pub rows: u16,
24 pub cursor: (u16, u16),
26 pub env: HashMap<String, String>,
28 pub data: HashMap<String, String>,
30 pub commands: Vec<ModalItem>,
32}
33
34impl PluginSharedState {
35 pub fn new(cols: u16, rows: u16) -> Self {
37 let size = (cols as usize) * (rows as usize);
38 Self {
39 cells: vec![Cell::default(); size],
40 cols,
41 rows,
42 cursor: (0, 0),
43 env: std::env::vars().collect(),
44 data: HashMap::new(),
45 commands: Vec::new(),
46 }
47 }
48
49 pub fn get_cell(&self, x: u16, y: u16) -> Option<Cell> {
51 if x >= self.cols || y >= self.rows {
52 return None;
53 }
54 let idx = (y as usize) * (self.cols as usize) + (x as usize);
55 self.cells.get(idx).copied()
56 }
57
58 pub fn set_cell(&mut self, x: u16, y: u16, cell: Cell) -> bool {
60 if x >= self.cols || y >= self.rows {
61 return false;
62 }
63 let idx = (y as usize) * (self.cols as usize) + (x as usize);
64 if let Some(c) = self.cells.get_mut(idx) {
65 *c = cell;
66 true
67 } else {
68 false
69 }
70 }
71
72 pub fn get_line(&self, y: u16) -> Option<String> {
74 if y >= self.rows {
75 return None;
76 }
77 let start = (y as usize) * (self.cols as usize);
78 let end = start + (self.cols as usize);
79 Some(
80 self.cells[start..end]
81 .iter()
82 .map(|c| c.c)
83 .collect::<String>()
84 .trim_end()
85 .to_string(),
86 )
87 }
88}
89
90#[derive(Clone)]
92pub struct PluginContext {
93 pub config: PluginConfigData,
95 pub state: Arc<Mutex<PluginSharedState>>,
97 pub logger_name: String,
99 pub commands: Arc<Mutex<Vec<RemoteCommand>>>,
101}
102
103impl PluginContext {
104 pub fn new(
106 config: PluginConfigData,
107 state: Arc<Mutex<PluginSharedState>>,
108 logger_name: impl Into<String>,
109 ) -> Self {
110 Self {
111 config,
112 state,
113 logger_name: logger_name.into(),
114 commands: Arc::new(Mutex::new(Vec::new())),
115 }
116 }
117
118 pub fn queue_command(&self, cmd: RemoteCommand) {
120 self.commands.lock().push(cmd);
121 }
122
123 pub fn get_cell(&self, x: u16, y: u16) -> Option<Cell> {
125 self.state.lock().get_cell(x, y)
126 }
127
128 pub fn set_cell(&self, x: u16, y: u16, cell: Cell) -> bool {
130 self.state.lock().set_cell(x, y, cell)
131 }
132
133 pub fn get_line(&self, y: u16) -> Option<String> {
135 self.state.lock().get_line(y)
136 }
137
138 pub fn get_size(&self) -> (u16, u16) {
140 let state = self.state.lock();
141 (state.cols, state.rows)
142 }
143
144 pub fn get_cursor(&self) -> (u16, u16) {
146 self.state.lock().cursor
147 }
148
149 pub fn get_env(&self, key: &str) -> Option<String> {
151 self.state.lock().env.get(key).cloned()
152 }
153
154 pub fn set_data(&self, key: impl Into<String>, value: impl Into<String>) {
156 self.state.lock().data.insert(key.into(), value.into());
157 }
158
159 pub fn get_data(&self, key: &str) -> Option<String> {
161 self.state.lock().data.get(key).cloned()
162 }
163
164 pub fn log(&self, level: LogLevel, message: &str) {
169 match level {
171 LogLevel::Error => log::error!("[{}] {}", self.logger_name, message),
172 LogLevel::Warn => log::warn!("[{}] {}", self.logger_name, message),
173 LogLevel::Info => log::info!("[{}] {}", self.logger_name, message),
174 LogLevel::Debug => log::debug!("[{}] {}", self.logger_name, message),
175 }
176
177 self.queue_command(RemoteCommand::PluginLog {
179 plugin_name: self.logger_name.clone(),
180 level,
181 message: message.to_string(),
182 });
183 }
184
185 pub fn notify(&self, title: &str, body: &str, level: NotifyLevel) {
190 self.queue_command(RemoteCommand::PluginNotify {
192 title: title.to_string(),
193 body: body.to_string(),
194 level,
195 });
196 }
197
198 pub fn notify_info(&self, title: &str, body: &str) {
200 self.notify(title, body, NotifyLevel::Info);
201 }
202
203 pub fn notify_success(&self, title: &str, body: &str) {
205 self.notify(title, body, NotifyLevel::Success);
206 }
207
208 pub fn notify_warning(&self, title: &str, body: &str) {
210 self.notify(title, body, NotifyLevel::Warning);
211 }
212
213 pub fn notify_error(&self, title: &str, body: &str) {
215 self.notify(title, body, NotifyLevel::Error);
216 }
217}
218
219#[derive(Debug, Copy, Clone, PartialEq, Eq)]
221pub enum LogLevel {
222 Error,
223 Warn,
224 Info,
225 Debug,
226}
227
228#[derive(Debug, Copy, Clone, PartialEq, Eq)]
230pub enum NotifyLevel {
231 Error,
232 Warning,
233 Info,
234 Success,
235}
236
237#[derive(Debug, Clone, Default, Deserialize, serde::Serialize)]
239pub struct PluginConfigData {
240 #[serde(flatten)]
241 pub data: HashMap<String, toml::Value>,
242}
243
244impl PluginConfigData {
245 pub fn get<T: for<'de> Deserialize<'de>>(&self, key: &str) -> Result<T> {
247 let value = self.data.get(key).ok_or_else(|| {
248 crate::error::PluginError::ConfigError(format!("Missing key: {}", key))
249 })?;
250 T::deserialize(value.clone())
251 .map_err(|e| crate::error::PluginError::ConfigError(e.to_string()))
252 }
253
254 pub fn get_opt<T: for<'de> Deserialize<'de>>(&self, key: &str) -> Option<T> {
256 self.data
257 .get(key)
258 .and_then(|v| T::deserialize(v.clone()).ok())
259 }
260}