mod html;
pub use html::{CellExport, generate_html};
use std::collections::HashMap;
use std::fs;
use std::path::Path;
use std::time::Instant;
use venus_core::graph::CellId;
use crate::colors;
use crate::executor::NotebookExecutor;
use crate::output::decoder::try_decode_value;
pub fn execute(
notebook_path: &str,
output_path: Option<&str>,
release: bool,
dark_theme: bool,
) -> anyhow::Result<()> {
let start = Instant::now();
let executor = NotebookExecutor::new(notebook_path, release)?;
println!(
"\n{}Venus Export{} - {}{}{}",
colors::BOLD,
colors::RESET,
colors::CYAN,
executor.notebook_name(),
colors::RESET
);
println!("{}", "─".repeat(50));
println!("\n{}Compiling cells...{}", colors::BOLD, colors::RESET);
let compilation = executor.compile()?;
println!("\n{}Executing cells...{}", colors::BOLD, colors::RESET);
let mut cell_exports: HashMap<CellId, CellExport> = HashMap::new();
for cell in &executor.cells {
let real_id = executor.cell_ids[&cell.name];
let error = compilation
.errors
.iter()
.find(|(name, _)| name == &cell.name)
.map(|(_, errs)| {
errs.iter()
.map(|e| e.rendered.clone().unwrap_or_else(|| e.format_terminal()))
.collect::<Vec<_>>()
.join("\n")
});
cell_exports.insert(
real_id,
CellExport {
name: cell.name.clone(),
description: cell.doc_comment.clone(),
source: cell.source_code.clone(),
return_type: cell.return_type.clone(),
dependencies: cell
.dependencies
.iter()
.map(|d| d.param_name.clone())
.collect(),
output: None,
error,
execution_time_ms: None,
},
);
}
if compilation.errors.is_empty() {
let execution = executor.execute_silent(&compilation, None)?;
for &cell_id in &execution.executed_cells {
if let Some(cell) = executor.cell_by_id(cell_id)
&& let Some(output) = execution.outputs.get(&cell_id)
&& let Some(export) = cell_exports.get_mut(&cell_id)
{
let output_text = output
.display_text()
.map(|s| s.to_string())
.or_else(|| try_decode_value(&cell.return_type, output.bytes()));
export.output = output_text;
export.execution_time_ms = Some(
execution.execution_time.as_millis() as u64
/ execution.executed_cells.len() as u64,
);
}
}
println!(
"{} ✓ Executed {} cells{} ({:.1}ms)",
colors::GREEN,
execution.executed_cells.len(),
colors::RESET,
execution.execution_time.as_secs_f64() * 1000.0
);
} else {
println!(
"{} ⚠ Skipping execution ({} compilation errors){}",
colors::YELLOW,
compilation.errors.len(),
colors::RESET
);
}
println!("\n{}Generating HTML...{}", colors::BOLD, colors::RESET);
let ordered_exports: Vec<CellExport> = executor
.order
.iter()
.filter_map(|id| cell_exports.remove(id))
.collect();
let html = generate_html(&executor.notebook_name(), &ordered_exports, dark_theme);
let path = Path::new(notebook_path);
let notebook_name = path
.file_stem()
.unwrap_or_default()
.to_string_lossy()
.to_string();
let output_file = output_path
.map(|p| p.to_string())
.unwrap_or_else(|| format!("{}.html", notebook_name));
fs::write(&output_file, html)?;
let total_time = start.elapsed();
println!("{}", "─".repeat(50));
println!(
"{}Exported{} to {}{}{}",
colors::GREEN,
colors::RESET,
colors::CYAN,
output_file,
colors::RESET
);
println!("Total time: {:.2}s", total_time.as_secs_f64());
Ok(())
}