1use std::path::PathBuf;
6
7use serde::Serialize;
8
9#[derive(Debug, Clone, Serialize)]
14pub enum Operation {
15 CreateDataLink {
18 pack: String,
19 handler: String,
20 source: PathBuf,
21 },
22
23 CreateUserLink {
26 pack: String,
27 handler: String,
28 datastore_path: PathBuf,
29 user_path: PathBuf,
30 },
31
32 RunCommand {
34 pack: String,
35 handler: String,
36 executable: String,
37 arguments: Vec<String>,
38 sentinel: String,
39 },
40
41 CheckSentinel {
43 pack: String,
44 handler: String,
45 sentinel: String,
46 },
47}
48
49impl Operation {
50 pub fn pack(&self) -> &str {
51 match self {
52 Self::CreateDataLink { pack, .. }
53 | Self::CreateUserLink { pack, .. }
54 | Self::RunCommand { pack, .. }
55 | Self::CheckSentinel { pack, .. } => pack,
56 }
57 }
58
59 pub fn handler(&self) -> &str {
60 match self {
61 Self::CreateDataLink { handler, .. }
62 | Self::CreateUserLink { handler, .. }
63 | Self::RunCommand { handler, .. }
64 | Self::CheckSentinel { handler, .. } => handler,
65 }
66 }
67
68 pub fn kind(&self) -> &'static str {
70 match self {
71 Self::CreateDataLink { .. } => "CreateDataLink",
72 Self::CreateUserLink { .. } => "CreateUserLink",
73 Self::RunCommand { .. } => "RunCommand",
74 Self::CheckSentinel { .. } => "CheckSentinel",
75 }
76 }
77}
78
79#[derive(Debug, Clone, Serialize)]
89pub enum HandlerIntent {
90 Link {
93 pack: String,
94 handler: String,
95 source: PathBuf,
96 user_path: PathBuf,
97 },
98
99 Stage {
102 pack: String,
103 handler: String,
104 source: PathBuf,
105 },
106
107 Run {
109 pack: String,
110 handler: String,
111 executable: String,
112 arguments: Vec<String>,
113 sentinel: String,
114 },
115}
116
117impl HandlerIntent {
118 pub fn pack(&self) -> &str {
119 match self {
120 Self::Link { pack, .. } | Self::Stage { pack, .. } | Self::Run { pack, .. } => pack,
121 }
122 }
123
124 pub fn handler(&self) -> &str {
125 match self {
126 Self::Link { handler, .. }
127 | Self::Stage { handler, .. }
128 | Self::Run { handler, .. } => handler,
129 }
130 }
131}
132
133#[derive(Debug, Clone, Serialize)]
135pub struct OperationResult {
136 pub operation: Operation,
137 pub success: bool,
138 pub message: String,
139}
140
141impl OperationResult {
142 pub fn ok(operation: Operation, message: impl Into<String>) -> Self {
143 Self {
144 operation,
145 success: true,
146 message: message.into(),
147 }
148 }
149
150 pub fn fail(operation: Operation, message: impl Into<String>) -> Self {
151 Self {
152 operation,
153 success: false,
154 message: message.into(),
155 }
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test]
164 fn operation_accessors() {
165 let op = Operation::CreateDataLink {
166 pack: "vim".into(),
167 handler: "symlink".into(),
168 source: PathBuf::from("/src/vimrc"),
169 };
170 assert_eq!(op.pack(), "vim");
171 assert_eq!(op.handler(), "symlink");
172 assert_eq!(op.kind(), "CreateDataLink");
173 }
174
175 #[test]
176 fn handler_intent_accessors() {
177 let intent = HandlerIntent::Link {
178 pack: "git".into(),
179 handler: "symlink".into(),
180 source: PathBuf::from("/src/gitconfig"),
181 user_path: PathBuf::from("/home/.gitconfig"),
182 };
183 assert_eq!(intent.pack(), "git");
184 assert_eq!(intent.handler(), "symlink");
185 }
186
187 #[test]
188 fn operation_result_constructors() {
189 let op = Operation::CheckSentinel {
190 pack: "vim".into(),
191 handler: "install".into(),
192 sentinel: "abc".into(),
193 };
194 let ok = OperationResult::ok(op.clone(), "done");
195 assert!(ok.success);
196
197 let fail = OperationResult::fail(op, "oops");
198 assert!(!fail.success);
199 }
200
201 #[test]
202 fn operation_serializes() {
203 let op = Operation::RunCommand {
204 pack: "vim".into(),
205 handler: "install".into(),
206 executable: "echo".into(),
207 arguments: vec!["hi".into()],
208 sentinel: "s1".into(),
209 };
210 let json = serde_json::to_string(&op).unwrap();
211 assert!(json.contains("RunCommand"));
212 assert!(json.contains("echo"));
213 assert!(json.contains("hi"));
214 }
215}