use std::collections::HashMap;
use nix_uri::FlakeRef;
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 AddStep {
Uri,
Id,
}
#[derive(Debug, Clone, PartialEq)]
pub enum FollowStep {
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 {
step: AddStep,
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 {
step: FollowStep,
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
.as_deref()
.and_then(|s| crate::change::ChangeId::parse(s).ok()),
uri: uri.clone(),
flake: true,
},
WorkflowData::Change {
selected_input,
uri,
..
} => Change::Change {
id: selected_input
.as_deref()
.and_then(|s| crate::change::ChangeId::parse(s).ok()),
uri: uri.clone(),
},
WorkflowData::Remove {
selected_inputs, ..
} => {
if selected_inputs.is_empty() {
Change::None
} else {
Change::Remove {
ids: selected_inputs
.iter()
.filter_map(|s| crate::change::ChangeId::parse(s).ok())
.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) {
let parsed = crate::change::ChangeId::parse(input)
.ok()
.zip(crate::follows::AttrPath::parse(target).ok());
if let Some((change_id, target_path)) = parsed {
Change::Follows {
input: change_id,
target: target_path,
}
} else {
Change::None
}
} else {
Change::None
}
}
}
}
}
pub fn parse_uri_and_infer_id(uri: &str) -> (Option<String>, String) {
match uri.parse::<FlakeRef>() {
Ok(flake_ref) => {
let id = flake_ref.id().map(str::to_owned);
(id, flake_ref.into_uri())
}
Err(_) => (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(outcome) => outcome.text.unwrap_or_else(|| flake_text.to_string()),
Err(e) => return format!("Error: {e}"),
};
Diff::new(flake_text, &new_text).to_string_plain()
}