Skip to main content

oseda_cli/cmd/
export.rs

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::{cmd::run, net::kill_port};
13
14/// Options struct for the export subcommand
15#[derive(Args, Debug, Clone)]
16pub struct ExportOptions {
17    /// String name of the output PDF file
18    #[arg(long, default_value = "slides.pdf")]
19    pub output: String,
20    /// Port the project runs on
21    #[arg(long, default_value_t = 3000)]
22    pub port: u16,
23}
24
25/// Export the current Oseda project to a PDF file via `decktape`
26pub fn export(opts: ExportOptions) -> Result<(), Box<dyn Error>> {
27    if kill_port(opts.port).is_err() {
28        eprintln!("Warning, could not kill value on desired port")
29    }
30
31    let output = Command::new("npm")
32        .args(["install", "decktape@3.15.0"])
33        .current_dir(".")
34        .output()?;
35
36    if !output.status.success() {
37        eprintln!(
38            "Decktape installation failure: {}",
39            String::from_utf8_lossy(&output.stderr)
40        );
41        return Err("npm init failed".into());
42    }
43
44    // decktape automatic http://localhost:3000/ Desktop/IntroToRust/slides.pdf
45
46    let shutdown_flag = Arc::new(AtomicBool::new(false));
47    let run_flag = shutdown_flag.clone();
48
49    let run_handle = std::thread::spawn(move || run::run_with_shutdown(run_flag));
50
51    // wait a moment for the localhost server to spin up
52    std::thread::sleep(std::time::Duration::from_millis(10000));
53
54    let addr = format!("http://localhost:{}", opts.port);
55
56    // run decktape, assuming the server has spun up by now
57    let export_output = Command::new("decktape")
58        .args(["automatic", &addr, &opts.output])
59        .output()?;
60
61    // send shutdown flag, should signal to run_with_shutdown to kill the process
62    shutdown_flag.store(true, Ordering::SeqCst);
63    // wait to run to terminate (hopefully gracefully) and join the process to cur. thread
64    let _ = run_handle.join();
65
66    if !export_output.status.success() {
67        eprintln!(
68            "Decktape PDF export failure: {}",
69            String::from_utf8_lossy(&export_output.stderr)
70        );
71        return Err("npm init failed".into());
72    }
73
74    Ok(())
75}