use std::collections::HashMap;
use nix_uri::FlakeRef;
use nix_uri::urls::UrlWrapper;
use crate::change::Change;
use crate::diff::Diff;
use crate::edit::FlakeEdit;
use crate::lock::NestedInput;
#[derive(Debug, Clone)]
pub struct SingleSelectResult {
pub item: String,
pub show_diff: bool,
}
#[derive(Debug, Clone)]
pub struct MultiSelectResultData {
pub items: Vec<String>,
pub show_diff: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConfirmResultAction {
Apply,
Back,
Exit,
}
#[derive(Debug, Clone, PartialEq)]
pub enum AddPhase {
Uri,
Id,
}
#[derive(Debug, Clone, PartialEq)]
pub enum FollowPhase {
SelectInput,
SelectTarget,
}
#[derive(Debug, Clone)]
pub enum UpdateResult {
Continue,
Done,
Cancelled,
}
#[derive(Debug, Clone)]
pub enum AppResult {
Change(Change),
SingleSelect(SingleSelectResult),
MultiSelect(MultiSelectResultData),
Confirm(ConfirmResultAction),
}
#[derive(Debug, Clone)]
pub enum WorkflowData {
Add {
phase: AddPhase,
uri: Option<String>,
id: Option<String>,
},
Change {
selected_input: Option<String>,
uri: Option<String>,
input_uris: HashMap<String, String>,
all_inputs: Vec<String>,
},
Remove {
selected_inputs: Vec<String>,
all_inputs: Vec<String>,
},
SelectOne {
selected_input: Option<String>,
},
SelectMany {
selected_inputs: Vec<String>,
},
ConfirmOnly {
action: Option<ConfirmResultAction>,
},
Follow {
phase: FollowPhase,
selected_input: Option<String>,
selected_target: Option<String>,
nested_inputs: Vec<NestedInput>,
top_level_inputs: Vec<String>,
},
}
impl WorkflowData {
pub fn build_change(&self) -> Change {
match self {
WorkflowData::Add { id, uri, .. } => Change::Add {
id: id.clone(),
uri: uri.clone(),
flake: true,
},
WorkflowData::Change {
selected_input,
uri,
..
} => Change::Change {
id: selected_input.clone(),
uri: uri.clone(),
ref_or_rev: None,
},
WorkflowData::Remove {
selected_inputs, ..
} => {
if selected_inputs.is_empty() {
Change::None
} else {
Change::Remove {
ids: selected_inputs.iter().map(|s| s.clone().into()).collect(),
}
}
}
WorkflowData::SelectOne { .. }
| WorkflowData::SelectMany { .. }
| WorkflowData::ConfirmOnly { .. } => Change::None,
WorkflowData::Follow {
selected_input,
selected_target,
..
} => {
if let (Some(input), Some(target)) = (selected_input, selected_target) {
Change::Follows {
input: input.clone().into(),
target: target.clone(),
}
} else {
Change::None
}
}
}
}
}
pub fn parse_uri_and_infer_id(uri: &str) -> (Option<String>, String) {
let flake_ref: Result<FlakeRef, _> = UrlWrapper::convert_or_parse(uri);
if let Ok(flake_ref) = flake_ref {
let parsed_uri = flake_ref.to_string();
let final_uri = if parsed_uri.is_empty() || parsed_uri == "none" {
uri.to_string()
} else {
parsed_uri
};
(flake_ref.id(), final_uri)
} else {
(None, uri.to_string())
}
}
pub fn compute_diff(flake_text: &str, change: &Change) -> String {
if matches!(change, Change::None) {
return String::new();
}
let Ok(mut edit) = FlakeEdit::from_text(flake_text) else {
return "Error parsing flake".to_string();
};
let new_text = match edit.apply_change(change.clone()) {
Ok(Some(text)) => text,
Ok(None) => flake_text.to_string(),
Err(e) => return format!("Error: {e}"),
};
Diff::new(flake_text, &new_text).to_string_plain()
}