Skip to main content

flake_edit/app/commands/
remove.rs

1//! `flake-edit remove`: drop an input or follows entry from the flake.
2//!
3//! Scripted mode takes a [`ChangeId`] directly. Interactive mode
4//! shows a picker over both top-level inputs and their indirect
5//! follows; follows entries display as `parent.nested => target` so
6//! the user sees the disconnected target, and the suffix is stripped
7//! before parsing back to a [`ChangeId`].
8
9use crate::change::{Change, ChangeId};
10use crate::edit::{FlakeEdit, sorted_input_ids};
11use crate::tui;
12
13use super::super::editor::Editor;
14use super::super::state::AppState;
15use super::{Error, Result, apply_change};
16
17pub fn remove(
18    editor: &Editor,
19    flake_edit: &mut FlakeEdit,
20    state: &AppState,
21    id: Option<String>,
22) -> Result<()> {
23    let change = if let Some(id) = id {
24        Change::Remove {
25            ids: vec![
26                ChangeId::parse(&id).map_err(|source| Error::InvalidInputId {
27                    id: id.clone(),
28                    source,
29                })?,
30            ],
31        }
32    } else if state.interactive {
33        let inputs = flake_edit.list();
34        let mut removable: Vec<String> = Vec::new();
35        for input_id in sorted_input_ids(inputs) {
36            let input = &inputs[input_id];
37            removable.push(input_id.clone());
38            for follows in input.follows() {
39                if let crate::input::Follows::Indirect { path, target } = follows {
40                    let target_str = match target {
41                        Some(t) => t.to_string(),
42                        None => "\"\"".to_string(),
43                    };
44                    removable.push(format!("{}.{} => {}", input_id, path, target_str));
45                }
46            }
47        }
48        if removable.is_empty() {
49            return Err(Error::NoInputs);
50        }
51
52        let tui_app = tui::App::remove("Remove", editor.text(), removable);
53        let Some(tui::AppResult::Change(tui_change)) = tui::run(tui_app)? else {
54            return Ok(());
55        };
56
57        // Strip the " => target" suffix on follows entries.
58        if let Change::Remove { ids } = tui_change {
59            let stripped_ids: Vec<_> = ids
60                .iter()
61                .filter_map(|id| {
62                    let s = id.to_string();
63                    let stripped = s.split(" => ").next().unwrap_or(&s);
64                    ChangeId::parse(stripped).ok()
65                })
66                .collect();
67            Change::Remove { ids: stripped_ids }
68        } else {
69            tui_change
70        }
71    } else {
72        return Err(Error::NoId);
73    };
74
75    apply_change(editor, flake_edit, state, change)
76}