Functional Weave
Code in Rust

todo.group-by-due@1.1.0

impl/rust/group_by_due_with_order.rs

2,266 bytes · the Rust implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::dates_day_of_week::day_of_week;  ← from dates.day-of-week ^1.0.0 · built alongside by fune
use super::todo_due_status::due_status;  ← from todo.due-status ^1.0.0 · built alongside by fune
use super::todo_item::Todo;  ← from todo.item ^1.0.0 · built alongside by fune
use super::todo_item_validate_todo::{todos_from_value, todos_to_value};

const SECTIONS: [(&str, &str); 7] = [
    ("overdue", "Overdue"),
    ("today", "Today"),
    ("tomorrow", "Tomorrow"),
    ("this-week", "This week"),
    ("later", "Later"),
    ("none", "No date"),
    ("done", "Done"),
];

/// Todos in planner sections, fixed order, empty sections left out. With
/// order "due" the sections spanning several dates are sorted by due date,
/// stably; with "input" every section keeps the caller's order.
///
/// # Panics
/// Panics on an unknown order, or an impossible or malformed date.
pub fn group_by_due_with_order(todos: &[Todo], today: &str, order: &str) -> Vec<DueGroup> {
    if order != "due" && order != "input" {
        panic!("order must be \"due\" or \"input\", found \"{}\"", order);
    }
    day_of_week(today); // a bad today is an error even for an empty list
    let statuses: Vec<String> = todos.iter().map(|todo| due_status(todo, today)).collect();
    let mut groups = Vec::new();
    for (status, title) in SECTIONS.iter() {
        let mut members: Vec<Todo> = todos
            .iter()
            .zip(statuses.iter())
            .filter(|(_, s)| s.as_str() == *status)
            .map(|(todo, _)| todo.clone())
            .collect();
        if members.is_empty() {
            continue;
        }
        if order == "due" && matches!(*status, "overdue" | "this-week" | "later") {
            // sort_by is stable, so equal dates keep input order.
            members.sort_by(|a, b| a.due.cmp(&b.due));
        }
        groups.push(DueGroup { status: status.to_string(), title: title.to_string(), todos: members });
    }
    groups
}

pub fn due_group_to_value(group: &DueGroup) -> Value {
    Value::obj(vec![
        ("status", Value::str(&group.status)),
        ("title", Value::str(&group.title)),
        ("todos", todos_to_value(&group.todos)),
    ])
}

pub fn fune_vector(args: &[Value]) -> Value {
    let groups = group_by_due_with_order(&todos_from_value(&args[0]), args[1].as_str(), args[2].as_str());
    Value::Arr(groups.iter().map(due_group_to_value).collect())
}