Functional Weave
Code in Rust

todo.filter@1.0.0

impl/rust.rs

2,576 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::collections_search_text::search_text;  ← from collections.search-text ^1.0.0 · built alongside by fune
use super::todo_item::{todo_from_value, todos_to_value, Todo};  ← from todo.item ^1.0.0 · built alongside by fune
use super::todo_item_validate_draft::trim_space;
use super::todo_normalise_tags::normalise_tags;  ← from todo.normalise-tags ^1.0.0 · built alongside by fune

/// The todos a list shows for its view, tag and search box, in input order.
///
/// The tag is normalised like stored tags, so "#Home" finds "home"; one that
/// normalises to nothing matches no todo. The search covers the title, the
/// notes and the tags joined with spaces (collections.search-text).
///
/// # Panics
/// Panics on an unknown view.
pub fn filter_todos(todos: &[Todo], filter: &TodoFilter) -> Vec<Todo> {
    let view = filter.view.as_str();
    if view != "all" && view != "active" && view != "completed" {
        panic!("unknown view \"{}\": expected all, active or completed", view);
    }
    let wanted: Option<String> = match &filter.tag {
        None => None,
        Some(tag) => match normalise_tags(&[tag.clone()]).into_iter().next() {
            None => return Vec::new(),
            Some(t) => Some(t),
        },
    };
    let kept: Vec<&Todo> = todos
        .iter()
        .filter(|t| !(view == "active" && t.done) && !(view == "completed" && !t.done))
        .filter(|t| wanted.as_ref().map_or(true, |w| t.tags.contains(w)))
        .collect();
    let query = trim_space(&filter.query);
    if query.is_empty() {
        return kept.into_iter().cloned().collect();
    }
    let records: Vec<Value> = kept
        .iter()
        .enumerate()
        .map(|(i, t)| {
            Value::obj(vec![
                ("i", Value::Int(i as i64)),
                ("title", Value::str(&t.title)),
                ("notes", t.notes.as_deref().map(Value::str).unwrap_or(Value::Null)),
                ("tags", Value::Str(t.tags.join(" "))),
            ])
        })
        .collect();
    let fields = vec!["title".to_string(), "notes".to_string(), "tags".to_string()];
    search_text(&records, &fields, query)
        .iter()
        .map(|r| kept[r.get("i").as_i64() as usize].clone())
        .collect()
}

pub fn todo_filter_from_value(v: &Value) -> TodoFilter {
    TodoFilter {
        view: v.get("view").as_str().to_string(),
        tag: if v.get("tag").is_null() { None } else { Some(v.get("tag").as_str().to_string()) },
        query: v.get("query").as_str().to_string(),
    }
}

pub fn fune_vector(args: &[Value]) -> Value {
    let todos: Vec<Todo> = args[0].as_arr().iter().map(todo_from_value).collect();
    todos_to_value(&filter_todos(&todos, &todo_filter_from_value(&args[1])))
}