chore: fix nickel-roundtrip render and optimize/cleanup

This commit is contained in:
Jesús Pérez 2025-12-27 02:23:50 +00:00
parent e58905c1f1
commit b906168f6d
Signed by: jesus
GPG Key ID: 9F243E355E0BC939
2 changed files with 76 additions and 37 deletions

View File

@ -325,7 +325,10 @@ impl RoundtripConfig {
})?;
// Parse TOML form definition
let form = form_parser::parse_toml(&form_content)?;
let mut form = form_parser::parse_toml(&form_content)?;
// Migrate to unified elements format if needed
form.migrate_to_elements();
// Extract base directory for resolving relative paths (includes, fragments)
let base_dir = form_path.parent().unwrap_or_else(|| Path::new("."));

View File

@ -286,6 +286,58 @@ fn detect_output_format(output_path: Option<&PathBuf>, default_format: &str) ->
.to_string()
}
/// Load default values from a Nickel (.ncl) file
///
/// Exports the .ncl file to JSON, then extracts and flattens values
/// matching the form fields.
///
/// Returns a HashMap of field names to their default values.
fn load_nickel_defaults(
ncl_path: &Path,
form_elements: &[form_parser::FormElement],
verbose: bool,
) -> Result<std::collections::HashMap<String, serde_json::Value>> {
use typedialog_core::nickel::NickelCli;
if verbose {
eprintln!("[web] Loading default values from {}", ncl_path.display());
}
NickelCli::verify()?;
let value = NickelCli::export(ncl_path)?;
match value {
serde_json::Value::Object(map) => {
// Extract fields from elements (which includes fragments after expand_includes)
let form_fields: Vec<form_parser::FieldDefinition> = form_elements
.iter()
.filter_map(|elem| match elem {
form_parser::FormElement::Field(f) => Some(f.clone()),
_ => None,
})
.collect();
// Extract Nickel structure AND flatten everything (combine both approaches)
let mut combined = extract_nickel_defaults(&map, &form_fields);
let flattened = flatten_json_object(&map);
// Flattened values fill in gaps not covered by extraction
for (k, v) in flattened {
combined.entry(k).or_insert(v);
}
if verbose {
eprintln!("[web] Loaded {} default field values", combined.len());
}
Ok(combined)
}
_ => Err(Error::validation_failed(
"Defaults .ncl must export to a JSON object".to_string(),
)),
}
}
async fn execute_form(
config: PathBuf,
defaults: Option<PathBuf>,
@ -314,44 +366,14 @@ async fn execute_form(
let initial_values = if let Some(defaults_path_input) = defaults {
// Resolve defaults path with cascading search
let defaults_path = resolve_file_path(&defaults_path_input, base_dir, false);
use typedialog_core::nickel::NickelCli;
let extension = defaults_path.extension().and_then(|s| s.to_str());
let is_ncl = extension == Some("ncl");
let is_toml = extension == Some("toml");
let defaults_json: std::collections::HashMap<String, serde_json::Value> = if is_ncl {
// Convert .ncl to JSON using nickel export
NickelCli::verify()?;
let value = NickelCli::export(&defaults_path)?;
match value {
serde_json::Value::Object(map) => {
// Extract fields from elements (which includes fragments after expand_includes)
let form_fields: Vec<form_parser::FieldDefinition> = form
.elements
.iter()
.filter_map(|elem| match elem {
form_parser::FormElement::Field(f) => Some(f.clone()),
_ => None,
})
.collect();
// Extract Nickel structure AND flatten everything (combine both approaches)
let mut combined = extract_nickel_defaults(&map, &form_fields);
let flattened = flatten_json_object(&map);
// Flattened values fill in gaps not covered by extraction
for (k, v) in flattened {
combined.entry(k).or_insert(v);
}
combined
}
_ => {
return Err(Error::validation_failed(
"Defaults .ncl must export to a JSON object".to_string(),
))
}
}
// Use helper function to load .ncl defaults
load_nickel_defaults(&defaults_path, &form.elements, false)?
} else if is_toml {
// Read TOML and convert to JSON
let defaults_content = fs::read_to_string(&defaults_path).map_err(|e| {
@ -552,7 +574,14 @@ async fn nickel_roundtrip_cmd(
form = form_parser::expand_includes(form, form_base_dir)
.map_err(|e| Error::validation_failed(format!("Failed to expand includes: {}", e)))?;
// Step 3: Execute form with Web backend (interactive HTTP server)
// Step 3: Load default values from input .ncl if exists
let initial_values = if input.exists() {
Some(load_nickel_defaults(&input, &form.elements, verbose)?)
} else {
None
};
// Step 4: Execute form with Web backend (same path as normal execute_form)
if verbose {
eprintln!("[roundtrip] Starting HTTP server for interactive form");
}
@ -563,9 +592,16 @@ async fn nickel_roundtrip_cmd(
println!("Starting interactive form on http://localhost:{}", port);
println!("Complete the form and submit to continue...\n");
let form_results =
form_parser::execute_with_backend_two_phase(form, backend.as_mut(), None, form_base_dir)
.await?;
// USE THE SAME EXECUTION PATH AS NORMAL WEB BACKEND
// This respects display_mode and calls execute_form_complete() when display_mode = "complete"
let form_results = form_parser::execute_with_backend_i18n_with_defaults(
form,
backend.as_mut(),
None,
form_base_dir,
initial_values,
)
.await?;
if verbose {
eprintln!(