Skip to main content

ll/
lib.rs

1/*!
2# ll — structured task-tree instrumentation
3
4**ll** instruments async and sync Rust code by wrapping sections into
5hierarchical [`Task`]s. Each task emits `start`/`end` events consumed by
6pluggable [`reporters::Reporter`]s (text loggers, in-memory capture, live
7terminal status).
8
9Tasks form a tree: every task can spawn children, and reporters see the full
10parent-child structure.
11
12- [Quick start](#quick-start)
13- [The `#[task]` macro](#the-task-macro)
14  - [Spawn variants](#spawn-variants)
15  - [Optional attributes](#optional-attributes) — `data(...)`, `tags(...)`, `name = "..."`
16  - [Examples](#examples)
17- [Manual spawning](#manual-spawning)
18- [Tags](#tags) — `#l0`–`#l3`, `#nostatus`, `#dontprint`
19
20## Quick start
21
22```ignore
23use ll::{task, Task};
24use anyhow::Result;
25
26#[task]
27async fn build(task: &Task) -> Result<()> {
28    task.data("compiler", "rustc");
29    compile(&task).await?;
30    Ok(())
31}
32
33#[task]
34async fn compile(task: &Task) -> Result<()> {
35    // task tree: build > compile
36    Ok(())
37}
38
39async fn run() -> Result<()> {
40    // Set up terminal reporters (from the ll_stdio crate):
41    // ll_stdio::init();
42    let root = Task::create_new("root");
43    build(&root).await
44}
45```
46
47## The `#[task]` macro
48
49The [`macro@task`] attribute macro eliminates spawn boilerplate. It wraps your
50function body in the appropriate `task.spawn*()` call, using the function
51name as the task name and shadowing the parent task parameter with the
52child task.
53
54### Spawn variants
55
56| Attribute | Spawn method | Function signature |
57|---|---|---|
58| `#[task]` | [`Task::spawn`] | `async fn` |
59| `#[task(sync)]` | [`Task::spawn_sync`] | `fn` |
60
61### Optional attributes
62
63- **`data(arg1, arg2, ...)`** — auto-emit `task.data("arg", arg)` for the
64  listed function parameters.
65- **`tags(l2, nostatus, ...)`** — append `#`-tags to the task name. Tags
66  control reporter visibility: `#l2`/`#l3` mute at lower log levels,
67  `#nostatus` hides from the terminal status display.
68- **`name = "custom"`** — override the task name (defaults to the function
69  name). Can be combined with `tags(...)`.
70
71Attributes combine freely: `#[task(sync, data(path), tags(l2))]`.
72
73### Examples
74
75Async task (most common):
76
77```ignore
78#[task]
79async fn fetch(url: &str, task: &Task) -> Result<String> {
80    task.data("url", url);
81    Ok(reqwest::get(url).await?.text().await?)
82}
83
84// caller:
85let body = fetch("https://example.com", &parent).await?;
86```
87
88Sync task with automatic data logging:
89
90```ignore
91#[task(sync, data(path))]
92fn read_config(path: &str, task: &Task) -> Result<Config> {
93    // task.data("path", path) is emitted automatically
94    Ok(toml::from_str(&std::fs::read_to_string(path)?)?)
95}
96```
97
98Muting verbose tasks with tags:
99
100```ignore
101#[task(tags(l2))]
102async fn verbose_step(task: &Task) -> Result<()> {
103    // only shown when reporter log level is L2 or higher
104    Ok(())
105}
106```
107
108Nested tasks build the tree automatically:
109
110```ignore
111#[task]
112async fn deploy(task: &Task) -> Result<()> {
113    provision(&task).await?;  // task tree: deploy > provision
114    restart(&task).await?;    // task tree: deploy > restart
115    Ok(())
116}
117```
118
119## Manual spawning
120
121You can also spawn tasks without the macro:
122
123```ignore
124let root = Task::create_new("root");
125root.spawn("subtask", |task| async move {
126    task.spawn_sync("child", |task| {
127        Ok(())
128    })?;
129    Ok(())
130}).await?;
131```
132
133## Tags
134
135Tags are metadata encoded inline in task names via `#` syntax. They control
136reporter visibility and filtering.
137
138| Tag | Effect |
139|---|---|
140| `#l0` | Reporter level L0 — highest priority, always shown |
141| `#l1` | Reporter level L1 — default |
142| `#l2` | Reporter level L2 — hidden unless reporter threshold is L2+ |
143| `#l3` | Reporter level L3 — lowest priority, most filtered |
144| `#nostatus` | Hidden from the live terminal status display (still in text logs) |
145| `#dontprint` | Suppressed from all text reporters |
146
147Tags can be set via the [`macro@task`] attribute (`tags(l2, nostatus)`) or
148embedded directly in task names (`"download #l3"`). Unrecognized tags are
149stored but have no built-in effect.
150
151Data keys also support tags: `task.data("response_body #trace", val)` marks
152the entry as trace-level, hiding it unless the data log level is set to Trace.
153The `#info` and `#debug` tags also work for data-level filtering.
154 */
155#![allow(clippy::new_without_default)]
156
157pub mod data;
158pub mod level;
159pub mod task;
160pub mod task_tree;
161pub mod uniq_id;
162pub mod utils;
163
164pub use ll_macros::task;
165pub use task::Task;
166
167pub mod reporters;
168pub use task_tree::add_reporter;
169
170pub use data::{Data, DataEntry, DataValue};
171pub use task_tree::ErrorFormatter;
172pub use task_tree::TaskInternal;
173pub use task_tree::TaskTree;