quartz_cli/action/
handle.rs1use std::collections::VecDeque;
2use std::process::ExitCode;
3
4use crate::{
5 endpoint::{Endpoint, EndpointHandle, EndpointPatch},
6 validator, Ctx, QuartzResult, StateField,
7};
8use colored::Colorize;
9
10#[derive(clap::Args, Debug)]
11pub struct CreateArgs {
12 handle: String,
13
14 #[command(flatten)]
15 patch: EndpointPatch,
16
17 #[arg(name = "use", long)]
19 switch: bool,
20}
21
22#[derive(clap::Args, Debug)]
23pub struct SwitchArgs {
24 handle: Option<String>,
25
26 #[command(flatten)]
27 patch: EndpointPatch,
28
29 #[arg(long)]
32 empty: bool,
33}
34
35#[derive(clap::Args, Debug)]
36pub struct CpArgs {
37 #[arg(long, short = 'r')]
38 recursive: bool,
39
40 src: String,
41 dest: String,
42}
43
44#[derive(clap::Args, Debug)]
45pub struct MvArgs {
46 handles: Vec<String>,
47}
48
49#[derive(clap::Args, Debug)]
50pub struct RmArgs {
51 #[arg(long, short = 'r')]
53 recursive: bool,
54
55 #[arg(name = "HANDLE", required = true)]
57 handles: Vec<String>,
58}
59
60pub fn create(ctx: &Ctx, mut args: CreateArgs) {
61 if args.handle.is_empty() {
62 panic!("missing endpoint handle");
63 }
64
65 let handle = EndpointHandle::from(args.handle);
66
67 if handle.exists(ctx) {
68 panic!("endpoint already exists");
69 }
70
71 let mut endpoint = Endpoint::from(&mut args.patch);
72 endpoint.set_handle(ctx, &handle);
73
74 if args.switch {
75 if let Ok(()) = StateField::Endpoint.set(ctx, &handle.path.join("/")) {
76 println!("Switched to {} endpoint", handle.handle().green());
77 } else {
78 panic!("failed to switch to {} endpoint", handle.handle().red());
79 }
80 }
81
82 handle.write(ctx);
83 endpoint.write();
84}
85
86pub fn switch(ctx: &mut Ctx, mut args: SwitchArgs) {
87 let handle = if let Some(mut handle) = args.handle {
88 if handle == "-" {
89 if let Ok(previous_handle) = StateField::PreviousEndpoint.get(ctx) {
90 handle = previous_handle;
91 }
92 }
93
94 let handle = EndpointHandle::from(handle);
95
96 if !handle.exists(ctx) {
97 eprint!("Handle {} doesn't exist", handle.handle().red(),);
98
99 if ctx.confirm("Do you wish to create it?") {
100 return create(
101 ctx,
102 CreateArgs {
103 handle: handle.handle(),
104 patch: args.patch,
105 switch: true,
106 },
107 );
108 } else {
109 ctx.code(ExitCode::FAILURE);
110 return;
111 }
112 }
113
114 let previous = StateField::Endpoint.get(ctx);
115 if StateField::Endpoint
116 .set(ctx, &handle.path.join("/"))
117 .is_ok()
118 {
119 if let Ok(prev) = previous {
120 let _ = StateField::PreviousEndpoint.set(ctx, &prev);
121 }
122
123 println!("Switched to {} endpoint", handle.handle().green());
124 } else {
125 panic!("failed to switch to {} endpoint", handle.handle().red());
126 }
127
128 handle
129 } else {
130 ctx.require_handle()
131 };
132
133 if args.empty {
134 handle.make_empty(ctx);
135 }
136
137 if !args.patch.has_changes() {
138 return;
139 }
140
141 let mut endpoint = handle
142 .endpoint(ctx)
143 .unwrap_or(Endpoint::new(handle.dir(ctx)));
144
145 endpoint.update(&mut args.patch);
146 endpoint.write();
147}
148
149pub fn cp(ctx: &Ctx, args: CpArgs) -> QuartzResult {
150 let src = ctx.require_input_handle(&args.src);
151 if !src.exists(ctx) {
152 panic!("no such handle: {}", src.handle());
153 }
154
155 let mut queue = VecDeque::<EndpointHandle>::new();
156 queue.push_back(src);
157
158 while let Some(mut src) = queue.pop_front() {
159 let endpoint = src.endpoint(ctx);
160
161 if args.recursive {
162 for child in src.children(ctx) {
163 queue.push_back(child.clone());
164 }
165 }
166
167 src.replace(&args.src, &args.dest);
168 src.write(ctx);
169
170 if let Some(mut endpoint) = endpoint {
171 endpoint.set_handle(ctx, &src);
172 endpoint.write();
173 }
174 }
175
176 Ok(())
177}
178
179pub fn rm(ctx: &mut Ctx, args: RmArgs) -> QuartzResult {
180 for name in args.handles {
181 let handle = EndpointHandle::from(&name);
182
183 if !handle.exists(ctx) {
184 ctx.code(ExitCode::FAILURE);
185 eprintln!("no such handle: {name}");
186 continue;
187 }
188
189 if !handle.children(ctx).is_empty() && !args.recursive {
190 ctx.code(ExitCode::FAILURE);
191 eprintln!(
192 "{} has child handles. Use -r option to confirm",
193 handle.handle(),
194 );
195 continue;
196 }
197
198 if std::fs::remove_dir_all(handle.dir(ctx)).is_ok() {
199 println!("Deleted endpoint {}", handle.handle());
200 } else {
201 ctx.code(ExitCode::FAILURE);
202 eprintln!("failed to delete endpoint {}", handle.handle());
203 }
204 }
205
206 Ok(())
207}
208
209pub fn mv(ctx: &mut Ctx, mut args: MvArgs) -> QuartzResult {
210 if args.handles.is_empty() {
211 panic!("no handles specified");
212 }
213
214 if args.handles.len() == 1 {
215 panic!("missing target handle");
216 }
217
218 let dest = EndpointHandle::from(args.handles.pop().unwrap());
219 let mut original_handles = Vec::<EndpointHandle>::new();
220 let mut queue = VecDeque::<(&str, EndpointHandle)>::new();
221
222 for arg in &args.handles {
223 let handle = EndpointHandle::from(arg);
224 if !handle.exists(ctx) {
225 ctx.code(ExitCode::FAILURE);
226 eprintln!("no such handle: {arg}");
227 continue;
228 }
229
230 original_handles.push(handle);
231 queue.push_back((arg, EndpointHandle::from(arg)));
232 }
233
234 while let Some((src, mut handle)) = queue.pop_front() {
235 let mut dest = EndpointHandle::from(dest.handle()); for child in handle.children(ctx) {
237 queue.push_back((src, child));
238 }
239
240 let maybe_endpoint = handle.endpoint(ctx);
241
242 if args.handles.len() >= 2 {
243 dest.path.push(handle.path.last().unwrap().to_string());
244 }
245
246 handle.replace(src, &dest.handle());
247 handle.write(ctx);
248
249 if let Some(mut endpoint) = maybe_endpoint {
250 endpoint.set_handle(ctx, &handle);
251 endpoint.write();
252 }
253 }
254
255 for handle in original_handles {
256 let _ = std::fs::remove_dir_all(handle.dir(ctx));
257 }
258
259 Ok(())
260}
261
262pub fn edit(ctx: &mut Ctx) -> QuartzResult {
263 let handle = ctx.require_handle();
264
265 ctx.edit(
266 &handle.dir(ctx).join("endpoint.toml"),
267 validator::toml_as::<Endpoint>,
268 )?;
269
270 Ok(())
271}