quartz_cli/action/
handle.rs

1use 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    /// Immediatly switches to this handle after creating it
18    #[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    /// Make handle empty. Using it with other editing options will write a new endpoint in
30    /// place of the old one
31    #[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    /// Delete child handles recursively
52    #[arg(long, short = 'r')]
53    recursive: bool,
54
55    /// Handles to be removed
56    #[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()); // copy
236        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}