Expand description
§Base concepts
Note: using ChatGPT to translate to English(About 70% of all?).
§Extensions
Each type of extension is defined by a relatively simple trait.
| Name | Description |
|---|---|
| Source | In terms of type, it is a Stream<Item = Item>. It is a data source. |
| Generator | It is similar to Source, but it takes an input and generates an arbitrary number of Items from it. |
| Filter | It takes one Item (also called Context) along with an input from the user, and applies a predicate to decide whether to keep it. |
| Sorter | It takes two Items and an input, and compares the Items with each other. |
| UI | It takes input from the user, processes it and then displays it on the screen. |
| Action | It takes the selected Item and executes the Action. |
§Diagram
§Making the first launcher
Up to now, we have briefly explained the different types of extensions, but from here, we’ll create a simple Launcher as a tutorial. For a practical configuration, please refer to the author’s settings.
On a side note: Even though it’s called a Launcher, it can be used for purposes other than launching, so the name might be somewhat misleading.
§Create project
Create a binary crate with cargo. And add ltrait and tokio as a dependency.
cargo new hello-ltrait
cd hello-ltrait
cargo add ltrait
cargo add tokio --features=fullConfigure error handler and logger:
use ltrait::color_eyre::Result;
use ltrait::{Launcher, Level};
#[tokio::main]
async fn main() -> Result<()> {
// keeping _guard is required to write log
let _guard = ltrait::setup(Level::INFO)?;
// TODO: Configure and run Launcher
Ok(())
}§Create a launcher and set UI
I introduce the concept of Cushion.
Cushion is a type, and it is recommended to implement it using an enum.
Although it is not impossible to create a Launcher without Cushion, it is not recommended due to the significant limitations it imposes.
Transform the Items extracted from the Source or Generator into a Cushion. Then, from the Cushion, transform it into the Context for each Sorter, Filter, etc.
I recommend you to use ltrait-ui-tui as your first ui. Add as a dependency.
cargo add ltrait-ui-tuiAnd write main.rs.
use ltrait::color_eyre::Result;
use ltrait::{Launcher, Level};
use ltrait_ui_tui::{Tui, TuiConfig, TuiEntry, style::Style, Viewport};
enum Item {
// TODO: add source
}
impl Into<String> for &Item {
fn into(self) -> String {
match self {
// TODO:
_ => "unknown item".into()
}
}
}
#[tokio::main]
async fn main() -> Result<()> {
// keeping _guard is required to write log
let _guard = ltrait::setup(Level::INFO)?;
let launcher = Launcher::default()
.set_ui(
Tui::new(TuiConfig::new(
Viewport::Fullscreen,
'>', // Selected
' ',
ltrait_ui_tui::sample_keyconfig,
)),
|c: &Item| TuiEntry {
text: (c.into(), Style::new()),
},
);
launcher.run().await
}Since there is not even a single Source or Generator here, running it should result in just an input field. Let’s try running it.
cargo run§Add a source, filter, sorter
The simplest source can be created by crate::source::from_iter.
You can add a source by crate::launcher::Launcher::add_source.
If you only want to see even numbers, this can easily be achieved using crate::filter::ClosureFilter and crate::launcher::Launcher::add_raw_filter.
And if you want to see the scene in order, you can use crate::sorter::ClosureSorter and crate::launcher::Launcher::add_raw_sorter.
Performance can be optimised by setting the crate::launcher::Launcher::batch_size.
Note: add_raw_** works like a syntax sugar. add_raw_**(/* ... */) will like
add_**(/* ... */, |c| c). The behaviour changes a little due to lifetime and other factors, so it is recommended to use raw if a raw version can be used.
use ltrait::color_eyre::Result;
use ltrait::{
Launcher,
Level,
filter::ClosureFilter,
sorter::ClosureSorter,
};
use ltrait_ui_tui::{Tui, TuiConfig, TuiEntry, style::Style, Viewport};
use std::cmp;
enum Item {
Num(u32)
}
impl Into<String> for &Item {
fn into(self) -> String {
match self {
Item::Num(x) => format!("{x}"),
_ => "unknown item".into()
}
}
}
#[tokio::main]
async fn main() -> Result<()> {
// keeping _guard is required to write log
let _guard = ltrait::setup(Level::INFO)?;
let launcher = Launcher::default()
// the simplest source
.add_source(ltrait::source::from_iter(1..=5000), /* transformer */ Item::Num)
.add_raw_filter(ClosureFilter::new(|c, _ /* input */| {
match c {
Item::Num(x) => (x % 2) == 0,
_ => true, // If variants are added to Item in the future, they are ignored here
}
}))
.reverse_sorter(false)
.add_raw_sorter(ClosureSorter::new(|lhs, rhs, _| {
match (lhs, rhs) {
(Item::Num(lhs), Item::Num(rhs)) => lhs.cmp(rhs),
_ => cmp::Ordering::Equal
}
}))
.batch_size(500)
.set_ui(
Tui::new(TuiConfig::new(
Viewport::Fullscreen,
'>', // Selected
' ',
)),
|c| TuiEntry {
text: (c.into(), Style::new()),
},
);
launcher.run().await
}Let’s run it again.
cargo run🎉 It’s complete! 🎉
Re-exports§
pub use crate::action::Action;pub use crate::filter::Filter;pub use crate::generator::Generator;pub use crate::launcher::Launcher;pub use crate::sorter::Sorter;pub use crate::source::Source;pub use crate::ui::UI;pub use async_trait;pub use color_eyre;pub use tokio_stream;
Modules§
Structs§
- Level
- Describes the level of verbosity of a span or event.
Functions§
- setup
- Install color_eyre and setup tracing(with tracing-log)