ito-core 0.1.33

Core functionality and business logic for Ito
Documentation
use super::TaskItem;

pub(super) fn parse_checkbox_tasks(contents: &str) -> Vec<TaskItem> {
    let mut tasks: Vec<TaskItem> = Vec::new();
    for line in contents.lines() {
        let l = line.trim_start();
        let bytes = l.as_bytes();
        if bytes.len() < 6 {
            continue;
        }
        let bullet = bytes[0] as char;
        if bullet != '-' && bullet != '*' {
            continue;
        }
        if bytes[1] != b' ' || bytes[2] != b'[' || bytes[4] != b']' || bytes[5] != b' ' {
            continue;
        }

        let marker = bytes[3] as char;
        let (done, rest, status) = match marker {
            'x' | 'X' => (true, &l[6..], None),
            ' ' => (false, &l[6..], None),
            '~' | '>' => (false, &l[6..], Some("in-progress".to_string())),
            _ => continue,
        };
        tasks.push(TaskItem {
            id: (tasks.len() + 1).to_string(),
            description: rest.trim().to_string(),
            done,
            status,
        });
    }
    tasks
}

pub(super) fn looks_like_enhanced_tasks(contents: &str) -> bool {
    for line in contents.lines() {
        let l = line.trim_start();
        if l.starts_with("### Task ") {
            return true;
        }
    }
    false
}

pub(super) fn parse_enhanced_tasks(contents: &str) -> Vec<TaskItem> {
    let mut tasks: Vec<TaskItem> = Vec::new();
    let mut current_id: Option<String> = None;
    let mut current_desc: Option<String> = None;
    let mut current_done = false;
    let mut current_status: Option<String> = None;

    fn push_current(
        tasks: &mut Vec<TaskItem>,
        current_id: &mut Option<String>,
        current_desc: &mut Option<String>,
        current_done: &mut bool,
        current_status: &mut Option<String>,
    ) {
        let Some(desc) = current_desc.take() else {
            current_id.take();
            *current_done = false;
            *current_status = None;
            return;
        };
        let id = current_id
            .take()
            .filter(|s| !s.trim().is_empty())
            .unwrap_or_else(|| (tasks.len() + 1).to_string());
        tasks.push(TaskItem {
            id,
            description: desc,
            done: *current_done,
            status: current_status.take(),
        });
        *current_done = false;
    }

    for line in contents.lines() {
        let l = line.trim_start();

        if let Some(rest) = l.strip_prefix("### Task ") {
            push_current(
                &mut tasks,
                &mut current_id,
                &mut current_desc,
                &mut current_done,
                &mut current_status,
            );

            let (id, desc) = rest.split_once(':').unwrap_or((rest, ""));
            let id = id.trim();
            let desc = if desc.trim().is_empty() {
                rest.trim()
            } else {
                desc.trim()
            };

            current_id = Some(id.to_string());
            current_desc = Some(desc.to_string());
            current_done = false;
            current_status = Some("pending".to_string());
            continue;
        }

        if let Some(rest) = l.strip_prefix("- **Status**:") {
            let status = rest.trim();
            if let Some(status) = status
                .strip_prefix("[x]")
                .or_else(|| status.strip_prefix("[X]"))
            {
                current_done = true;
                current_status = Some(status.trim().to_string());
                continue;
            }
            if let Some(status) = status
                .strip_prefix("[>]")
                .or_else(|| status.strip_prefix("[~]"))
            {
                current_done = false;
                current_status = Some(status.trim().to_string());
                continue;
            }
            if let Some(status) = status.strip_prefix("[ ]") {
                current_done = false;
                current_status = Some(status.trim().to_string());
                continue;
            }
            if let Some(status) = status.strip_prefix("[-]") {
                current_done = false;
                current_status = Some(status.trim().to_string());
                continue;
            }
        }
    }

    push_current(
        &mut tasks,
        &mut current_id,
        &mut current_desc,
        &mut current_done,
        &mut current_status,
    );

    tasks
}

#[cfg(test)]
#[path = "task_parsing_tests.rs"]
mod task_parsing_tests;