1#![allow(dead_code)]
2use std::{collections::HashMap, path::PathBuf};
3
4use crate::{core::watcher::WatchContext, log::logger::Logger};
5use anyhow::Result;
6use serde::{Deserialize, Serialize};
7use tokio::{fs, sync::RwLock};
8
9#[doc = include_str!("docs/app_state.md")]
10#[derive(Default)]
11pub struct AppState {
12 pub watches: RwLock<HashMap<String, WatchContext>>,
13}
14
15impl AppState {
16 pub async fn load_from_disk() -> Result<Self, anyhow::Error> {
17 let registry = AppState::load_watches().await?;
18 let mut watches: HashMap<String, WatchContext> = HashMap::new();
19
20 for mut ctx in registry.projects {
21 let logger = Logger::new(&ctx.log_path()).await?;
22 ctx.logger = logger;
23 watches.insert(ctx.id.clone(), ctx);
24 }
25
26 Ok(Self {
27 watches: RwLock::new(watches),
28 })
29 }
30
31 pub async fn save_to_disk(&self) -> Result<()> {
32 let guard = self.watches.read().await;
33 let registry = WatchRegistry {
34 projects: guard.values().cloned().collect(),
35 };
36 save_watches(®istry).await
37 }
38
39 pub async fn init_watch_file() -> Result<()> {
44 let path = get_watch_path();
45 println!("watch file at: {}", path.to_str().unwrap());
46 if !fs::try_exists(&path).await? {
47 if let Some(parent) = path.parent() {
48 fs::create_dir_all(parent).await?;
49 }
50 let empty = WatchRegistry::default();
51 let json = serde_json::to_string_pretty(&empty)?;
52 fs::write(path, json).await?;
53 }
54 Ok(())
55 }
56
57 pub async fn load_watches() -> Result<WatchRegistry> {
59 let path = get_watch_path();
60 let data = fs::read_to_string(&path).await?;
61 let watches = serde_json::from_str(&data)?;
62 Ok(watches)
63 }
64
65 pub async fn add_watch(ctx: &WatchContext) -> Result<()> {
67 let mut watches = AppState::load_watches().await?;
68
69 if let Some(existing) = watches
70 .projects
71 .iter_mut()
72 .find(|p| p.project_dir == ctx.project_dir)
73 {
74 *existing = ctx.clone();
75 } else {
76 watches.projects.push(ctx.clone());
77 }
78
79 save_watches(&watches).await?;
80 Ok(())
81 }
82
83 pub async fn remove_watch_by_id(id: &str) -> Result<()> {
85 let mut watches = AppState::load_watches().await?;
86 watches.projects.retain(|p| p.id != id); save_watches(&watches).await?;
88 Ok(())
89 }
90}
91
92#[derive(Serialize, Deserialize, Clone, Default)]
93pub struct WatchRegistry {
94 pub projects: Vec<WatchContext>,
95}
96
97pub async fn get_id_by_name(name: &str) -> Result<Option<String>> {
98 let watches = AppState::load_watches().await?;
99 Ok(watches
100 .projects
101 .iter()
102 .find(|p| p.repo.name == name)
103 .map(|p| p.id.clone()))
104}
105
106pub async fn get_name_by_id(id: &str) -> Result<Option<String>> {
107 let watches = AppState::load_watches().await?;
108 Ok(watches
109 .projects
110 .iter()
111 .find(|p| p.id == id)
112 .map(|p| p.repo.name.clone()))
113}
114
115fn get_watch_path() -> PathBuf {
120 let path = dirs::data_local_dir()
121 .unwrap_or_else(|| std::env::current_dir().expect("Failed to get current directory"));
122 path.join("fleetd").join("watches.json")
123}
124
125async fn save_watches(watches: &WatchRegistry) -> Result<()> {
126 let path = get_watch_path();
127 let json = serde_json::to_string_pretty(watches)?;
128 fs::write(path, json).await?;
129 Ok(())
130}