notion-cli-tool 0.1.0

A fast and simple Notion CLI written in Rust
use colored::Colorize;

pub fn extract_title(item: &serde_json::Value) -> String {
    if let Some(props) = item.get("properties") {
        if let Some(title_prop) = props.get("title").or(props.get("Name")) {
            if let Some(title_arr) = title_prop.get("title").and_then(|t| t.as_array()) {
                if let Some(first) = title_arr.first() {
                    if let Some(text) = first.get("plain_text").and_then(|t| t.as_str()) {
                        return text.to_string();
                    }
                }
            }
        }
    }

    if let Some(title_arr) = item.get("title").and_then(|t| t.as_array()) {
        if let Some(first) = title_arr.first() {
            if let Some(text) = first.get("plain_text").and_then(|t| t.as_str()) {
                return text.to_string();
            }
        }
    }

    "(Untitled)".to_string()
}

pub fn extract_property_value(prop: &serde_json::Value) -> Option<String> {
    if let Some(rich_text) = prop.get("rich_text").and_then(|r| r.as_array()) {
        let text: String = rich_text
            .iter()
            .filter_map(|rt| rt.get("plain_text").and_then(|t| t.as_str()))
            .collect();
        if !text.is_empty() {
            return Some(text);
        }
    }

    if let Some(select) = prop.get("select") {
        if let Some(name) = select.get("name").and_then(|n| n.as_str()) {
            return Some(name.to_string());
        }
    }

    if let Some(multi_select) = prop.get("multi_select").and_then(|m| m.as_array()) {
        let values: Vec<&str> = multi_select
            .iter()
            .filter_map(|s| s.get("name").and_then(|n| n.as_str()))
            .collect();
        if !values.is_empty() {
            return Some(values.join(", "));
        }
    }

    if let Some(number) = prop.get("number") {
        if let Some(n) = number.as_f64() {
            return Some(n.to_string());
        }
    }

    if let Some(checkbox) = prop.get("checkbox").and_then(|c| c.as_bool()) {
        return Some(if checkbox { "" } else { "" }.to_string());
    }

    if let Some(date) = prop.get("date") {
        if let Some(start) = date.get("start").and_then(|s| s.as_str()) {
            return Some(start.to_string());
        }
    }

    if let Some(url) = prop.get("url").and_then(|u| u.as_str()) {
        return Some(url.to_string());
    }

    None
}

pub fn print_block(block: &serde_json::Value) {
    let block_type = block
        .get("type")
        .and_then(|t| t.as_str())
        .unwrap_or("unknown");

    match block_type {
        "paragraph" => {
            if let Some(text) = extract_rich_text(block, "paragraph") {
                println!("{}", text);
            }
        }
        "heading_1" => {
            if let Some(text) = extract_rich_text(block, "heading_1") {
                println!("\n{}", format!("# {}", text).bold());
            }
        }
        "heading_2" => {
            if let Some(text) = extract_rich_text(block, "heading_2") {
                println!("\n{}", format!("## {}", text).bold());
            }
        }
        "heading_3" => {
            if let Some(text) = extract_rich_text(block, "heading_3") {
                println!("\n{}", format!("### {}", text).bold());
            }
        }
        "bulleted_list_item" => {
            if let Some(text) = extract_rich_text(block, "bulleted_list_item") {
                println!("{}", text);
            }
        }
        "numbered_list_item" => {
            if let Some(text) = extract_rich_text(block, "numbered_list_item") {
                println!("  1. {}", text);
            }
        }
        "code" => {
            if let Some(text) = extract_rich_text(block, "code") {
                println!("```\n{}\n```", text.dimmed());
            }
        }
        "divider" => {
            println!("{}", "---".dimmed());
        }
        _ => {}
    }
}

pub fn extract_rich_text(block: &serde_json::Value, block_type: &str) -> Option<String> {
    let rich_text = block.get(block_type)?.get("rich_text")?.as_array()?;
    let text: String = rich_text
        .iter()
        .filter_map(|rt| rt.get("plain_text").and_then(|t| t.as_str()))
        .collect();

    if text.is_empty() {
        None
    } else {
        Some(text)
    }
}