1pub mod repository;
8pub mod pile;
9pub mod shove;
10pub mod timeline;
11pub mod objects;
12pub mod diff;
13pub mod merge;
14pub mod remote;
15pub mod commands;
16
17pub use repository::Repository;
19pub use pile::{Pile, PileEntry, PileStatus};
20pub use shove::{Shove, ShoveId, Author};
21pub use timeline::Timeline;
22pub use objects::{ObjectStore, ObjectId, Tree, TreeEntry};
23pub use merge::MergeStrategy;
24
25use std::path::PathBuf;
27use thiserror::Error;
28use anyhow::Result;
29
30#[derive(Error, Debug)]
32pub enum VcsError {
33 #[error("Repository not found at {0}")]
34 RepositoryNotFound(PathBuf),
35
36 #[error("Invalid repository state: {0}")]
37 InvalidRepositoryState(String),
38
39 #[error("Object not found: {0}")]
40 ObjectNotFound(String),
41
42 #[error("Timeline not found: {0}")]
43 TimelineNotFound(String),
44
45 #[error("Shove not found: {0}")]
46 ShoveNotFound(String),
47
48 #[error("Merge conflict in {0}")]
49 MergeConflict(PathBuf),
50
51 #[error("I/O error: {0}")]
52 IoError(#[from] std::io::Error),
53
54 #[error("Serialization error: {0}")]
55 SerializationError(String),
56
57 #[error("Remote error: {0}")]
58 RemoteError(String),
59}
60
61#[derive(Debug, Clone)]
63pub struct RepoStatus {
64 pub current_timeline: String,
65 pub head_shove: Option<ShoveId>,
66 pub piled_files: Vec<PileEntry>,
67 pub modified_files: Vec<PathBuf>,
68 pub untracked_files: Vec<PathBuf>,
69 pub conflicts: Vec<PathBuf>,
70}
71
72#[derive(Debug, Clone)]
74pub struct FileChange {
75 pub path: PathBuf,
76 pub change_type: ChangeType,
77 pub old_id: Option<ObjectId>,
78 pub new_id: Option<ObjectId>,
79}
80
81#[derive(Debug, Clone, PartialEq, Eq)]
83pub enum ChangeType {
84 Added,
85 Modified,
86 Deleted,
87 Renamed(PathBuf), Copied(PathBuf), }
90
91pub fn init() -> Result<()> {
93 Ok(())
95}
96
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use std::fs;
102 use tempfile::tempdir;
103 use chrono::Utc;
104
105 #[test]
106 fn test_repository_creation() {
107 let temp_dir = tempdir().unwrap();
108 let repo_path = temp_dir.path();
109
110 let repo = Repository::new(repo_path).unwrap();
112
113 assert!(repo_path.join(".pocket").exists());
115 assert!(repo_path.join(".pocket").join("config.toml").exists());
116 assert!(repo_path.join(".pocket").join("objects").exists());
117 assert!(repo_path.join(".pocket").join("shoves").exists());
118 assert!(repo_path.join(".pocket").join("timelines").exists());
119
120 let repo = Repository::open(repo_path).unwrap();
122 assert_eq!(repo.path, repo_path);
123 }
124
125 #[test]
126 fn test_pile_and_shove() {
127 let temp_dir = tempdir().unwrap();
128 let repo_path = temp_dir.path();
129
130 let mut repo = Repository::new(repo_path).unwrap();
132
133 let test_file = repo_path.join("test.txt");
135 fs::write(&test_file, "Hello, world!").unwrap();
136
137 repo.pile.add_path(&test_file, &repo.object_store).unwrap();
139
140 assert_eq!(repo.pile.entries.len(), 1);
142
143 let author = Author {
145 name: "Test User".to_string(),
146 email: "test@example.com".to_string(),
147 timestamp: Utc::now(),
148 };
149
150 let shove = repo.create_shove("Initial commit").unwrap();
151
152 assert!(repo_path.join(".pocket").join("shoves").join(format!("{}.toml", shove.as_str())).exists());
154
155 }
158
159 #[test]
160 fn test_timeline() {
161 let temp_dir = tempdir().unwrap();
162 let repo_path = temp_dir.path();
163
164 let mut repo = Repository::new(repo_path).unwrap();
166
167 let test_file = repo_path.join("test.txt");
169 fs::write(&test_file, "Hello, world!").unwrap();
170
171 repo.pile.add_path(&test_file, &repo.object_store).unwrap();
173
174 let author = Author {
176 name: "Test User".to_string(),
177 email: "test@example.com".to_string(),
178 timestamp: Utc::now(),
179 };
180
181 let shove = repo.create_shove("Initial commit").unwrap();
182
183 let timeline = Timeline::new("feature", Some(shove.clone()));
185
186 let timeline_path = repo_path.join(".pocket").join("timelines").join("feature.toml");
188 timeline.save(&timeline_path).unwrap();
189
190 assert!(timeline_path.exists());
192
193 let loaded_timeline = Timeline::load(&timeline_path).unwrap();
195 assert_eq!(loaded_timeline.head, Some(shove));
196 }
197}