Functional Weave
Code in Rust

todo.due-label

The short English label shown next to a todo: "2 days overdue", "Due today", "Due tomorrow", "Due Fri 2 Oct".

1.0.0 · published 2026-10-03 by charlie · Anterra

Pinned by 21 tests, run in TypeScript, Python and Rust.

What it does

The short label an app shows next to a todo, worked out from todo.due-status so a todo labelled overdue is also in the Overdue section and counted as overdue in the summary.

| Status | Label | | --- | --- | | done, or no due date | `""` (show nothing) | | overdue | `"1 day overdue"`, `"12 days overdue"` | | today | `"Due today"` | | tomorrow | `"Due tomorrow"` | | this week or later | `"Due Fri 2 Oct"`; `"Due Fri 1 Jan 2027"` when the year differs from today's |

For example

  • due_label(id a, title Task a, notes —, done true, priority none, due 2026-09-20, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at 2026-09-27T18:00:00Z, order 0, 2026-09-28) → a done todo shows nothing, even when its date has passed
  • due_label(id a, title Task a, notes —, done true, priority none, due —, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at 2026-09-27T18:00:00Z, order 0, 2026-09-28) → a done todo with no date shows nothing
  • due_label(id a, title Task a, notes —, done false, priority none, due —, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28) → no due date shows nothing

The function

The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.

pub fn due_label(todo: &Todo, today: &str) -> String
todoTodo
todaydatethe user's local date, from the app; never the clock
returnsstringempty for a done todo or one with no due date

Your code names it in one line, in the file that uses it

fune!(todo.due-label@^1);  // then call due_label(…)
impl/rust.rs · 49 lines · open · 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_add_days::parse_iso_date;  ← from dates.add-days ^1.0.0 · built alongside by fune
use super::dates_day_of_week::day_name;  ← from dates.day-of-week ^1.0.0 · built alongside by fune
use super::dates_days_between::days_between;  ← from dates.days-between ^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_value, Todo};  ← from todo.item ^1.0.0 · built alongside by fune

const MONTHS: [&str; 12] = [
    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
];

/// The short English label for a todo's due date, built on due_status so the
/// label, the section and the overdue count never disagree.
///
/// # Panics
/// Panics on an impossible or malformed date.
pub fn due_label(todo: &Todo, today: &str) -> String {
    let status = due_status(todo, today);
    let due = match (status.as_str(), &todo.due) {
        ("done", _) | ("none", _) | (_, None) => return String::new(),
        (_, Some(d)) => d.as_str(),
    };
    match status.as_str() {
        "overdue" => {
            let late = days_between(due, today);
            if late == 1 {
                "1 day overdue".to_string()
            } else {
                format!("{} days overdue", late)
            }
        }
        "today" => "Due today".to_string(),
        "tomorrow" => "Due tomorrow".to_string(),
        _ => {
            let d = parse_iso_date(due);
            let label = format!("Due {} {} {}", &day_name(due)[0..3], d.day, MONTHS[(d.month - 1) as usize]);
            // The year only when it differs, so next January never reads as this one.
            if d.year == parse_iso_date(today).year {
                label
            } else {
                format!("{} {}", label, d.year)
            }
        }
    }
}

pub fn fune_vector(args: &[Value]) -> Value {
    Value::str(&due_label(&todo_from_value(&args[0]), args[1].as_str()))
}

Install

fune build

With that line in your source, in a Rust project (language rust in fune.project), fune build resolves it and its 5 dependencies, pins them in fune.lock, downloads only the Rust package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. A crate’s build.rs runs it before every compile. Or pin a range in fune.project and build in one step:

fune add todo.due-label
Download for Rust todo.due-label-1.0.0-rust.fune · 12,000 bytes sha256 97536432045a05c1196246011d3bc9ba79ca9278b158052ed4ae124792eb6d8d

The manifest, vectors and README with only the Rust implementation. Install it without the registry with fune add ./todo.due-label-1.0.0-rust.fune, or fetch it from a terminal with fune pull todo.due-label@1.0.0:rust.

The whole function, every language, is one file too: todo.due-label-1.0.0.fune, 14,494 bytes, sha256 8aefefe87feab04119b7d82cf60a2bc4f421a0265722e14f34271dd4a0d7e21e. It installs into a project of any language.

Customise it in your app

The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.

before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.

// fune: before todo.due-label

after — your function gets the result and the arguments, and returns the final result.

// fune: after todo.due-label

replace — inside this capability’s code only, calls to a dependency go to your function, with the same signature. Other capabilities that use it are unaffected; write in * to replace it everywhere.

// fune: replace dates.add-days in todo.due-label
// fune: replace dates.day-of-week in todo.due-label
// fune: replace dates.days-between in todo.due-label
// fune: replace todo.due-status in todo.due-label
// fune: replace todo.item in todo.due-label

step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show todo.due-label --steps.

// fune: step todo.due-label after <n|label>

Tests

A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.

CaseArgumentsExpected
a done todo shows nothing, even when its date has passed id a, title Task a, notes —, done true, priority none, due 2026-09-20, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at 2026-09-27T18:00:00Z, order 0, 2026-09-28 →
a done todo with no date shows nothing id a, title Task a, notes —, done true, priority none, due —, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at 2026-09-27T18:00:00Z, order 0, 2026-09-28 →
no due date shows nothing id a, title Task a, notes —, done false, priority none, due —, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 →
one day late is singular id a, title Task a, notes —, done false, priority none, due 2026-09-27, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 → 1 day overdue
ten days late id a, title Task a, notes —, done false, priority none, due 2026-09-18, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 → 10 days overdue
due last New Year's Eve is 271 days overdue id a, title Task a, notes —, done false, priority none, due 2025-12-31, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 → 271 days overdue
due today id a, title Task a, notes —, done false, priority none, due 2026-09-28, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 → Due today
due tomorrow id a, title Task a, notes —, done false, priority none, due 2026-09-29, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 → Due tomorrow
Friday this week, seen on Monday id a, title Task a, notes —, done false, priority none, due 2026-10-02, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 → Due Fri 2 Oct
Sunday is still this week id a, title Task a, notes —, done false, priority none, due 2026-10-04, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 → Due Sun 4 Oct
Show the other 11 tests
CaseArgumentsExpected
next Monday is later, same format id a, title Task a, notes —, done false, priority none, due 2026-10-05, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 → Due Mon 5 Oct
the day has no leading zero id a, title Task a, notes —, done false, priority none, due 2026-11-09, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 → Due Mon 9 Nov
a date next year carries the year id a, title Task a, notes —, done false, priority none, due 2027-01-01, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 → Due Fri 1 Jan 2027
on Thursday 31 December, Sunday 3 January is this week and carries the year id a, title Task a, notes —, done false, priority none, due 2027-01-03, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-12-31 → Due Sun 3 Jan 2027
on New Year's Day, New Year's Eve is 1 day overdue id a, title Task a, notes —, done false, priority none, due 2026-12-31, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2027-01-01 → 1 day overdue
on Saturday, Sunday is tomorrow id a, title Task a, notes —, done false, priority none, due 2026-10-04, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-10-03 → Due tomorrow
on Saturday, Monday is later id a, title Task a, notes —, done false, priority none, due 2026-10-05, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-10-03 → Due Mon 5 Oct
today in the wrong format id a, title Task a, notes —, done false, priority none, due 2026-10-02, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 28/09/2026 → error: is not an ISO date
today with a trailing newline id a, title Task a, notes —, done false, priority none, due 2026-10-02, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 → error: is not an ISO date
a due date that never existed id a, title Task a, notes —, done false, priority none, due 2026-02-30, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 → error: is not a real calendar date
a bad today is an error even on a done todo id a, title Task a, notes —, done true, priority none, due —, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at 2026-09-27T18:00:00Z, order 0, 2026-9-28 → error: is not an ISO date

More from the author

- **English only.** Three-letter English weekday and month names, the day without a leading zero. A localised app should not translate these strings: map the `DueStatus` from todo.due-status itself and format the date with its own locale data. - The overdue count is whole calendar days (dates.days-between), since due dates have no time of day. There is no "yesterday": one day late is `"1 day overdue"`. - The year is added whenever the due date is in a different calendar year from today, in either direction, so a date next January is never mistaken for this January. - `today` is passed in by the app. Malformed or impossible dates are errors, with dates.add-days's messages, even on a done todo.

Files

PathBytes
README.md1,205
impl/python.py1,089
impl/rust.rs1,741
impl/typescript.ts1,211
vectors.json6,442