use nix_uri::FlakeRef;
use crate::change::{Change, ChangeId};
use crate::edit::{FlakeEdit, InputMap, sorted_input_ids};
use crate::tui;
use super::super::editor::Editor;
use super::super::state::AppState;
use super::uri::{BuildKind, UriOptions, apply_uri_options, build_uri_change, transform_uri};
use super::{Error, Result, apply_change};
pub fn change(
editor: &Editor,
flake_edit: &mut FlakeEdit,
state: &AppState,
id: Option<String>,
uri: Option<String>,
opts: UriOptions<'_>,
) -> Result<()> {
let inputs = flake_edit.list();
let change = match (id, uri, state.interactive) {
(None, None, true) | (None, Some(_), true) => {
change_full_interactive(editor, state, inputs, &opts)?
}
(Some(id), None, true) => change_uri_interactive(editor, state, inputs, &id, &opts)?,
(Some(id_val), Some(uri_str), _) => {
build_uri_change(BuildKind::Change, id_val, uri_str, &opts)?
}
(Some(uri), None, false) | (None, Some(uri), false) => change_infer_id(uri, &opts)?,
(None, None, false) => {
return Err(Error::NoId);
}
};
apply_change(editor, flake_edit, state, change)
}
fn change_full_interactive(
editor: &Editor,
state: &AppState,
inputs: &InputMap,
opts: &UriOptions<'_>,
) -> Result<Change> {
let input_pairs: Vec<(String, String)> = sorted_input_ids(inputs)
.into_iter()
.map(|id| (id.clone(), inputs[id].url().to_string()))
.collect();
if input_pairs.is_empty() {
return Err(Error::NoInputs);
}
let tui_app = tui::App::change("Change", editor.text(), input_pairs, state.cache_config());
let Some(tui::AppResult::Change(tui_change)) = tui::run(tui_app)? else {
return Ok(Change::None);
};
if let Change::Change { id, uri, .. } = tui_change {
let final_uri = uri
.map(|u| transform_uri(u, opts.ref_or_rev, opts.shallow))
.transpose()?;
Ok(Change::Change { id, uri: final_uri })
} else {
Ok(tui_change)
}
}
fn change_uri_interactive(
editor: &Editor,
state: &AppState,
inputs: &InputMap,
id: &str,
opts: &UriOptions<'_>,
) -> Result<Change> {
let current_uri = inputs.get(id).map(|i| i.url());
let tui_app = tui::App::change_uri(
"Change",
editor.text(),
id,
current_uri,
state.diff,
state.cache_config(),
);
let Some(tui::AppResult::Change(tui_change)) = tui::run(tui_app)? else {
return Ok(Change::None);
};
if let Change::Change {
uri: Some(new_uri), ..
} = tui_change
{
let final_uri = transform_uri(new_uri, opts.ref_or_rev, opts.shallow)?;
let id = ChangeId::parse(id).map_err(|source| Error::InvalidInputId {
id: id.to_string(),
source,
})?;
Ok(Change::Change {
id: Some(id),
uri: Some(final_uri),
})
} else {
Err(Error::NoUri)
}
}
fn change_infer_id(uri: String, opts: &UriOptions<'_>) -> Result<Change> {
let flake_ref: FlakeRef = uri.parse().map_err(|source| Error::InvalidUri {
uri: uri.clone(),
source,
})?;
let flake_ref = apply_uri_options(flake_ref, opts.ref_or_rev, opts.shallow);
let id = flake_ref
.id()
.map(str::to_owned)
.ok_or_else(|| Error::CouldNotInferId { uri: uri.clone() })?;
let id = ChangeId::parse(&id).map_err(|source| Error::InvalidInputId { id, source })?;
let final_uri = flake_ref.into_uri();
Ok(Change::Change {
id: Some(id),
uri: Some(final_uri),
})
}