zernel/commands/
notebook.rs1use anyhow::{Context, Result};
6use clap::Subcommand;
7use std::process::Command;
8
9#[derive(Subcommand)]
10pub enum NotebookCommands {
11 Start {
13 #[arg(long, default_value = "8888")]
15 port: u16,
16 #[arg(long)]
18 no_browser: bool,
19 },
20 Open {
22 path: String,
24 },
25 Convert {
27 input: String,
29 #[arg(long, default_value = "py")]
31 to: String,
32 },
33 List,
35 Stop,
37}
38
39pub async fn run(cmd: NotebookCommands) -> Result<()> {
40 match cmd {
41 NotebookCommands::Start { port, no_browser } => {
42 println!("Starting Jupyter Lab on port {port}...");
43
44 let mut args = vec![
45 "lab".to_string(),
46 format!("--port={port}"),
47 "--ip=0.0.0.0".into(),
48 ];
49 if no_browser {
50 args.push("--no-browser".into());
51 }
52
53 let status = tokio::process::Command::new("jupyter")
54 .args(&args)
55 .status()
56 .await
57 .with_context(|| "jupyter not found — install with: zernel install jupyter")?;
58
59 if !status.success() {
60 anyhow::bail!("Jupyter exited with code {}", status.code().unwrap_or(-1));
61 }
62 }
63
64 NotebookCommands::Open { path } => {
65 println!("Opening {path}...");
66 let status = tokio::process::Command::new("jupyter")
67 .args(["lab", &path])
68 .status()
69 .await
70 .with_context(|| "jupyter not found")?;
71 let _ = status;
72 }
73
74 NotebookCommands::Convert { input, to } => {
75 let output = input.replace(".ipynb", &format!(".{to}"));
76 println!("Converting {input} → {output}");
77
78 let to_format = match to.as_str() {
79 "py" => "script",
80 "html" => "html",
81 "pdf" => "pdf",
82 "md" => "markdown",
83 other => other,
84 };
85
86 let status = Command::new("jupyter")
87 .args(["nbconvert", "--to", to_format, &input])
88 .status()
89 .with_context(|| "jupyter nbconvert not found")?;
90
91 if status.success() {
92 println!("Converted: {output}");
93 } else {
94 println!("Conversion failed.");
95 }
96 }
97
98 NotebookCommands::List => {
99 println!("Running Jupyter Servers:");
100 let output = Command::new("jupyter")
101 .args(["lab", "list"])
102 .output()
103 .with_context(|| "jupyter not found")?;
104 print!("{}", String::from_utf8_lossy(&output.stdout));
105 }
106
107 NotebookCommands::Stop => {
108 println!("Stopping Jupyter servers...");
109 let status = Command::new("jupyter")
110 .args(["lab", "stop"])
111 .status()
112 .with_context(|| "jupyter not found")?;
113 if status.success() {
114 println!("All servers stopped.");
115 }
116 }
117 }
118 Ok(())
119}