nb-fabric 0.3.0

CLI for managing Microsoft Fabric notebooks; create, edit cells, execute interactively, schedule, and query OneLake data
// #region Imports
use anyhow::{Context, Result};
use base64::Engine;
use base64::engine::general_purpose::STANDARD as BASE64;
use reqwest::Client;
use std::path::Path;

use crate::client;
// #endregion

// #region Functions

/// Handle `nb export <workspace/notebook> -o <path>` command.
/// Exports a notebook definition to a local .ipynb file.
///
/// Inputs:
///   - reference: "workspace/notebook" format
///   - output: file path to write the .ipynb to
pub async fn run_export(http: &Client, reference: &str, output: &str) -> Result<()> {
    let (ws_name, nb_name) = client::parse_ref(reference)?;
    let ws_id = client::resolve_workspace(http, ws_name).await?;
    let nb = client::resolve_item(http, &ws_id, nb_name, "Notebook").await?;

    println!("  Exporting '{}'...", nb.display_name);

    let def = client::get_definition(http, &ws_id, &nb.id, "ipynb").await?;

    let parts = def
        .definition
        .context("No definition returned")?
        .parts;

    // Find the notebook-content.ipynb part
    let part = parts
        .iter()
        .find(|p| p.path.contains("notebook-content") || p.path.ends_with(".ipynb"))
        .or_else(|| parts.first())
        .context("No parts in definition")?;

    let decoded = BASE64
        .decode(&part.payload)
        .context("Failed to decode base64 payload")?;

    // Pretty-print the JSON before writing
    let json_val: serde_json::Value = serde_json::from_slice(&decoded)
        .unwrap_or_else(|_| serde_json::Value::String(String::from_utf8_lossy(&decoded).into()));
    let pretty = serde_json::to_string_pretty(&json_val)?;

    let out_path = Path::new(output);
    if let Some(parent) = out_path.parent() {
        if !parent.exists() {
            std::fs::create_dir_all(parent)?;
        }
    }

    std::fs::write(out_path, pretty.as_bytes())
        .context("Failed to write output file")?;

    println!("  Exported to {}", output);
    println!("  Size       {} bytes", pretty.len());

    Ok(())
}

// #endregion