openvcs_core/
models.rs

1use serde::{Deserialize, Serialize};
2use std::sync::Arc;
3
4#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
5#[serde(tag = "type")]
6pub enum BranchKind {
7    Local,
8    Remote { remote: String },
9    Unknown,
10}
11
12#[derive(Serialize, Deserialize, Default, Clone, Copy, Debug, PartialEq, Eq)]
13pub struct StatusSummary {
14    pub untracked: usize,
15    pub modified: usize,
16    pub staged: usize,
17    pub conflicted: usize,
18}
19
20#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
21pub struct BranchItem {
22    pub name: String,
23    pub full_ref: String,
24    pub kind: BranchKind,
25    pub current: bool,
26}
27
28/// A single file’s status in the working tree / index.
29/// `status` is backend-agnostic (e.g., "A" | "M" | "D" | "R?" etc).
30#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
31pub struct FileEntry {
32    pub path: String,
33    #[serde(default)]
34    pub old_path: Option<String>,
35    pub status: String,
36    #[serde(default)]
37    pub staged: bool,
38    #[serde(default)]
39    pub resolved_conflict: bool,
40    pub hunks: Vec<String>,
41}
42
43#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
44pub struct ConflictDetails {
45    pub path: String,
46    pub ours: Option<String>,
47    pub theirs: Option<String>,
48    pub base: Option<String>,
49    #[serde(default)]
50    pub binary: bool,
51    #[serde(default)]
52    pub lfs_pointer: bool,
53}
54
55#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
56#[serde(rename_all = "kebab-case")]
57pub enum ConflictSide {
58    Ours,
59    Theirs,
60}
61
62/// Flat status summary plus file list.
63#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Default)]
64pub struct StatusPayload {
65    pub files: Vec<FileEntry>,
66    pub ahead: u32,
67    pub behind: u32,
68}
69
70/// Lightweight commit representation for lists.
71#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
72pub struct CommitItem {
73    pub id: String,
74    pub msg: String,
75    pub meta: String,
76    pub author: String,
77}
78
79/// Options controlling fetch behavior.
80#[derive(Serialize, Deserialize, Default, Clone, Copy, Debug, PartialEq, Eq)]
81pub struct FetchOptions {
82    pub prune: bool,
83}
84
85/// A single stash entry (backend-agnostic)
86#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
87pub struct StashItem {
88    pub selector: String,
89    pub msg: String,
90    pub meta: String,
91}
92
93/// Query for commit history.
94#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Default)]
95pub struct LogQuery {
96    pub rev: Option<String>,
97    pub path: Option<String>,
98    pub since_utc: Option<String>,
99    pub until_utc: Option<String>,
100    pub author_contains: Option<String>,
101    pub skip: u32,
102    pub limit: u32,
103    pub topo_order: bool,
104    pub include_merges: bool,
105}
106
107impl LogQuery {
108    pub fn head(limit: u32) -> Self {
109        Self {
110            limit,
111            ..Default::default()
112        }
113    }
114}
115
116#[derive(Serialize, Deserialize, Clone, Debug, Default)]
117pub struct Capabilities {
118    pub commits: bool,
119    pub branches: bool,
120    pub tags: bool,
121    pub staging: bool,
122    pub push_pull: bool,
123    pub fast_forward: bool,
124}
125
126#[derive(Serialize, Deserialize, Clone, Debug)]
127#[serde(tag = "type", rename_all = "kebab-case")]
128pub enum VcsEvent {
129    Info {
130        msg: String,
131    },
132    RemoteMessage(String),
133    Progress {
134        phase: String,
135        detail: String,
136    },
137    Auth {
138        method: String,
139        detail: String,
140    },
141    PushStatus {
142        refname: String,
143        status: Option<String>,
144    },
145    Warning(String),
146    Error(String),
147}
148
149pub type OnEvent = Arc<dyn Fn(VcsEvent) + Send + Sync + 'static>;
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154
155    #[test]
156    fn log_query_head_sets_limit_and_defaults_rest() {
157        let query = LogQuery::head(25);
158        assert_eq!(query.limit, 25);
159        assert!(query.rev.is_none());
160        assert!(query.path.is_none());
161        assert_eq!(query.skip, 0);
162        assert!(!query.topo_order);
163    }
164
165    #[test]
166    fn status_summary_default_is_zeroed() {
167        let summary = StatusSummary::default();
168        assert_eq!(summary.untracked, 0);
169        assert_eq!(summary.modified, 0);
170        assert_eq!(summary.staged, 0);
171        assert_eq!(summary.conflicted, 0);
172    }
173}