use crate::command::{Command, Response};
use exfiltrate_internal::args::{ArgKind, ArgSpec, ParsedArgs};
pub(crate) struct BuildInfoCommand;
static ARGS: &[ArgSpec] = &[ArgSpec::flag(
"json",
"return MessagePack-encoded BuildInfo instead of text",
ArgKind::Bool,
)];
impl Command for BuildInfoCommand {
fn name(&self) -> &'static str {
"build_info"
}
fn short_description(&self) -> &'static str {
"Reports crate versions, target triple, profile, and enabled features. Use this first to learn what you are attached to."
}
fn full_description(&self) -> &'static str {
"Reports what this program is and what it was built from.
Includes the host program's name and version, the linked `exfiltrate` version, the
target triple, the cargo profile, the enabled `exfiltrate` features, and — when the
build recorded them — a git revision and a build timestamp.
This is the same payload the connection handshake exchanges, so `exfiltrate status`
shows it too without a second round trip."
}
fn args(&self) -> &'static [ArgSpec] {
ARGS
}
fn execute(&self, args: Vec<String>) -> Result<Response, Response> {
let parsed = ParsedArgs::parse(self.args(), args).map_err(Response::String)?;
let info = crate::config_snapshot().build_info();
if parsed.boolean("json") {
Response::from_serialize(&info)
} else {
Ok(info.to_string().into())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use exfiltrate_internal::build_info::BuildInfo;
#[test]
fn the_text_form_names_the_target_and_the_crate_version() {
let text = BuildInfoCommand.execute(Vec::new()).unwrap().into_string();
assert!(text.contains(env!("CARGO_PKG_VERSION")), "{text}");
assert!(text.contains(env!("EXFILTRATE_TARGET")), "{text}");
assert!(text.contains("protocol version"), "{text}");
}
#[test]
fn the_json_form_decodes_back_into_build_info() {
let Response::Bytes(bytes) = BuildInfoCommand
.execute(vec!["--json".to_string()])
.unwrap()
else {
panic!("--json must answer with Bytes");
};
let info: BuildInfo = rmp_serde::from_slice(&bytes).unwrap();
assert_eq!(info.exfiltrate_version, env!("CARGO_PKG_VERSION"));
assert!(info.is_compatible());
}
#[test]
fn an_unknown_flag_is_refused_rather_than_ignored() {
let error = BuildInfoCommand
.execute(vec!["--nope".to_string()])
.unwrap_err()
.to_string();
assert!(error.contains("--nope"), "{error}");
}
}