Skip to main content

ready_set/
lib.rs

1//! ready-set core: dispatcher + built-ins.
2//!
3//! Most consumers want the `ready-set` binary, not this library. The library
4//! is exposed so integration tests can drive the dispatcher in-process.
5//!
6//! The dispatcher↔plugin contracts the core implements are versioned and
7//! live under
8//! [`docs/contracts/`](https://github.com/pulsearc-ai/ready-set/tree/main/docs/contracts):
9//! [`env-vars.md`](https://github.com/pulsearc-ai/ready-set/blob/main/docs/contracts/env-vars.md)
10//! (the `READY_SET_*` env surface),
11//! [`capabilities.md`](https://github.com/pulsearc-ai/ready-set/blob/main/docs/contracts/capabilities.md)
12//! (descriptor and report shapes),
13//! [`describe.md`](https://github.com/pulsearc-ai/ready-set/blob/main/docs/contracts/describe.md)
14//! and
15//! [`manifest.md`](https://github.com/pulsearc-ai/ready-set/blob/main/docs/contracts/manifest.md)
16//! (plugin metadata discovery),
17//! [`exit-codes.md`](https://github.com/pulsearc-ai/ready-set/blob/main/docs/contracts/exit-codes.md)
18//! (process exit code semantics),
19//! [`cache.md`](https://github.com/pulsearc-ai/ready-set/blob/main/docs/contracts/cache.md)
20//! (the `--list` cache).
21
22#![forbid(unsafe_code)]
23#![warn(missing_docs)]
24
25pub mod builtins;
26pub mod cache;
27pub mod capabilities;
28pub mod cli;
29pub mod discovery;
30pub mod env;
31pub mod exec;
32pub mod lifecycle;
33pub mod metadata;
34pub mod project;
35
36use std::ffi::OsString;
37use std::path::PathBuf;
38
39use ready_set_sdk::context::{ColorMode, LogLevel};
40use ready_set_sdk::{ExitCode, OutputMode};
41
42/// Entry point shared by `main.rs` and integration tests.
43pub fn run(argv: impl IntoIterator<Item = OsString>) -> ExitCode {
44    let parsed = cli::parse(argv.into_iter());
45    let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
46    match parsed {
47        cli::ParsedArgs::Help => {
48            let contract = build_contract(&cli::GlobalFlags::default(), &cwd);
49            builtins::help::run(&[], &contract)
50        },
51        cli::ParsedArgs::Version => {
52            let contract = build_contract(&cli::GlobalFlags::default(), &cwd);
53            builtins::version::run(&[], &contract)
54        },
55        cli::ParsedArgs::List { all, globals } => {
56            let contract = build_contract(&globals, &cwd);
57            let forwarded: Vec<OsString> = if all {
58                vec![OsString::from("--all")]
59            } else {
60                Vec::new()
61            };
62            builtins::list::run(&forwarded, &contract)
63        },
64        cli::ParsedArgs::Empty { globals } => {
65            let contract = build_contract(&globals, &cwd);
66            builtins::ready::run(&[], &contract)
67        },
68        cli::ParsedArgs::Subcommand {
69            name,
70            args,
71            globals,
72        } => {
73            let contract = build_contract(&globals, &cwd);
74            if let Some(handler) = builtins::route(&name) {
75                return handler(&args, &contract);
76            }
77            // Fall through to plugin discovery.
78            let Some(entry) = discovery::find_plugin(&name) else {
79                eprintln!(
80                    "ready-set: unknown subcommand `{name}`\n\
81                     hint: search crates.io for `ready-set-{name}` to install one"
82                );
83                return ExitCode::UnknownSubcommand;
84            };
85            exec::dispatch_to_plugin(&entry, &args, &contract)
86        },
87    }
88}
89
90fn build_contract(globals: &cli::GlobalFlags, cwd: &std::path::Path) -> env::EnvContract {
91    let project_root = project::detect_project_root(cwd);
92    let config_path = project_root
93        .as_deref()
94        .map(|root| root.join(".ready-set.toml"))
95        .filter(|p| p.is_file());
96
97    env::EnvContract {
98        dispatcher_version: env!("CARGO_PKG_VERSION")
99            .parse()
100            .unwrap_or_else(|_| semver::Version::new(0, 0, 0)),
101        project_root,
102        config_path,
103        output: globals.output.unwrap_or(OutputMode::Human),
104        log: globals.log.unwrap_or(LogLevel::Normal),
105        color: globals.color.unwrap_or(ColorMode::Auto),
106    }
107}