1#![forbid(unsafe_code)]
2#![deny(missing_docs)]
3use std::{ffi::OsString, fmt};
30
31mod args;
32mod boot;
33mod codec_boot;
34mod crates_io;
35mod envelope;
36mod exit;
37#[cfg(feature = "registry")]
38mod git_registry;
39mod handoff;
40mod introspect;
41mod load;
42mod receipt;
43mod source;
44
45#[cfg(test)]
46mod codec_boot_tests;
47#[cfg(test)]
48mod handoff_tests;
49#[cfg(test)]
50mod introspect_tests;
51#[cfg(test)]
52mod load_tests;
53#[cfg(test)]
54mod publish_tests;
55#[cfg(test)]
56mod scenario_tests;
57
58pub use args::{CliCommand, parse_args};
59pub use boot::{CliBoot, CliEnvelope, Payload};
60pub use codec_boot::{DEFAULT_CODEC_NAME, boot_codec_name, codec_lib_symbol};
61pub use crates_io::{CratesIoResolver, CratesIoSpec, ResolvedCratesIoSource, VersionReq};
62#[cfg(feature = "registry")]
63pub use git_registry::{GIT_REGISTRY_ENDPOINT_ENV, GitRegistryResolver};
64pub use handoff::{CLI_MAIN_ENTRYPOINT, CliEntrypoint, cli_main_entrypoint_symbol};
65pub use load::LoadSession;
66pub use receipt::{LoadReceipt, LoadReceiptRole};
67pub use source::LibSourceSpec;
68
69const HELP: &str = "\
70Usage: sim [OPTIONS] [PAYLOAD...]
71
72Options:
73 --help Print this help text.
74 --version Print the binary version.
75 --codec NAME Select the boot codec name.
76 --load SRC Add a library source to load.
77 --native-audio-provider SRC
78 Try a native audio provider source and degrade if absent.
79 --list Request a loaded-lib list.
80 --inspect SYMBOL Request inspection of a loaded lib or export.
81 --eval TEXT Carry eval text for loaded-lib handoff.
82 --script PATH Carry a script path for loaded-lib handoff.
83 --stdin TEXT Carry stdin text for loaded-lib handoff.
84
85Note: this is a pre-publish bootloader frame. It bakes in no codec. By
86default it fetches nothing over the network and boots only libraries provided
87via --load (an artifact source) or already present in the local cache. A build
88with the registry feature can fetch from an explicit git registry endpoint installed
89by the host. With no source it reports `no codec '<name>' available`.
90";
91
92#[derive(Debug, Clone, PartialEq, Eq)]
94pub struct CliError {
95 message: String,
96}
97
98impl CliError {
99 pub fn new(message: impl Into<String>) -> Self {
101 Self {
102 message: message.into(),
103 }
104 }
105
106 pub(crate) fn unsupported(arg: &str) -> Self {
107 Self::new(format!("unsupported argument: {arg}"))
108 }
109
110 pub(crate) fn missing_value(flag: &str) -> Self {
111 Self::new(format!("{flag} requires a value"))
112 }
113
114 pub(crate) fn duplicate(flag: &str) -> Self {
115 Self::new(format!("{flag} was provided more than once"))
116 }
117}
118
119impl fmt::Display for CliError {
120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121 f.write_str(&self.message)
122 }
123}
124
125impl std::error::Error for CliError {}
126
127pub fn version_line() -> String {
129 format!("sim {}\n", env!("CARGO_PKG_VERSION"))
130}
131
132pub fn run<I, S>(args: I) -> Result<i32, CliError>
142where
143 I: IntoIterator<Item = S>,
144 S: Into<OsString>,
145{
146 let mut session = LoadSession::new();
147 run_with_session(args, &mut session)
148}
149
150pub fn run_with_session<I, S>(args: I, session: &mut LoadSession) -> Result<i32, CliError>
152where
153 I: IntoIterator<Item = S>,
154 S: Into<OsString>,
155{
156 run_command_with_session(parse_args(args)?, session)
157}
158
159pub fn run_command_with_session(
161 command: CliCommand,
162 session: &mut LoadSession,
163) -> Result<i32, CliError> {
164 match command {
165 CliCommand::Help => {
166 print!("{HELP}");
167 Ok(0)
168 }
169 CliCommand::Version => {
170 print!("{}", version_line());
171 Ok(0)
172 }
173 CliCommand::Boot(boot) => session.run_loaded_boot(&boot),
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 #[test]
182 fn version_line_uses_package_version() {
183 assert_eq!(version_line(), "sim 0.1.0\n");
184 }
185
186 #[test]
187 fn direct_payload_enters_loaded_boot() {
188 let err = run(["sim", "run"]).unwrap_err();
189 assert!(err.to_string().starts_with("no codec 'lisp' available"));
190 }
191}