1use std::{
2 error::Error,
3 process::Command,
4 sync::{
5 atomic::{AtomicBool, Ordering},
6 Arc,
7 },
8};
9
10use clap::Args;
11
12use crate::{
13 cmd::run,
14 config::read_and_validate_config,
15 net::kill_port,
16 puppeteer::{is_puppeteer_chrome_installed, prompt_install_puppeteer_chrome},
17};
18
19#[derive(Args, Debug, Clone)]
21pub struct ExportOptions {
22 #[arg(long, default_value_t = get_default_output())]
24 pub output: String,
25 #[arg(long, default_value_t = 3000)]
27 pub port: u16,
28}
29
30fn get_default_output() -> String {
31 let default = String::from("slides.pdf");
32
33 let Ok(config) = read_and_validate_config() else {
34 println!("Warning: Could not read oseda-config.json");
35 println!("Using slides.pdf as name instead");
36 return default;
37 };
38
39 format!("{}.pdf", config.title.trim())
40}
41
42pub fn export(opts: ExportOptions) -> Result<(), Box<dyn Error>> {
44 println!("Cleaning any existing oseda processing...");
45 if kill_port(opts.port).is_err() {
46 eprintln!("Warning, could not kill value on desired port")
47 }
48
49 let output = Command::new("npm")
50 .args(["install", "decktape@3.15.0"])
51 .current_dir(".")
52 .output()?;
53
54 if !is_puppeteer_chrome_installed() {
56 prompt_install_puppeteer_chrome()?;
57 }
58 println!("Puppeteer Chrome is installed, continuing...");
59
60 if !output.status.success() {
61 eprintln!(
62 "Decktape installation failure: {}",
63 String::from_utf8_lossy(&output.stderr)
64 );
65 return Err("npm init failed".into());
66 }
67
68 let shutdown_flag = Arc::new(AtomicBool::new(false));
71 let run_flag = shutdown_flag.clone();
72
73 let run_handle = std::thread::spawn(move || run::run_with_shutdown(run_flag));
74
75 std::thread::sleep(std::time::Duration::from_millis(10000));
77
78 let addr = format!("http://localhost:{}", opts.port);
79
80 let export_output = Command::new("npm")
82 .args(["exec", "decktape", "reveal", &addr, &opts.output])
83 .output()?;
84
85 shutdown_flag.store(true, Ordering::SeqCst);
87 let _ = run_handle.join();
89
90 if !export_output.status.success() {
91 eprintln!(
92 "Decktape PDF export failure: {}",
93 String::from_utf8_lossy(&export_output.stderr)
94 );
95 return Err("npm init failed".into());
96 }
97
98 Ok(())
99}