braid_core/fs/
versions.rs1use crate::core::Version;
2use crate::core::{BraidError, Result};
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use std::path::PathBuf;
6use tokio::fs;
7
8#[derive(Debug, Serialize, Deserialize, Default)]
9pub struct VersionStore {
10 pub file_versions: HashMap<String, FileVersion>,
12 #[serde(skip)]
13 pub path: PathBuf,
14}
15
16#[derive(Debug, Serialize, Deserialize, Clone)]
17pub struct FileVersion {
18 pub current_version: Vec<String>, pub parents: Vec<String>,
20 #[serde(default)]
22 pub content_hash: Option<String>,
23}
24
25impl VersionStore {
26 pub async fn load() -> Result<Self> {
27 let store_path = get_store_path()?;
28 Self::load_from(store_path).await
29 }
30
31 pub async fn load_from(path: PathBuf) -> Result<Self> {
32 if !path.exists() {
33 return Ok(Self {
34 file_versions: HashMap::new(),
35 path,
36 });
37 }
38
39 let content = fs::read_to_string(&path).await?;
40 let mut store: VersionStore = serde_json::from_str(&content).unwrap_or_default();
41 store.path = path;
42 Ok(store)
43 }
44
45 pub async fn save(&self) -> Result<()> {
46 if let Some(parent) = self.path.parent() {
47 fs::create_dir_all(parent)
48 .await
49 .map_err(|e| BraidError::Io(e))?;
50 }
51 let content = serde_json::to_string_pretty(self).map_err(|e| BraidError::Json(e))?;
52 fs::write(&self.path, content)
53 .await
54 .map_err(|e| BraidError::Io(e))?;
55 Ok(())
56 }
57
58 pub fn update(&mut self, url: &str, version: Vec<Version>, parents: Vec<Version>) {
59 self.file_versions.insert(
60 url.to_string(),
61 FileVersion {
62 current_version: version.iter().map(|v| v.to_string()).collect(),
63 parents: parents.iter().map(|v| v.to_string()).collect(),
64 content_hash: None,
65 },
66 );
67 }
68
69 pub fn update_with_hash(
71 &mut self,
72 url: &str,
73 version: Vec<Version>,
74 parents: Vec<Version>,
75 hash: Option<String>,
76 ) {
77 self.file_versions.insert(
78 url.to_string(),
79 FileVersion {
80 current_version: version.iter().map(|v| v.to_string()).collect(),
81 parents: parents.iter().map(|v| v.to_string()).collect(),
82 content_hash: hash,
83 },
84 );
85 }
86
87 pub fn get(&self, url: &str) -> Option<&FileVersion> {
88 self.file_versions.get(url)
89 }
90
91 pub fn get_version_by_hash(&self, _fullpath: &str, hash: &str) -> Option<Vec<String>> {
94 for (_, fv) in &self.file_versions {
96 if fv.content_hash.as_deref() == Some(hash) {
97 return Some(fv.current_version.clone());
98 }
99 }
100 None
101 }
102
103 pub fn set_content_hash(&mut self, url: &str, hash: String) {
105 if let Some(fv) = self.file_versions.get_mut(url) {
106 fv.content_hash = Some(hash);
107 }
108 }
109}
110
111fn get_store_path() -> Result<PathBuf> {
112 if let Ok(root) = std::env::var("BRAID_ROOT") {
113 return Ok(PathBuf::from(root).join(".braidfs").join("versions.json"));
114 }
115 let home =
116 dirs::home_dir().ok_or_else(|| BraidError::Fs("Could not find home directory".into()))?;
117 Ok(home.join("http").join(".braidfs").join("versions.json"))
118}