1use nix_uri::FlakeRef;
9
10use crate::change::{Change, ChangeId};
11use crate::edit::{FlakeEdit, InputMap, sorted_input_ids};
12use crate::tui;
13
14use super::super::editor::Editor;
15use super::super::state::AppState;
16use super::uri::{BuildKind, UriOptions, apply_uri_options, build_uri_change, transform_uri};
17use super::{Error, Result, apply_change};
18
19pub fn change(
20 editor: &Editor,
21 flake_edit: &mut FlakeEdit,
22 state: &AppState,
23 id: Option<String>,
24 uri: Option<String>,
25 opts: UriOptions<'_>,
26) -> Result<()> {
27 let inputs = flake_edit.list();
28
29 let change = match (id, uri, state.interactive) {
30 (None, None, true) | (None, Some(_), true) => {
33 change_full_interactive(editor, state, inputs, &opts)?
34 }
35 (Some(id), None, true) => change_uri_interactive(editor, state, inputs, &id, &opts)?,
37 (Some(id_val), Some(uri_str), _) => {
39 build_uri_change(BuildKind::Change, id_val, uri_str, &opts)?
40 }
41 (Some(uri), None, false) | (None, Some(uri), false) => change_infer_id(uri, &opts)?,
43 (None, None, false) => {
44 return Err(Error::NoId);
45 }
46 };
47
48 apply_change(editor, flake_edit, state, change)
49}
50
51fn change_full_interactive(
54 editor: &Editor,
55 state: &AppState,
56 inputs: &InputMap,
57 opts: &UriOptions<'_>,
58) -> Result<Change> {
59 let input_pairs: Vec<(String, String)> = sorted_input_ids(inputs)
60 .into_iter()
61 .map(|id| (id.clone(), inputs[id].url().to_string()))
62 .collect();
63
64 if input_pairs.is_empty() {
65 return Err(Error::NoInputs);
66 }
67
68 let tui_app = tui::App::change("Change", editor.text(), input_pairs, state.cache_config());
69 let Some(tui::AppResult::Change(tui_change)) = tui::run(tui_app)? else {
70 return Ok(Change::None);
71 };
72
73 if let Change::Change { id, uri, .. } = tui_change {
75 let final_uri = uri
76 .map(|u| transform_uri(u, opts.ref_or_rev, opts.shallow))
77 .transpose()?;
78 Ok(Change::Change { id, uri: final_uri })
79 } else {
80 Ok(tui_change)
81 }
82}
83
84fn change_uri_interactive(
87 editor: &Editor,
88 state: &AppState,
89 inputs: &InputMap,
90 id: &str,
91 opts: &UriOptions<'_>,
92) -> Result<Change> {
93 let current_uri = inputs.get(id).map(|i| i.url());
94 let tui_app = tui::App::change_uri(
95 "Change",
96 editor.text(),
97 id,
98 current_uri,
99 state.diff,
100 state.cache_config(),
101 );
102
103 let Some(tui::AppResult::Change(tui_change)) = tui::run(tui_app)? else {
104 return Ok(Change::None);
105 };
106
107 if let Change::Change {
109 uri: Some(new_uri), ..
110 } = tui_change
111 {
112 let final_uri = transform_uri(new_uri, opts.ref_or_rev, opts.shallow)?;
113 let id = ChangeId::parse(id).map_err(|source| Error::InvalidInputId {
114 id: id.to_string(),
115 source,
116 })?;
117 Ok(Change::Change {
118 id: Some(id),
119 uri: Some(final_uri),
120 })
121 } else {
122 Err(Error::NoUri)
123 }
124}
125
126fn change_infer_id(uri: String, opts: &UriOptions<'_>) -> Result<Change> {
129 let flake_ref: FlakeRef = uri.parse().map_err(|source| Error::InvalidUri {
130 uri: uri.clone(),
131 source,
132 })?;
133 let flake_ref = apply_uri_options(flake_ref, opts.ref_or_rev, opts.shallow);
134
135 let id = flake_ref
136 .id()
137 .map(str::to_owned)
138 .ok_or_else(|| Error::CouldNotInferId { uri: uri.clone() })?;
139 let id = ChangeId::parse(&id).map_err(|source| Error::InvalidInputId { id, source })?;
140 let final_uri = flake_ref.into_uri();
141
142 Ok(Change::Change {
143 id: Some(id),
144 uri: Some(final_uri),
145 })
146}