1pub mod error;
29pub mod head;
31pub mod index;
33pub mod layout;
35pub mod lockfile;
37pub mod loose;
39pub mod pack;
41pub mod reflog;
43pub mod refs;
45pub mod repo;
47pub mod tree_diff;
49
50pub use error::StoreError;
51pub use head::HeadState;
52
53use std::path::Path;
54
55use claw_core::cof::{cof_decode, cof_encode};
56use claw_core::hash::content_hash;
57use claw_core::id::ObjectId;
58use claw_core::object::Object;
59
60use crate::layout::RepoLayout;
61
62pub struct ClawStore {
63 layout: RepoLayout,
64}
65
66impl ClawStore {
67 pub fn init(root: &Path) -> Result<Self, StoreError> {
68 let layout = RepoLayout::new(root);
69 layout.create_dirs()?;
70 repo::write_default_config(&layout)?;
71 head::write_head(
72 &layout,
73 &HeadState::Symbolic {
74 ref_name: "heads/main".to_string(),
75 },
76 )?;
77 Ok(Self { layout })
78 }
79
80 pub fn open(root: &Path) -> Result<Self, StoreError> {
81 let layout = RepoLayout::new(root);
82 if !layout.claw_dir().exists() {
83 return Err(StoreError::NotARepository(root.to_path_buf()));
84 }
85 if !layout.head_file().exists() {
87 head::write_head(
88 &layout,
89 &HeadState::Symbolic {
90 ref_name: "heads/main".to_string(),
91 },
92 )?;
93 }
94 if !layout.reflogs_dir().exists() {
95 std::fs::create_dir_all(layout.reflogs_dir())?;
96 }
97 Ok(Self { layout })
98 }
99
100 pub fn root(&self) -> &Path {
101 self.layout.root()
102 }
103
104 pub fn layout(&self) -> &RepoLayout {
105 &self.layout
106 }
107
108 pub fn store_object(&self, obj: &Object) -> Result<ObjectId, StoreError> {
109 let payload = obj.serialize_payload()?;
110 let type_tag = obj.type_tag();
111 let id = content_hash(type_tag, &payload);
112 let cof_data = cof_encode(type_tag, &payload)?;
113 loose::write_loose_object(&self.layout, &id, &cof_data)?;
114 Ok(id)
115 }
116
117 pub fn load_object(&self, id: &ObjectId) -> Result<Object, StoreError> {
118 let cof_data = loose::read_loose_object(&self.layout, id)?;
119 let (type_tag, payload) = cof_decode(&cof_data)?;
120 let obj = Object::deserialize_payload(type_tag, &payload)?;
121 Ok(obj)
122 }
123
124 pub fn load_cof_bytes(&self, id: &ObjectId) -> Result<Vec<u8>, StoreError> {
129 loose::read_loose_object(&self.layout, id)
130 }
131
132 pub fn has_object(&self, id: &ObjectId) -> bool {
133 loose::loose_object_path(&self.layout, id).exists()
134 }
135
136 pub fn set_ref(&self, name: &str, target: &ObjectId) -> Result<(), StoreError> {
137 refs::write_ref(&self.layout, name, target)
138 }
139
140 pub fn get_ref(&self, name: &str) -> Result<Option<ObjectId>, StoreError> {
141 refs::read_ref(&self.layout, name)
142 }
143
144 pub fn list_refs(&self, prefix: &str) -> Result<Vec<(String, ObjectId)>, StoreError> {
145 refs::list_refs(&self.layout, prefix)
146 }
147
148 pub fn delete_ref(&self, name: &str) -> Result<(), StoreError> {
149 refs::delete_ref(&self.layout, name)
150 }
151
152 pub fn read_head(&self) -> Result<HeadState, StoreError> {
153 head::read_head(&self.layout)
154 }
155
156 pub fn write_head(&self, state: &HeadState) -> Result<(), StoreError> {
157 head::write_head(&self.layout, state)
158 }
159
160 pub fn resolve_head(&self) -> Result<Option<ObjectId>, StoreError> {
161 head::resolve_head(&self.layout)
162 }
163
164 pub fn update_ref_cas(
165 &self,
166 name: &str,
167 expected_old: Option<&ObjectId>,
168 new_target: &ObjectId,
169 author: &str,
170 message: &str,
171 ) -> Result<(), StoreError> {
172 refs::update_ref_cas(
173 &self.layout,
174 name,
175 expected_old,
176 new_target,
177 author,
178 message,
179 )
180 }
181
182 pub fn list_object_ids(&self) -> Result<Vec<ObjectId>, StoreError> {
183 loose::list_loose_object_ids(&self.layout)
184 }
185}