| Crates.io | starbase |
| lib.rs | starbase |
| version | 0.10.6 |
| created_at | 2023-04-06 00:57:23.750599+00 |
| updated_at | 2025-09-06 18:15:45.281267+00 |
| description | Framework for building performant command line applications and developer tools. |
| homepage | |
| repository | https://github.com/moonrepo/starbase |
| max_upload_size | |
| id | 831818 |
| size | 59,064 |
Application framework for building performant command line applications and developer tools.
An application uses a session based approach, where a session object contains data required for the entire application lifecycle.
Create an App, optionally setup diagnostics (miette) and tracing (tracing), and then run the
application with the provided session. A mutable session is required, as the session can be mutated
for each phase.
use starbase::{App, MainResult};
use std::process::ExitCode;
use crate::CustomSession;
#[tokio::main]
async fn main() -> MainResult {
let app = App::default();
app.setup_diagnostics();
let exit_code = app.run(CustomSession::default(), |session| async {
// Run CLI
Ok(None)
}).await?;
Ok(ExitCode::from(exit_code))
}
A session must implement the AppSession trait. This trait provides 4 optional methods, each
representing a different phase in the application life cycle.
use starbase::{AppSession, AppResult};
use std::path::PathBuf;
use async_trait::async_trait;
#[derive(Clone)]
pub struct CustomSession {
pub workspace_root: PathBuf,
}
#[async_trait]
impl AppSession for CustomSession {
async fn startup(&mut self) -> AppResult {
self.workspace_root = detect_workspace_root()?;
Ok(None)
}
}
Sessions must be cloneable and be
Send + Synccompatible. We clone the session when spawning tokio tasks. If you want to persist data across threads, wrap session properties inArc,RwLock, and other mechanisms.
An application is divided into phases, where each phase will be processed and completed before moving onto the next phase. The following phases are available:
App#run).
If a session implements the
AppSession#executetrait method, it will run in parallel with theApp#runmethod.
Errors and diagnostics are provided by the miette crate. All
layers of the application return the miette::Result type (via AppResult). This allows for errors
to be easily converted to diagnostics, and for miette to automatically render to the terminal for
errors and panics.
To benefit from this, update your main function to return MainResult.
use starbase::{App, MainResult};
#[tokio::main]
async fn main() -> MainResult {
let app = App::default();
app.setup_diagnostics();
app.setup_tracing_with_defaults();
// ...
Ok(())
}
To make the most out of errors, and in turn diagnostics, it's best (also suggested) to use the
thiserror crate.
use miette::Diagnostic;
use thiserror::Error;
#[derive(Debug, Diagnostic, Error)]
pub enum AppError {
#[error(transparent)]
#[diagnostic(code(app::io_error))]
IoError(#[from] std::io::Error),
#[error("Systems offline!")]
#[diagnostic(code(app::bad_code))]
SystemsOffline,
}
A returned Err must be converted to a diagnostic first. There are 2 approaches to achieve this:
#[system]
async fn could_fail() {
// Convert error using into()
Err(AppError::SystemsOffline.into())
// OR use ? operator on Err()
Err(AppError::SystemsOffline)?
}