use super::picker::{PickKind, pick_file};
use crate::{MainWindow, gui::show_toast};
use indicatrix_cut_core::{
NativeDesignFile,
native::{LoadNativeOnlyResult, load_native_only},
native_path_for_asc,
};
use std::path::{Path, PathBuf};
pub(super) enum PickedNative {
Pair(Box<PickedPair>),
AscOnly { asc_path: PathBuf, asc_text: String },
SelfContained {
native_path: PathBuf,
loaded: Box<LoadNativeOnlyResult>,
asc_filename: String,
},
}
pub(super) struct PickedPair {
pub(super) native_path: PathBuf,
pub(super) parsed_native: Box<NativeDesignFile>,
pub(super) asc_text: String,
pub(super) native_text: String,
}
pub(super) enum NativePairOrSelfContained {
Pair {
parsed_native: Box<NativeDesignFile>,
asc_text: String,
native_text: String,
},
SelfContained {
loaded: Box<LoadNativeOnlyResult>,
asc_filename: String,
},
}
pub(super) fn read_native_pair_then(
ui: &MainWindow,
native_path: &Path,
on_done: impl FnOnce(&MainWindow, Option<NativePairOrSelfContained>) + 'static,
) {
let native_text = match std::fs::read_to_string(native_path) {
Ok(text) => text,
Err(e) => {
show_toast(
ui,
&format!("Failed to read {}: {e}", native_path.display()),
"error",
);
on_done(ui, None);
return;
}
};
let parsed_native = match indicatrix_cut_core::native::parse_toml_string(&native_text) {
Ok(n) => Box::new(n),
Err(e) => {
show_toast(
ui,
&format!(
"'{}' is not a valid native design file: {e}",
native_path.display()
),
"error",
);
on_done(ui, None);
return;
}
};
let asc_path = native_path.with_file_name(&parsed_native.asc_filename);
let asc_filename = parsed_native.asc_filename.clone();
let asc_findable = asc_path.is_file()
|| indicatrix_cut_core::asc_path_for_native(native_path)
.is_some_and(|guessed| guessed != asc_path && guessed.is_file());
if !asc_findable && let Ok(loaded) = load_native_only(&native_text) {
on_done(
ui,
Some(NativePairOrSelfContained::SelfContained {
loaded: Box::new(loaded),
asc_filename,
}),
);
return;
}
resolve_paired_asc_text_then(
ui,
native_path,
&asc_path,
&asc_filename,
move |ui, asc_text| {
on_done(
ui,
asc_text.map(|asc_text| NativePairOrSelfContained::Pair {
parsed_native,
asc_text,
native_text,
}),
);
},
);
}
fn resolve_paired_asc_text_then(
ui: &MainWindow,
native_path: &Path,
recorded_asc_path: &Path,
recorded_asc_filename: &str,
on_done: impl FnOnce(&MainWindow, Option<String>) + 'static,
) {
if let Ok(text) = std::fs::read_to_string(recorded_asc_path) {
on_done(ui, Some(text));
return;
}
if let Some(guessed) = indicatrix_cut_core::asc_path_for_native(native_path)
&& guessed != recorded_asc_path
&& let Ok(text) = std::fs::read_to_string(&guessed)
{
on_done(ui, Some(text));
return;
}
show_toast(
ui,
&format!(
"'{}' names a paired .asc file '{recorded_asc_filename}', but it could not be found \
next to it. Locate it to continue.",
native_path.display()
),
"info",
);
pick_file(ui, PickKind::LocateAsc, move |ui, located| {
let Some(located) = located else {
on_done(ui, None);
return;
};
match std::fs::read_to_string(&located) {
Ok(text) => on_done(ui, Some(text)),
Err(e) => {
show_toast(
ui,
&format!("Failed to read {}: {e}", located.display()),
"error",
);
on_done(ui, None);
}
}
});
}
fn picked_native_from_pair_result(
native_path: PathBuf,
outcome: NativePairOrSelfContained,
) -> PickedNative {
match outcome {
NativePairOrSelfContained::Pair {
parsed_native,
asc_text,
native_text,
} => PickedNative::Pair(Box::new(PickedPair {
native_path,
parsed_native,
asc_text,
native_text,
})),
NativePairOrSelfContained::SelfContained {
loaded,
asc_filename,
} => PickedNative::SelfContained {
native_path,
loaded,
asc_filename,
},
}
}
fn read_picked_asc_then(
ui: &MainWindow,
asc_path: PathBuf,
on_done: impl FnOnce(&MainWindow, Option<PickedNative>) + 'static,
) {
let native_path = native_path_for_asc(&asc_path);
if native_path.is_file() {
let native_path_for_read = native_path.clone();
read_native_pair_then(ui, &native_path_for_read, move |ui, result| {
on_done(
ui,
result.map(|outcome| picked_native_from_pair_result(native_path, outcome)),
);
});
return;
}
let asc_text = match std::fs::read_to_string(&asc_path) {
Ok(text) => text,
Err(e) => {
show_toast(
ui,
&format!("Failed to read {}: {e}", asc_path.display()),
"error",
);
on_done(ui, None);
return;
}
};
on_done(ui, Some(PickedNative::AscOnly { asc_path, asc_text }));
}
pub(super) fn pick_native_or_asc_then(
ui: &MainWindow,
on_done: impl FnOnce(&MainWindow, Option<PickedNative>) + 'static,
) {
pick_file(ui, PickKind::OpenNativeOrAsc, move |ui, picked_path| {
let Some(picked_path) = picked_path else {
on_done(ui, None);
return;
};
if picked_path
.extension()
.is_some_and(|e| e.eq_ignore_ascii_case("asc"))
{
read_picked_asc_then(ui, picked_path, on_done);
return;
}
let picked_path_for_read = picked_path.clone();
read_native_pair_then(ui, &picked_path_for_read, move |ui, result| {
on_done(
ui,
result.map(|outcome| picked_native_from_pair_result(picked_path, outcome)),
);
});
});
}