Skip to main content

librojo/cli/
mod.rs

1//! Defines Rojo's CLI through clap types.
2
3mod build;
4mod doc;
5mod fmt_project;
6mod init;
7mod plugin;
8mod serve;
9mod sourcemap;
10mod syncback;
11mod upload;
12
13use std::{borrow::Cow, env, path::Path, str::FromStr};
14
15use anyhow::Context;
16use clap::Parser;
17use thiserror::Error;
18
19pub use self::build::BuildCommand;
20pub use self::doc::DocCommand;
21pub use self::fmt_project::FmtProjectCommand;
22pub use self::init::{InitCommand, InitKind};
23pub use self::plugin::{PluginCommand, PluginSubcommand};
24pub use self::serve::ServeCommand;
25pub use self::sourcemap::SourcemapCommand;
26pub use self::syncback::SyncbackCommand;
27pub use self::upload::UploadCommand;
28
29/// Command line options that Rojo accepts, defined using the clap crate.
30#[derive(Debug, Parser)]
31#[clap(name = "Rojo", version, about)]
32pub struct Options {
33    #[clap(flatten)]
34    pub global: GlobalOptions,
35
36    /// Subcommand to run in this invocation.
37    #[clap(subcommand)]
38    pub subcommand: Subcommand,
39}
40
41impl Options {
42    pub fn run(self) -> anyhow::Result<()> {
43        match self.subcommand {
44            Subcommand::Init(subcommand) => subcommand.run(),
45            Subcommand::Serve(subcommand) => subcommand.run(self.global),
46            Subcommand::Build(subcommand) => subcommand.run(),
47            Subcommand::Upload(subcommand) => subcommand.run(),
48            Subcommand::Sourcemap(subcommand) => subcommand.run(),
49            Subcommand::FmtProject(subcommand) => subcommand.run(),
50            Subcommand::Doc(subcommand) => subcommand.run(),
51            Subcommand::Plugin(subcommand) => subcommand.run(),
52            Subcommand::Syncback(subcommand) => subcommand.run(self.global),
53        }
54    }
55}
56
57#[derive(Debug, Parser)]
58pub struct GlobalOptions {
59    /// Sets verbosity level. Can be specified multiple times.
60    #[clap(long("verbose"), short, global(true), parse(from_occurrences))]
61    pub verbosity: u8,
62
63    /// Set color behavior. Valid values are auto, always, and never.
64    #[clap(long("color"), global(true), default_value("auto"))]
65    pub color: ColorChoice,
66}
67
68#[derive(Debug, Clone, Copy)]
69pub enum ColorChoice {
70    Auto,
71    Always,
72    Never,
73}
74
75impl FromStr for ColorChoice {
76    type Err = ColorChoiceParseError;
77
78    fn from_str(source: &str) -> Result<Self, Self::Err> {
79        match source {
80            "auto" => Ok(ColorChoice::Auto),
81            "always" => Ok(ColorChoice::Always),
82            "never" => Ok(ColorChoice::Never),
83            _ => Err(ColorChoiceParseError {
84                attempted: source.to_owned(),
85            }),
86        }
87    }
88}
89
90impl From<ColorChoice> for termcolor::ColorChoice {
91    fn from(value: ColorChoice) -> Self {
92        match value {
93            ColorChoice::Auto => termcolor::ColorChoice::Auto,
94            ColorChoice::Always => termcolor::ColorChoice::Always,
95            ColorChoice::Never => termcolor::ColorChoice::Never,
96        }
97    }
98}
99
100impl From<ColorChoice> for env_logger::WriteStyle {
101    fn from(value: ColorChoice) -> Self {
102        match value {
103            ColorChoice::Auto => env_logger::WriteStyle::Auto,
104            ColorChoice::Always => env_logger::WriteStyle::Always,
105            ColorChoice::Never => env_logger::WriteStyle::Never,
106        }
107    }
108}
109
110#[derive(Debug, Error)]
111#[error("Invalid color choice '{attempted}'. Valid values are: auto, always, never")]
112pub struct ColorChoiceParseError {
113    attempted: String,
114}
115
116#[derive(Debug, Parser)]
117pub enum Subcommand {
118    Init(InitCommand),
119    Serve(ServeCommand),
120    Build(BuildCommand),
121    Upload(UploadCommand),
122    Sourcemap(SourcemapCommand),
123    FmtProject(FmtProjectCommand),
124    Doc(DocCommand),
125    Plugin(PluginCommand),
126    Syncback(SyncbackCommand),
127}
128
129pub(super) fn resolve_path(path: &Path) -> anyhow::Result<Cow<'_, Path>> {
130    if path.is_absolute() {
131        Ok(Cow::Borrowed(path))
132    } else {
133        let current_dir = env::current_dir().context(
134            "Could not determine the current working directory. \
135             It may have been deleted, or Rojo may not have permission to access it.",
136        )?;
137        Ok(Cow::Owned(current_dir.join(path)))
138    }
139}