Functional Weave
Code in TypeScript

todo.list@1.1.0

impl/rust/add_todo.rs

2,628 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::todo_item::{validate_draft, Todo, TodoDraft};  ← from todo.item ^1.0.0 · built alongside by fune
use super::todo_item_validate_draft::{todo_draft_from_value, trim_space};
use super::todo_item_validate_todo::{is_utc_timestamp, todos_from_value, todos_to_value};
use super::todo_normalise_tags::normalise_tags;  ← from todo.normalise-tags ^1.0.0 · built alongside by fune

/// Refuse a clock reading that would not compare as a string with the stored ones.
pub fn require_utc_now(now: &str) {
    if !is_utc_timestamp(now) {
        panic!("now must be a UTC timestamp like 2026-09-28T09:30:00Z");
    }
}

/// The draft as it will be stored: title and notes trimmed (ASCII only),
/// blank notes as None, tags in normal form. Then checked with validate_draft;
/// the first field in field order that fails is the error.
pub fn checked_draft(draft: &TodoDraft) -> TodoDraft {
    let notes = draft.notes.as_deref().map(trim_space).unwrap_or("");
    let clean = TodoDraft {
        title: trim_space(&draft.title).to_string(),
        notes: if notes.is_empty() { None } else { Some(notes.to_string()) },
        priority: draft.priority.clone(),
        due: draft.due.clone(),
        tags: normalise_tags(&draft.tags),
        recurrence: draft.recurrence.clone(),
    };
    let verdict = validate_draft(&clean);
    if let Some((field, message)) = verdict.errors.first() {
        panic!("invalid todo: {}: {}", field, message);
    }
    clean
}

/// Add a todo from the add form: open, created now, last in manual order,
/// appended. Caller mistakes (id, clock) are reported before form mistakes.
///
/// # Panics
/// On a blank or duplicate id, a `now` that is not a UTC timestamp, or an
/// invalid draft.
pub fn add_todo(todos: &[Todo], draft: &TodoDraft, id: &str, now: &str) -> Vec<Todo> {
    if trim_space(id).is_empty() {
        panic!("a todo needs an id");
    }
    if todos.iter().any(|t| t.id == id) {
        panic!("a todo with id \"{}\" already exists", id);
    }
    require_utc_now(now);
    let clean = checked_draft(draft);
    let order = todos.iter().map(|t| t.order + 1).fold(0, i64::max);
    let mut out = todos.to_vec();
    out.push(Todo {
        id: id.to_string(),
        title: clean.title,
        notes: clean.notes,
        done: false,
        priority: clean.priority,
        due: clean.due,
        tags: clean.tags,
        recurrence: clean.recurrence,
        created_at: now.to_string(),
        completed_at: None,
        order,
    });
    out
}

pub fn fune_vector(args: &[Value]) -> Value {
    let todos = todos_from_value(&args[0]);
    todos_to_value(&add_todo(&todos, &todo_draft_from_value(&args[1]), args[2].as_str(), args[3].as_str()))
}