1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# TBL
**T**erminal **B**ar (time)**L**ine (**WIP**)
`cargo run --example datetime`

## Example
```rust
use tbl::Renderer;
let data = vec![(0., 2.), (3., 4.)];
let rendered = Renderer::new(data.as_slice(), &|&e| e, &|_| None::<String>) // explicit type for Option<Label>
.with_length(42)
.render()
.unwrap();
assert_eq!(rendered, "===================== ===========");
```
## Custom Data and Renderer
```rust
use tbl::{Block, BlockRenderer, RenderBlock, Renderer, TBLError, Bound};
struct CustomData {
bounds: (usize, usize),
label: String // must be Clone + Debug
}
fn bounds(cd: &CustomData)-> Bound {
let (a, b) = cd.bounds;
(a as f64, b as f64)
}
fn label(cd: &CustomData)-> Option<String> {
Some(cd.label.clone())
}
struct CustomRenderer {}
impl BlockRenderer<String> for CustomRenderer {
fn render(&self, b: &Block<String>) -> RenderBlock {
match b {
Block::Space(length) => RenderBlock::Space("\u{2606}".repeat(*length)),
Block::Segment(length, label) => {
let mut truncated = label.clone().unwrap_or_default();
truncated.truncate(*length);
RenderBlock::Block(format!(
"{}{}",
truncated,
"\u{2605}".repeat(*length - truncated.len())
))
}
}
}
}
let data = vec![CustomData{bounds: (0, 2), label: "hello".to_string()}, CustomData{bounds: (3, 4), label: "world!".to_string()}];
let rendered = Renderer::new(data.as_slice(), &bounds, &label)
.with_length(60)
.with_renderer(&CustomRenderer {})
.render().unwrap();
assert_eq!(rendered, "hello★★★★★★★★★★★★★★★★★★★★★★★★★☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆world!★★★★★★★★★");
```
See [examples](examples) folder for more examples.
## TODO
- [x] ~~support or~~ forbid joint intervals e.g. `[(0,2), (1,3)]`
- [x] prepare for release on crate.io
- [x] add doc
- [x] add test
- [ ] split `build_blocks` into several parts