1use crate::bindings::*;
4use crate::page::content::ContentPage;
5
6use crate::utils::ComBuilder;
7use serde_json::{Map, Value as JsonValue, json};
8use std::sync::{Arc, Mutex};
9use windows::Win32::Foundation::{E_FAIL, ERROR_FILE_INVALID};
10use windows_core::{ComObject, Error, implement};
11
12#[implement(ICommandSettings)]
14pub struct CommandSettings(pub ComObject<ContentPage>);
15
16impl ICommandSettings_Impl for CommandSettings_Impl {
17 fn SettingsPage(&self) -> windows_core::Result<IContentPage> {
18 Ok(self.0.to_interface())
19 }
20}
21
22#[derive(Debug, Clone)]
23struct BasePropSetting {
24 id: String,
25 is_required: bool,
26 error_message: String,
27 title: String,
28 label: String,
29}
30
31impl BasePropSetting {
32 fn new(id: impl ToString) -> Self {
33 Self {
34 id: id.to_string(),
35 is_required: false,
36 error_message: String::new(),
37 title: String::new(),
38 label: String::new(),
39 }
40 }
41
42 fn serialize(&self) -> Map<String, JsonValue> {
43 let mut map = Map::new();
44 map.insert("id".into(), json!(self.id));
45 map.insert("isRequired".into(), json!(self.is_required));
46 map.insert("errorMessage".into(), json!(self.error_message));
47 map.insert("title".into(), json!(self.title));
48 map.insert("label".into(), json!(self.label));
49 map
50 }
51}
52
53trait SettingItem {
54 fn id(&self) -> &str;
55 fn base_prop_mut(&mut self) -> &mut BasePropSetting;
56 fn serialize_adaptive_card(&self) -> serde_json::Value;
57 fn serialize_value(&self) -> Option<serde_json::Value>;
58 fn update(&self, data: &Map<String, JsonValue>);
59}
60
61pub trait SettingBasePropModifier {
63 fn is_required(self, is_required: bool) -> Self;
65 fn error_message(self, error_message: impl ToString) -> Self;
67 fn caption(self, caption: impl ToString) -> Self;
69 fn description(self, description: impl ToString) -> Self;
71}
72
73impl<T> SettingBasePropModifier for T
74where
75 T: SettingItem,
76{
77 fn caption(mut self, caption: impl ToString) -> Self {
78 self.base_prop_mut().label = caption.to_string();
79 self
80 }
81
82 fn error_message(mut self, error_message: impl ToString) -> Self {
83 self.base_prop_mut().error_message = error_message.to_string();
84 self
85 }
86
87 fn description(mut self, description: impl ToString) -> Self {
88 self.base_prop_mut().title = description.to_string();
89 self
90 }
91
92 fn is_required(mut self, is_required: bool) -> Self {
93 self.base_prop_mut().is_required = is_required;
94 self
95 }
96}
97
98#[derive(Debug, Clone)]
100pub struct TextSetting {
101 placeholder: String,
102 pattern: String,
103 is_multiline: bool,
104 base_prop: BasePropSetting,
105 value: Arc<Mutex<Option<String>>>,
106}
107
108impl TextSetting {
109 pub fn new(id: impl ToString) -> Self {
113 Self {
114 placeholder: String::new(),
115 pattern: String::new(),
116 is_multiline: false,
117 base_prop: BasePropSetting::new(id),
118 value: Arc::new(Mutex::new(None)),
119 }
120 }
121
122 pub fn placeholder(mut self, placeholder: impl ToString) -> Self {
124 self.placeholder = placeholder.to_string();
125 self
126 }
127
128 pub fn pattern(mut self, pattern: impl ToString) -> Self {
130 self.pattern = pattern.to_string();
131 self
132 }
133
134 pub fn is_multiline(mut self, is_multiline: bool) -> Self {
136 self.is_multiline = is_multiline;
137 self
138 }
139
140 pub fn default(self, value: impl ToString) -> Self {
143 self.value
144 .lock()
145 .ok()
146 .map(|mut v| v.replace(value.to_string()));
147 self
148 }
149}
150
151#[derive(Debug, Clone)]
155pub struct NumberSetting {
156 placeholder: String,
157 min: Option<f64>,
158 max: Option<f64>,
159 base_prop: BasePropSetting,
160 value: Arc<Mutex<Option<f64>>>,
161}
162
163impl NumberSetting {
164 pub fn new(id: impl ToString) -> Self {
168 Self {
169 placeholder: String::new(),
170 min: None,
171 max: None,
172 base_prop: BasePropSetting::new(id),
173 value: Arc::new(Mutex::new(None)),
174 }
175 }
176
177 pub fn placeholder(mut self, placeholder: impl ToString) -> Self {
180 self.placeholder = placeholder.to_string();
181 self
182 }
183
184 pub fn min(mut self, min: f64) -> Self {
186 self.min = Some(min);
187 self
188 }
189
190 pub fn max(mut self, max: f64) -> Self {
192 self.max = Some(max);
193 self
194 }
195
196 pub fn default(self, value: f64) -> Self {
200 self.value.lock().ok().map(|mut v| v.replace(value));
201 self
202 }
203}
204
205#[derive(Debug, Clone)]
209pub struct ToggleSetting {
210 base_prop: BasePropSetting,
211 value: Arc<Mutex<Option<bool>>>,
212}
213
214impl ToggleSetting {
215 pub fn new(id: impl ToString) -> Self {
219 Self {
220 base_prop: BasePropSetting::new(id),
221 value: Arc::new(Mutex::new(None)),
222 }
223 }
224
225 pub fn default(self, value: bool) -> Self {
229 self.value.lock().ok().map(|mut v| v.replace(value));
230 self
231 }
232}
233
234pub trait Choice: Sized + Clone + 'static {
269 fn value(&self) -> &str;
273
274 fn title(&self) -> &str;
276}
277
278impl Choice for String {
279 fn value(&self) -> &str {
280 self
281 }
282
283 fn title(&self) -> &str {
284 self
285 }
286}
287
288impl Choice for (&'static str, &'static str) {
289 fn value(&self) -> &str {
290 self.0
291 }
292
293 fn title(&self) -> &str {
294 self.1
295 }
296}
297
298impl Choice for &'static str {
299 fn value(&self) -> &str {
300 self
301 }
302
303 fn title(&self) -> &str {
304 self
305 }
306}
307
308#[derive(Debug, Clone)]
310pub struct ChoiceSetSetting<T> {
311 choices: Vec<T>,
312 base_prop: BasePropSetting,
313 value: Arc<Mutex<Option<T>>>,
314}
315
316impl<T: Choice> ChoiceSetSetting<T> {
317 pub fn new(id: impl ToString) -> Self {
321 Self {
322 choices: Vec::new(),
323 base_prop: BasePropSetting::new(id),
324 value: Arc::new(Mutex::new(None)),
325 }
326 }
327
328 pub fn add_choice(mut self, choice: T) -> Self {
332 self.choices.push(choice);
333 self
334 }
335
336 pub fn choices(mut self, choices: Vec<T>) -> Self {
340 self.choices = choices;
341 self
342 }
343
344 pub fn default(self, value: T) -> Self {
348 self.value.lock().ok().map(|mut v| v.replace(value));
349 self
350 }
351}
352
353impl SettingItem for TextSetting {
354 fn base_prop_mut(&mut self) -> &mut BasePropSetting {
355 &mut self.base_prop
356 }
357
358 fn id(&self) -> &str {
359 &self.base_prop.id
360 }
361
362 fn serialize_adaptive_card(&self) -> serde_json::Value {
363 let mut map = self.base_prop.serialize();
364 map.insert("type".into(), json!("Input.Text"));
365 map.insert("placeholder".into(), json!(self.placeholder));
366 map.insert("isMultiline".into(), json!(self.is_multiline));
367 map.insert("pattern".into(), json!(self.pattern));
368 if let Some(value) = self.value.lock().ok().and_then(|v| (*v).clone()) {
369 map.insert("value".into(), json!(value));
370 }
371 json!(map)
372 }
373
374 fn update(&self, data: &Map<String, JsonValue>) {
375 if let Some(value) = data
376 .get(self.id())
377 .and_then(|v| v.as_str().map(|s| s.to_string()))
378 {
379 self.value.lock().ok().map(|mut v| v.replace(value));
380 }
381 }
382
383 fn serialize_value(&self) -> Option<serde_json::Value> {
384 self.value
385 .lock()
386 .ok()
387 .and_then(|v| v.clone())
388 .map(|v| json!(v))
389 }
390}
391
392impl SettingItem for NumberSetting {
395 fn base_prop_mut(&mut self) -> &mut BasePropSetting {
396 &mut self.base_prop
397 }
398
399 fn id(&self) -> &str {
400 &self.base_prop.id
401 }
402
403 fn serialize_adaptive_card(&self) -> serde_json::Value {
404 let mut map = self.base_prop.serialize();
405 map.insert("type".into(), json!("Input.Number"));
406 map.insert("placeholder".into(), json!(self.placeholder));
407 if let Some(min) = self.min {
408 map.insert("min".into(), json!(min));
409 }
410 if let Some(max) = self.max {
411 map.insert("max".into(), json!(max));
412 }
413 if let Some(value) = self.value.lock().ok().and_then(|v| *v) {
414 map.insert("value".into(), json!(value));
415 }
416
417 json!(map)
418 }
419
420 fn update(&self, data: &Map<String, JsonValue>) {
421 if let Some(value) = data
422 .get(self.id())
423 .and_then(|v| v.as_str())
424 .and_then(|s| s.parse().ok())
425 {
426 self.value.lock().ok().map(|mut v| v.replace(value));
427 }
428 }
429
430 fn serialize_value(&self) -> Option<serde_json::Value> {
431 self.value
432 .lock()
433 .ok()
434 .and_then(|v| v.clone())
435 .map(|v| json!(v.to_string()))
436 }
437}
438
439impl SettingItem for ToggleSetting {
442 fn base_prop_mut(&mut self) -> &mut BasePropSetting {
443 &mut self.base_prop
444 }
445
446 fn id(&self) -> &str {
447 &self.base_prop.id
448 }
449
450 fn serialize_adaptive_card(&self) -> serde_json::Value {
451 let mut map = self.base_prop.serialize();
452 map.insert("type".into(), json!("Input.Toggle"));
453 if let Some(value) = self.value.lock().ok().and_then(|v| *v) {
454 map.insert("value".into(), json!(value.to_string()));
455 }
456 json!(map)
457 }
458
459 fn update(&self, data: &Map<String, JsonValue>) {
460 if let Some(value) = data.get(self.id()).and_then(|v| match v.as_str() {
461 Some("true") => Some(true),
462 Some("false") => Some(false),
463 _ => None,
464 }) {
465 self.value.lock().ok().map(|mut v| v.replace(value));
466 }
467 }
468
469 fn serialize_value(&self) -> Option<serde_json::Value> {
470 self.value
471 .lock()
472 .ok()
473 .and_then(|v| v.clone())
474 .map(|v| json!(v))
475 }
476}
477
478impl<T: Choice> SettingItem for ChoiceSetSetting<T> {
479 fn base_prop_mut(&mut self) -> &mut BasePropSetting {
480 &mut self.base_prop
481 }
482
483 fn id(&self) -> &str {
484 &self.base_prop.id
485 }
486
487 fn serialize_adaptive_card(&self) -> serde_json::Value {
488 let mut map = self.base_prop.serialize();
489 map.insert("type".into(), json!("Input.ChoiceSet"));
490 map.insert(
491 "choices".into(),
492 json!(
493 self.choices
494 .iter()
495 .map(|c| {
496 json!({
497 "value": c.value(),
498 "title": c.title(),
499 })
500 })
501 .collect::<Vec<_>>()
502 ),
503 );
504 if let Some(value) = self.value.lock().ok().and_then(|v| (*v).clone()) {
505 map.insert("value".into(), json!(value.value()));
506 }
507 json!(map)
508 }
509
510 fn update(&self, data: &Map<String, JsonValue>) {
511 if let Some(value) = data
512 .get(self.id())
513 .and_then(|v| v.as_str().map(|s| s.to_string()))
514 {
515 if let Some(choice) = self.choices.iter().find(|c| c.value() == value) {
516 self.value
517 .lock()
518 .ok()
519 .map(|mut v| v.replace(choice.clone()));
520 }
521 }
522 }
523
524 fn serialize_value(&self) -> Option<serde_json::Value> {
525 self.value
526 .lock()
527 .ok()
528 .and_then(|v| v.clone())
529 .map(|v| json!(v.value()))
530 }
531}
532
533trait ValueLock {
534 type Value: Clone + 'static;
535 fn value_lock(&self) -> Arc<Mutex<Option<Self::Value>>>;
536}
537
538impl ValueLock for TextSetting {
539 type Value = String;
540 fn value_lock(&self) -> Arc<Mutex<Option<Self::Value>>> {
541 self.value.clone()
542 }
543}
544
545impl ValueLock for NumberSetting {
546 type Value = f64;
547 fn value_lock(&self) -> Arc<Mutex<Option<Self::Value>>> {
548 self.value.clone()
549 }
550}
551
552impl ValueLock for ToggleSetting {
553 type Value = bool;
554 fn value_lock(&self) -> Arc<Mutex<Option<Self::Value>>> {
555 self.value.clone()
556 }
557}
558
559impl<T: Choice> ValueLock for ChoiceSetSetting<T> {
560 type Value = T;
561 fn value_lock(&self) -> Arc<Mutex<Option<Self::Value>>> {
562 self.value.clone()
563 }
564}
565
566#[implement(ICommandSettings)]
571#[derive(Clone)]
572pub struct JsonCommandSettings {
573 path: std::path::PathBuf,
574 settings: Vec<Arc<dyn SettingItem + Send + Sync>>,
575 page: ComObject<ContentPage>,
576}
577
578impl JsonCommandSettings {
579 pub fn new(path: std::path::PathBuf) -> Self {
587 use crate::cmd::BaseCommandBuilder;
588 use crate::icon::{IconData, IconInfo};
589 use crate::page::BasePageBuilder;
590 use crate::page::content::ContentPageBuilder;
591 let page = ContentPageBuilder::new(
592 BasePageBuilder::new(
593 BaseCommandBuilder::new()
594 .name("Settings")
595 .icon(IconInfo::new(IconData::from("\u{E713}")))
596 .build(),
597 )
598 .build(),
599 )
600 .build();
601 Self {
602 path,
603 settings: Vec::new(),
604 page,
605 }
606 }
607
608 #[allow(private_bounds, reason = "Trait bounds are only for internal use")]
618 pub fn add_setting<
619 T: SettingItem + ValueLock<Value = V> + Send + Sync + 'static,
620 V: Clone + 'static,
621 >(
622 &mut self,
623 setting: T,
624 ) -> Arc<Mutex<Option<V>>> {
625 let lock = setting.value_lock();
626 self.settings.push(Arc::new(setting));
627 lock
628 }
629
630 fn template_json(&self) -> JsonValue {
631 let body: Vec<_> = self
632 .settings
633 .iter()
634 .map(|s| s.serialize_adaptive_card())
635 .collect();
636 let mut keys: Map<String, JsonValue> = Map::new();
637 for id in self.settings.iter().map(|s| s.id()) {
638 keys.insert(id.to_string(), json!(id));
639 }
640
641 let v = json!({
642 "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
643 "type": "AdaptiveCard",
644 "version": "1.5",
645 "body": body,
646 "actions": [
647 {
648 "type": "Action.Submit",
649 "title": "Save",
650 "data": keys
651 }
652 ]
653 });
654 v
655 }
656
657 fn read_settings(&self) -> Option<()> {
658 let data = std::fs::read_to_string(&self.path).ok()?;
659 let data: Map<String, JsonValue> = serde_json::from_str(&data).ok()?;
660 for setting in self.settings.iter() {
661 setting.update(&data);
662 }
663 Some(())
664 }
665
666 fn write_settings(&self) -> Option<()> {
667 let mut data = Map::new();
668 for setting in self.settings.iter() {
669 if let Some(value) = setting.serialize_value() {
670 data.insert(setting.id().to_string(), value);
671 }
672 }
673 let json = serde_json::to_string(&data).ok()?;
674 let path = self.path.canonicalize().ok()?;
675 std::fs::create_dir_all(path.parent()?).ok()?;
676 std::fs::write(&path, json).ok()?;
677 Some(())
678 }
679}
680
681impl ICommandSettings_Impl for JsonCommandSettings_Impl {
682 fn SettingsPage(&self) -> windows_core::Result<IContentPage> {
683 use crate::cmd_result::CommandResult;
684 use crate::content::FormContentBuilder;
685 let slf = self.this.clone();
686 slf.read_settings();
687 let form = FormContentBuilder::new()
688 .template_json(
689 serde_json::to_string(&slf.template_json())
690 .map_err(|e| Error::new(E_FAIL, e.to_string()))?,
691 )
692 .submit(move |form, input, _| {
693 let data: Map<String, JsonValue> =
694 serde_json::from_str(&input.to_string_lossy())
695 .map_err(|e| Error::new(E_FAIL, e.to_string()))?;
696 for setting in slf.settings.iter() {
697 setting.update(&data);
698 }
699
700 slf.write_settings().ok_or(Error::new(
701 ERROR_FILE_INVALID.to_hresult(),
702 format!("Failed to write settings to {}", slf.path.display()),
703 ))?;
704
705 let new_template = serde_json::to_string(&slf.template_json())
706 .map_err(|e| Error::new(E_FAIL, e.to_string()))?;
707 let mut guard = form.template_json_mut()?;
708 *guard = new_template.into();
709 drop(guard);
710 slf.page.emit_items_changed(slf.page.to_interface(), -1);
711 Ok(CommandResult::GoHome)
712 })
713 .build();
714
715 let mut guard = self.page.contents_mut()?;
716 *guard = vec![form.into()];
717 drop(guard);
718
719 Ok(self.page.to_interface())
720 }
721}