diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c2e1bbf1..3617eb478 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ * Better settings controls ([#725]) * Rework patch visualizer with many fixes and improvements ([#726]) * Added support for syncing in `.toml` files ([#633]) +* Add `plugin` flag to the `build` command that outputs to the local plugins folder ([#735]) [#668]: https://github.com/rojo-rbx/rojo/pull/668 [#674]: https://github.com/rojo-rbx/rojo/pull/674 @@ -34,6 +35,7 @@ [#725]: https://github.com/rojo-rbx/rojo/pull/725 [#726]: https://github.com/rojo-rbx/rojo/pull/726 [#633]: https://github.com/rojo-rbx/rojo/pull/633 +[#735]: https://github.com/rojo-rbx/rojo/pull/735 ## [7.3.0] - April 22, 2023 * Added `$attributes` to project format. ([#574]) diff --git a/benches/build.rs b/benches/build.rs index 456a5339a..87d33e442 100644 --- a/benches/build.rs +++ b/benches/build.rs @@ -31,11 +31,12 @@ fn bench_build_place(c: &mut Criterion, name: &str, path: &str) { fn place_setup>(input_path: P) -> (TempDir, BuildCommand) { let dir = tempdir().unwrap(); let input = input_path.as_ref().to_path_buf(); - let output = dir.path().join("output.rbxlx"); + let output = Some(dir.path().join("output.rbxlx")); let options = BuildCommand { project: input, watch: false, + plugin: None, output, }; diff --git a/src/cli/build.rs b/src/cli/build.rs index d6c3f3499..8f705d376 100644 --- a/src/cli/build.rs +++ b/src/cli/build.rs @@ -4,10 +4,11 @@ use std::{ path::{Path, PathBuf}, }; -use anyhow::Context; -use clap::Parser; +use anyhow::{bail, Context}; +use clap::{CommandFactory, Parser}; use fs_err::File; use memofs::Vfs; +use roblox_install::RobloxStudio; use tokio::runtime::Runtime; use crate::serve_session::ServeSession; @@ -16,6 +17,8 @@ use super::resolve_path; const UNKNOWN_OUTPUT_KIND_ERR: &str = "Could not detect what kind of file to build. \ Expected output file to end in .rbxl, .rbxlx, .rbxm, or .rbxmx."; +const UNKNOWN_PLUGIN_KIND_ERR: &str = "Could not detect what kind of file to build. \ + Expected plugin file to end in .rbxm or .rbxmx."; /// Generates a model or place file from the Rojo project. #[derive(Debug, Parser)] @@ -28,7 +31,13 @@ pub struct BuildCommand { /// /// Should end in .rbxm, .rbxl, .rbxmx, or .rbxlx. #[clap(long, short)] - pub output: PathBuf, + pub output: Option, + + /// Alternative to the output flag that outputs the result in the local plugins folder. + /// + /// Should end in .rbxm or .rbxl. + #[clap(long, short)] + pub plugin: Option, /// Whether to automatically rebuild when any input files change. #[clap(long)] @@ -37,18 +46,52 @@ pub struct BuildCommand { impl BuildCommand { pub fn run(self) -> anyhow::Result<()> { - let project_path = resolve_path(&self.project); + let (output_path, output_kind) = match (self.output, self.plugin) { + (Some(_), Some(_)) => { + BuildCommand::command() + .error( + clap::ErrorKind::ArgumentConflict, + "the argument '--output ' cannot be used with '--plugin '", + ) + .exit(); + } + (None, None) => { + BuildCommand::command() + .error( + clap::ErrorKind::MissingRequiredArgument, + "one of the following arguments must be provided: \n --output \n --plugin ", + ) + .exit(); + } + (Some(output), None) => { + let output_kind = + OutputKind::from_output_path(&output).context(UNKNOWN_OUTPUT_KIND_ERR)?; + + (output, output_kind) + } + (None, Some(plugin)) => { + if plugin.is_absolute() { + bail!("plugin flag path cannot be absolute.") + } - let output_kind = detect_output_kind(&self.output).context(UNKNOWN_OUTPUT_KIND_ERR)?; + let output_kind = + OutputKind::from_plugin_path(&plugin).context(UNKNOWN_PLUGIN_KIND_ERR)?; + let studio = RobloxStudio::locate()?; + + (studio.plugins_path().join(&plugin), output_kind) + } + }; + + let project_path = resolve_path(&self.project); log::trace!("Constructing in-memory filesystem"); let vfs = Vfs::new_default(); vfs.set_watch_enabled(self.watch); - let session = ServeSession::new(vfs, &project_path)?; + let session = ServeSession::new(vfs, project_path)?; let mut cursor = session.message_queue().cursor(); - write_model(&session, &self.output, output_kind)?; + write_model(&session, &output_path, output_kind)?; if self.watch { let rt = Runtime::new().unwrap(); @@ -58,7 +101,7 @@ impl BuildCommand { let (new_cursor, _patch_set) = rt.block_on(receiver).unwrap(); cursor = new_cursor; - write_model(&session, &self.output, output_kind)?; + write_model(&session, &output_path, output_kind)?; } } @@ -86,15 +129,27 @@ enum OutputKind { Rbxl, } -fn detect_output_kind(output: &Path) -> Option { - let extension = output.extension()?.to_str()?; +impl OutputKind { + fn from_output_path(output: &Path) -> Option { + let extension = output.extension()?.to_str()?; + + match extension { + "rbxlx" => Some(OutputKind::Rbxlx), + "rbxmx" => Some(OutputKind::Rbxmx), + "rbxl" => Some(OutputKind::Rbxl), + "rbxm" => Some(OutputKind::Rbxm), + _ => None, + } + } - match extension { - "rbxlx" => Some(OutputKind::Rbxlx), - "rbxmx" => Some(OutputKind::Rbxmx), - "rbxl" => Some(OutputKind::Rbxl), - "rbxm" => Some(OutputKind::Rbxm), - _ => None, + fn from_plugin_path(output: &Path) -> Option { + let extension = output.extension()?.to_str()?; + + match extension { + "rbxmx" => Some(OutputKind::Rbxmx), + "rbxm" => Some(OutputKind::Rbxm), + _ => None, + } } }