use crate::identity::Identity;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CommandLine {
identity: Identity,
flattened_commands: Vec<&'static str>,
command_path: Vec<&'static str>,
}
impl CommandLine {
#[must_use]
pub const fn identity(&self) -> Identity {
self.identity
}
#[must_use]
pub fn flattened_commands(&self) -> &[&'static str] {
&self.flattened_commands
}
#[must_use]
pub fn command_path(&self) -> &[&'static str] {
&self.command_path
}
#[must_use]
pub fn cargo_command(&self) -> String {
let mut command = format!("cargo {}", self.identity.binary_name());
for name in &self.command_path {
command.push(' ');
command.push_str(name);
}
command
}
#[doc(hidden)]
#[must_use]
pub fn from_dispatch(
identity: Identity,
flattened_commands: impl IntoIterator<Item = &'static str>,
command_path: impl IntoIterator<Item = &'static str>,
) -> Self {
let command_path: Vec<&'static str> = command_path.into_iter().collect();
assert!(
!command_path.is_empty(),
"a task always runs under at least one subcommand name"
);
Self {
identity,
flattened_commands: flattened_commands.into_iter().collect(),
command_path,
}
}
}
#[cfg(test)]
mod tests {
use super::CommandLine;
use crate::identity::Identity;
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
#[test]
fn command_line_is_send_and_sync() {
assert_send::<CommandLine>();
assert_sync::<CommandLine>();
}
fn an_identity(binary_name: &'static str) -> Identity {
Identity::from_macro_expansion("a-package", binary_name, "1.2.3")
}
#[test]
fn each_field_reads_back_what_from_dispatch_was_given() {
let identity = an_identity("a-binary");
let command_line =
CommandLine::from_dispatch(identity, ["add", "build"], ["tools", "sync"]);
assert_eq!(command_line.identity(), identity);
assert_eq!(command_line.flattened_commands(), ["add", "build"]);
assert_eq!(command_line.command_path(), ["tools", "sync"]);
}
#[test]
fn flattened_commands_is_empty_when_nothing_was_given() {
let command_line = CommandLine::from_dispatch(an_identity("a-binary"), [], ["sync"]);
assert!(command_line.flattened_commands().is_empty());
}
#[test]
fn cargo_command_names_the_bin_then_every_level_of_the_path() {
let command_line =
CommandLine::from_dispatch(an_identity("acme"), [], ["tools", "db", "sync"]);
assert_eq!(command_line.cargo_command(), "cargo acme tools db sync");
}
#[test]
fn cargo_command_for_a_flattened_child_is_the_bin_then_its_name() {
let command_line = CommandLine::from_dispatch(an_identity("ritual"), ["sync"], ["sync"]);
assert_eq!(command_line.cargo_command(), "cargo ritual sync");
}
#[test]
#[should_panic(expected = "a task always runs under at least one subcommand name")]
fn from_dispatch_refuses_an_empty_command_path() {
let _ = CommandLine::from_dispatch(an_identity("a-binary"), [], []);
}
}