pocket_cli/vcs/
objects.rs1use std::path::{Path, PathBuf};
6use std::fs;
7use serde::{Serialize, Deserialize};
8use anyhow::{Result, anyhow};
9use sha2::{Sha256, Digest};
10
11#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
13pub struct ObjectId(String);
14
15impl ObjectId {
16 pub fn from_content(content: &[u8]) -> Self {
18 let mut hasher = Sha256::new();
19 hasher.update(content);
20 let hash = hasher.finalize();
21 Self(format!("{:x}", hash))
22 }
23
24 pub fn from_str(s: &str) -> Result<Self> {
26 Ok(Self(s.to_string()))
27 }
28
29 pub fn as_str(&self) -> &str {
31 &self.0
32 }
33}
34
35#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
37pub enum EntryType {
38 File,
39 Tree,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct TreeEntry {
45 pub name: String,
46 pub id: ObjectId,
47 pub entry_type: EntryType,
48 pub permissions: u32,
49}
50
51#[derive(Debug, Serialize, Deserialize)]
53pub struct Tree {
54 pub entries: Vec<TreeEntry>,
55}
56
57pub struct ObjectStore {
59 base_path: PathBuf,
60}
61
62impl ObjectStore {
63 pub fn new(base_path: PathBuf) -> Self {
65 Self { base_path }
66 }
67
68 pub fn store_file(&self, path: &Path) -> Result<ObjectId> {
70 let content = fs::read(path)?;
71 self.store_object(&content)
72 }
73
74 pub fn store_object(&self, content: &[u8]) -> Result<ObjectId> {
76 let id = ObjectId::from_content(content);
77 let object_path = self.get_object_path(&id);
78
79 if let Some(parent) = object_path.parent() {
81 fs::create_dir_all(parent)?;
82 }
83
84 if !object_path.exists() {
86 fs::write(object_path, content)?;
87 }
88
89 Ok(id)
90 }
91
92 pub fn get_object(&self, id: &ObjectId) -> Result<Vec<u8>> {
94 let path = self.get_object_path(id);
95 if !path.exists() {
96 return Err(anyhow!("Object not found: {}", id.as_str()));
97 }
98
99 Ok(fs::read(path)?)
100 }
101
102 pub fn has_object(&self, id: &ObjectId) -> bool {
104 self.get_object_path(id).exists()
105 }
106
107 fn get_object_path(&self, id: &ObjectId) -> PathBuf {
109 let id_str = id.as_str();
110 let prefix = &id_str[0..2];
112 let suffix = &id_str[2..];
113 self.base_path.join(prefix).join(suffix)
114 }
115
116 pub fn store_tree(&self, tree: &Tree) -> Result<ObjectId> {
118 let content = toml::to_string(tree)?;
119 self.store_object(content.as_bytes())
120 }
121
122 pub fn get_tree(&self, id: &ObjectId) -> Result<Tree> {
124 let content = self.get_object(id)?;
125 let content_str = String::from_utf8(content)?;
126 let tree: Tree = toml::from_str(&content_str)?;
127 Ok(tree)
128 }
129
130 }