Skip to main content

dora_cli/
lib.rs

1use colored::Colorize;
2use std::{
3    net::{IpAddr, Ipv4Addr},
4    path::PathBuf,
5};
6
7mod command;
8mod common;
9mod env_overrides;
10mod formatting;
11pub mod output;
12pub mod session;
13mod template;
14mod ws_client;
15pub use ws_client::WsSession;
16
17pub use command::{BuildConfig, build};
18pub use command::{Executable, Run as RunCommand, run};
19
20/// Default address for *connecting* to a coordinator (client side).
21const LOCALHOST: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
22/// Default address for the coordinator to *listen* on (server side).
23const LISTEN_DEFAULT: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
24
25#[derive(Debug, clap::Parser)]
26#[clap(version = get_version_info())]
27pub struct Args {
28    #[clap(subcommand)]
29    command: command::Command,
30}
31
32fn get_version_info() -> clap::builder::Str {
33    build_version_string().into()
34}
35
36fn build_version_string() -> String {
37    env!("CARGO_PKG_VERSION").to_string()
38}
39
40#[derive(Debug, clap::Args)]
41pub struct CommandNew {
42    /// The entity that should be created
43    #[clap(long, value_enum, default_value_t = Kind::Dataflow)]
44    kind: Kind,
45    /// The programming language that should be used
46    #[clap(long, value_enum, default_value_t = Lang::Rust)]
47    lang: Lang,
48    /// Desired name of the entity
49    name: String,
50    /// Where to create the entity
51    #[clap(hide = true)]
52    path: Option<PathBuf>,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
56enum Kind {
57    Dataflow,
58    Node,
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
62enum Lang {
63    Rust,
64    Python,
65    C,
66    Cxx,
67}
68
69/// Parse a command line and run it, the way both dora entry points need to.
70///
71/// `main.rs` and the `dora` console script in the `dora-rs-cli` wheel both land
72/// here, so clap's exit behaviour -- usage errors to stderr with status 2,
73/// `--help` / `--version` to stdout with status 0 -- is defined once instead of
74/// being reimplemented per entry point.
75pub fn lib_main_from_argv<I, T>(argv: I)
76where
77    I: IntoIterator<Item = T>,
78    T: Into<std::ffi::OsString> + Clone,
79{
80    use clap::Parser as _;
81
82    let argv: Vec<std::ffi::OsString> = argv.into_iter().map(Into::into).collect();
83
84    // Record argv[0] -- the console-script/executable path -- so that, in the
85    // `dora-rs-cli` wheel, `dora up` / `dora cluster up` re-spawn the real
86    // `dora` binary via `sys.argv[0]` rather than a fixed `args_os()` index that
87    // is wrong on Windows (#3327). `py_main` passes the normalized `sys.argv`
88    // here, whose first element is that path on both Unix and Windows. Harmless
89    // in the standalone binary, where `dora_executable_path` uses `current_exe`.
90    if let Some(exe) = argv.first() {
91        command::set_python_executable_path(exe.clone());
92    }
93
94    lib_main(Args::try_parse_from(argv).unwrap_or_else(|err| err.exit()))
95}
96
97pub fn lib_main(args: Args) {
98    if let Err(err) = args.command.execute() {
99        eprintln!("\n\n{}", "[ERROR]".bold().red());
100        eprintln!("{err:?}");
101        std::process::exit(1);
102    }
103}