1mod 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#[derive(Debug, Parser)]
31#[clap(name = "Rojo", version, about)]
32pub struct Options {
33 #[clap(flatten)]
34 pub global: GlobalOptions,
35
36 #[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 #[clap(long("verbose"), short, global(true), parse(from_occurrences))]
61 pub verbosity: u8,
62
63 #[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}