datalab-cli 0.1.0

A powerful CLI for converting, extracting, and processing documents using the Datalab API
Documentation
use crate::client::DatalabClient;
use crate::error::{DatalabError, Result};
use crate::output::Progress;
use clap::{Args, Subcommand};
use serde_json::json;
use std::fs;
use std::path::PathBuf;

#[derive(Subcommand, Debug)]
pub enum WorkflowsCommand {
    /// Create a new workflow
    Create(CreateArgs),
    /// List all workflows
    List(ListArgs),
    /// Get workflow details
    Get(GetArgs),
    /// Execute a workflow
    Execute(ExecuteArgs),
    /// Get execution status
    Execution(ExecutionArgs),
    /// Delete a workflow
    Delete(DeleteArgs),
    /// List available step types
    StepTypes(StepTypesArgs),
}

#[derive(Args, Debug)]
pub struct CreateArgs {
    /// Workflow name
    #[arg(long, value_name = "NAME")]
    pub name: String,

    /// JSON file containing workflow steps
    #[arg(long, value_name = "FILE")]
    pub steps: PathBuf,

    /// Team ID for ownership
    #[arg(long, value_name = "ID")]
    pub team_id: Option<i64>,

    /// Request timeout in seconds
    #[arg(long, default_value = "60", value_name = "SECS")]
    pub timeout: u64,
}

#[derive(Args, Debug)]
pub struct ListArgs {
    /// Request timeout in seconds
    #[arg(long, default_value = "60", value_name = "SECS")]
    pub timeout: u64,
}

#[derive(Args, Debug)]
pub struct GetArgs {
    /// Workflow ID
    #[arg(value_name = "WORKFLOW_ID")]
    pub workflow_id: String,

    /// Request timeout in seconds
    #[arg(long, default_value = "60", value_name = "SECS")]
    pub timeout: u64,
}

#[derive(Args, Debug)]
pub struct ExecuteArgs {
    /// Workflow ID
    #[arg(value_name = "WORKFLOW_ID")]
    pub workflow_id: String,

    /// JSON file containing input configuration
    #[arg(long, value_name = "FILE")]
    pub input: PathBuf,

    /// Request timeout in seconds
    #[arg(long, default_value = "300", value_name = "SECS")]
    pub timeout: u64,
}

#[derive(Args, Debug)]
pub struct ExecutionArgs {
    /// Execution ID
    #[arg(value_name = "EXECUTION_ID")]
    pub execution_id: String,

    /// Request timeout in seconds
    #[arg(long, default_value = "60", value_name = "SECS")]
    pub timeout: u64,
}

#[derive(Args, Debug)]
pub struct DeleteArgs {
    /// Workflow ID
    #[arg(value_name = "WORKFLOW_ID")]
    pub workflow_id: String,

    /// Request timeout in seconds
    #[arg(long, default_value = "60", value_name = "SECS")]
    pub timeout: u64,
}

#[derive(Args, Debug)]
pub struct StepTypesArgs {
    /// Request timeout in seconds
    #[arg(long, default_value = "60", value_name = "SECS")]
    pub timeout: u64,
}

pub async fn execute(cmd: WorkflowsCommand, progress: &Progress) -> Result<()> {
    match cmd {
        WorkflowsCommand::Create(args) => create(args, progress).await,
        WorkflowsCommand::List(args) => list(args, progress).await,
        WorkflowsCommand::Get(args) => get(args, progress).await,
        WorkflowsCommand::Execute(args) => execute_workflow(args, progress).await,
        WorkflowsCommand::Execution(args) => execution(args, progress).await,
        WorkflowsCommand::Delete(args) => delete(args, progress).await,
        WorkflowsCommand::StepTypes(args) => step_types(args, progress).await,
    }
}

async fn create(args: CreateArgs, progress: &Progress) -> Result<()> {
    let client = DatalabClient::new(Some(args.timeout))?;

    if !args.steps.exists() {
        return Err(DatalabError::FileNotFound(args.steps.clone()));
    }

    progress.start("create-workflow", Some(&args.name));

    let steps_content = fs::read_to_string(&args.steps)?;
    let steps: serde_json::Value = serde_json::from_str(&steps_content)
        .map_err(|e| DatalabError::InvalidInput(format!("Invalid JSON in steps file: {}", e)))?;

    let mut body = json!({
        "name": args.name,
        "steps": steps,
    });

    if let Some(team_id) = args.team_id {
        body["team_id"] = json!(team_id);
    }

    let response = client.post_json("workflows/workflows", &body).await?;

    println!("{}", serde_json::to_string_pretty(&response)?);

    Ok(())
}

async fn list(args: ListArgs, progress: &Progress) -> Result<()> {
    let client = DatalabClient::new(Some(args.timeout))?;

    progress.start("list-workflows", None);

    let response = client.get("workflows/workflows").await?;

    println!("{}", serde_json::to_string_pretty(&response)?);

    Ok(())
}

async fn get(args: GetArgs, progress: &Progress) -> Result<()> {
    let client = DatalabClient::new(Some(args.timeout))?;

    progress.start("get-workflow", Some(&args.workflow_id));

    let path = format!("workflows/workflows/{}", args.workflow_id);
    let response = client.get(&path).await?;

    println!("{}", serde_json::to_string_pretty(&response)?);

    Ok(())
}

async fn execute_workflow(args: ExecuteArgs, progress: &Progress) -> Result<()> {
    let client = DatalabClient::new(Some(args.timeout))?;

    if !args.input.exists() {
        return Err(DatalabError::FileNotFound(args.input.clone()));
    }

    progress.start("execute-workflow", Some(&args.workflow_id));

    let input_content = fs::read_to_string(&args.input)?;
    let input_config: serde_json::Value = serde_json::from_str(&input_content)
        .map_err(|e| DatalabError::InvalidInput(format!("Invalid JSON in input file: {}", e)))?;

    let body = json!({
        "input_config": input_config,
    });

    let path = format!("workflows/workflows/{}/execute", args.workflow_id);
    let response = client.post_json(&path, &body).await?;

    println!("{}", serde_json::to_string_pretty(&response)?);

    Ok(())
}

async fn execution(args: ExecutionArgs, progress: &Progress) -> Result<()> {
    let client = DatalabClient::new(Some(args.timeout))?;

    progress.start("get-execution", Some(&args.execution_id));

    let path = format!("workflows/executions/{}", args.execution_id);
    let response = client.get(&path).await?;

    println!("{}", serde_json::to_string_pretty(&response)?);

    Ok(())
}

async fn delete(args: DeleteArgs, progress: &Progress) -> Result<()> {
    let client = DatalabClient::new(Some(args.timeout))?;

    progress.start("delete-workflow", Some(&args.workflow_id));

    let path = format!("workflows/workflows/{}", args.workflow_id);
    client.delete(&path).await?;

    println!(
        "{}",
        serde_json::to_string_pretty(&json!({
            "deleted": true,
            "workflow_id": args.workflow_id,
        }))?
    );

    Ok(())
}

async fn step_types(args: StepTypesArgs, progress: &Progress) -> Result<()> {
    let client = DatalabClient::new(Some(args.timeout))?;

    progress.start("list-step-types", None);

    let response = client.get("workflows/step-types").await?;

    println!("{}", serde_json::to_string_pretty(&response)?);

    Ok(())
}