spin_plugins/badger/
store.rs1use std::path::PathBuf;
2
3use anyhow::anyhow;
4use serde::{Deserialize, Serialize};
5
6const DEFAULT_STORE_DIR: &str = "spin";
7const DEFAULT_STORE_FILE: &str = "plugins-notifications.json";
8
9pub struct BadgerRecordManager {
10 db_path: PathBuf,
11}
12
13#[derive(Serialize, Deserialize)]
14pub struct BadgerRecord {
15 name: String,
16 badgered_from: semver::Version,
17 badgered_to: Vec<semver::Version>,
18 when: chrono::DateTime<chrono::Utc>,
19}
20
21pub enum PreviousBadger {
22 Fresh,
23 FromCurrent {
24 to: Vec<semver::Version>,
25 when: chrono::DateTime<chrono::Utc>,
26 },
27}
28
29impl PreviousBadger {
30 fn includes(&self, version: &semver::Version) -> bool {
31 match self {
32 Self::Fresh => false,
33 Self::FromCurrent { to, .. } => to.contains(version),
34 }
35 }
36
37 pub fn includes_any(&self, version: &[&semver::Version]) -> bool {
38 version.iter().any(|version| self.includes(version))
39 }
40}
41
42impl BadgerRecordManager {
43 pub fn default() -> anyhow::Result<Self> {
44 let base_dir = dirs::cache_dir()
45 .or_else(|| dirs::home_dir().map(|p| p.join(".spin")))
46 .ok_or_else(|| anyhow!("Unable to get local data directory or home directory"))?;
47 let db_path = base_dir.join(DEFAULT_STORE_DIR).join(DEFAULT_STORE_FILE);
48 Ok(Self { db_path })
49 }
50
51 fn load(&self) -> Vec<BadgerRecord> {
52 match std::fs::read(&self.db_path) {
53 Ok(v) => serde_json::from_slice(&v).unwrap_or_default(),
54 Err(_) => vec![], }
56 }
57
58 fn save(&self, records: Vec<BadgerRecord>) -> anyhow::Result<()> {
59 if let Some(dir) = self.db_path.parent() {
60 std::fs::create_dir_all(dir)?;
61 }
62 let json = serde_json::to_vec_pretty(&records)?;
63 std::fs::write(&self.db_path, json)?;
64 Ok(())
65 }
66
67 async fn last_badger(&self, name: &str) -> Option<BadgerRecord> {
68 self.load().into_iter().find(|r| r.name == name)
69 }
70
71 pub async fn previous_badger(
72 &self,
73 name: &str,
74 current_version: &semver::Version,
75 ) -> PreviousBadger {
76 match self.last_badger(name).await {
77 Some(b) if &b.badgered_from == current_version => PreviousBadger::FromCurrent {
78 to: b.badgered_to,
79 when: b.when,
80 },
81 _ => PreviousBadger::Fresh,
82 }
83 }
84
85 pub async fn record_badger(&self, name: &str, from: &semver::Version, to: &[&semver::Version]) {
86 let new = BadgerRecord {
87 name: name.to_owned(),
88 badgered_from: from.clone(),
89 badgered_to: to.iter().cloned().map(<semver::Version>::clone).collect(),
90 when: chrono::Utc::now(),
91 };
92
93 let mut existing = self.load();
97 match existing.iter().position(|r| r.name == name) {
98 Some(index) => existing[index] = new,
99 None => existing.push(new),
100 };
101 _ = self.save(existing);
102 }
103}