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 net::kill_port,
15 puppeteer::{is_puppeteer_chrome_installed, prompt_install_puppeteer_chrome},
16};
17
18#[derive(Args, Debug, Clone)]
20pub struct ExportOptions {
21 #[arg(long, default_value = "slides.pdf")]
23 pub output: String,
24 #[arg(long, default_value_t = 3000)]
26 pub port: u16,
27}
28
29pub fn export(opts: ExportOptions) -> Result<(), Box<dyn Error>> {
31 println!("Cleaning any existing oseda processing...");
32 if kill_port(opts.port).is_err() {
33 eprintln!("Warning, could not kill value on desired port")
34 }
35
36 let output = Command::new("npm")
37 .args(["install", "decktape@3.15.0"])
38 .current_dir(".")
39 .output()?;
40
41 if !is_puppeteer_chrome_installed() {
43 prompt_install_puppeteer_chrome()?;
44 }
45 println!("Puppeteer Chrome is installed, continuing...");
46
47 if !output.status.success() {
48 eprintln!(
49 "Decktape installation failure: {}",
50 String::from_utf8_lossy(&output.stderr)
51 );
52 return Err("npm init failed".into());
53 }
54
55 let shutdown_flag = Arc::new(AtomicBool::new(false));
58 let run_flag = shutdown_flag.clone();
59
60 let run_handle = std::thread::spawn(move || run::run_with_shutdown(run_flag));
61
62 std::thread::sleep(std::time::Duration::from_millis(10000));
64
65 let addr = format!("http://localhost:{}", opts.port);
66
67 let export_output = Command::new("npm")
69 .args(["exec", "decktape", "reveal", &addr, &opts.output])
70 .output()?;
71
72 shutdown_flag.store(true, Ordering::SeqCst);
74 let _ = run_handle.join();
76
77 if !export_output.status.success() {
78 eprintln!(
79 "Decktape PDF export failure: {}",
80 String::from_utf8_lossy(&export_output.stderr)
81 );
82 return Err("npm init failed".into());
83 }
84
85 Ok(())
86}