diff --git a/src/cli/build.rs b/src/cli/build.rs index d6c3f3499..435c3bf6f 100644 --- a/src/cli/build.rs +++ b/src/cli/build.rs @@ -14,7 +14,7 @@ use crate::serve_session::ServeSession; use super::resolve_path; -const UNKNOWN_OUTPUT_KIND_ERR: &str = "Could not detect what kind of file to build. \ +pub(in crate::cli) 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."; /// Generates a model or place file from the Rojo project. @@ -72,7 +72,7 @@ impl BuildCommand { /// The different kinds of output that Rojo can build to. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum OutputKind { +pub(in crate::cli) enum OutputKind { /// An XML model file. Rbxmx, @@ -86,7 +86,7 @@ enum OutputKind { Rbxl, } -fn detect_output_kind(output: &Path) -> Option { +pub(in crate::cli) fn detect_output_kind(output: &Path) -> Option { let extension = output.extension()?.to_str()?; match extension { @@ -103,7 +103,7 @@ fn xml_encode_config() -> rbx_xml::EncodeOptions { } #[profiling::function] -fn write_model( +pub(in crate::cli) fn write_model( session: &ServeSession, output: &Path, output_kind: OutputKind, diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 36b329404..7315c27a2 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -4,6 +4,7 @@ mod build; mod doc; mod fmt_project; mod init; +mod open; mod plugin; mod serve; mod sourcemap; @@ -18,6 +19,7 @@ pub use self::build::BuildCommand; pub use self::doc::DocCommand; pub use self::fmt_project::FmtProjectCommand; pub use self::init::{InitCommand, InitKind}; +use self::open::OpenCommand; pub use self::plugin::{PluginCommand, PluginSubcommand}; pub use self::serve::ServeCommand; pub use self::sourcemap::SourcemapCommand; @@ -46,6 +48,7 @@ impl Options { Subcommand::FmtProject(subcommand) => subcommand.run(), Subcommand::Doc(subcommand) => subcommand.run(), Subcommand::Plugin(subcommand) => subcommand.run(), + Subcommand::Open(subcommand) => subcommand.run(self.global), } } } @@ -119,6 +122,7 @@ pub enum Subcommand { FmtProject(FmtProjectCommand), Doc(DocCommand), Plugin(PluginCommand), + Open(OpenCommand), } pub(super) fn resolve_path(path: &Path) -> Cow<'_, Path> { diff --git a/src/cli/open.rs b/src/cli/open.rs new file mode 100644 index 000000000..4b522326a --- /dev/null +++ b/src/cli/open.rs @@ -0,0 +1,74 @@ +use anyhow::{Context, Ok}; +use clap::Parser; +use memofs::Vfs; +use roblox_install::RobloxStudio; + +use crate::{ + cli::{ + build::{detect_output_kind, write_model, UNKNOWN_OUTPUT_KIND_ERR}, + plugin::install_plugin, + serve::{show_start_message, DEFAULT_BIND_ADDRESS, DEFAULT_PORT}, + }, + serve_session::ServeSession, + web::LiveServer, + PROJECT_FILENAME, +}; +use std::{env, net::IpAddr, path::PathBuf, sync::Arc}; + +use super::GlobalOptions; + +#[derive(Debug, Parser)] +pub struct OpenCommand { + /// Path to the project file to serve from. Defaults to default.project.json. + #[clap(value_parser)] + pub project: Option, + + // Path to an output place to build and serve to. Will be created automatically + /// if it doesn't exist. + #[clap(long)] + pub output: PathBuf, + + /// The IP address to listen on. Defaults to `127.0.0.1`. + #[clap(long)] + pub address: Option, + + /// The port to listen on. Defaults to the project's preference, or `34872` if + /// it has none. + #[clap(long)] + pub port: Option, +} + +impl OpenCommand { + pub fn run(self, global: GlobalOptions) -> anyhow::Result<()> { + let project = self + .project + .unwrap_or_else(|| env::current_dir().unwrap().join(PROJECT_FILENAME)); + let output_kind = detect_output_kind(&self.output).context(UNKNOWN_OUTPUT_KIND_ERR)?; + + log::trace!("Constructing in-memory filesystem"); + let vfs = Vfs::new_default(); + let session = ServeSession::new(vfs, &project)?; + + if !self.output.exists() { + write_model(&session, &self.output, output_kind)?; + } + install_plugin().unwrap(); + + opener::open(&self.output).expect("Could not open place in Roblox Studio"); + + let ip = self + .address + .or_else(|| session.serve_address()) + .unwrap_or_else(|| DEFAULT_BIND_ADDRESS.into()); + let port = self + .port + .or_else(|| session.project_port()) + .unwrap_or(DEFAULT_PORT); + let server = LiveServer::new(Arc::new(session)); + + show_start_message(ip, port, global.color.into())?; + server.start((ip, port).into()); + + Ok(()) + } +} diff --git a/src/cli/plugin.rs b/src/cli/plugin.rs index aa8773b35..90d802689 100644 --- a/src/cli/plugin.rs +++ b/src/cli/plugin.rs @@ -46,7 +46,7 @@ impl PluginSubcommand { } } -fn install_plugin() -> anyhow::Result<()> { +pub(in crate::cli) fn install_plugin() -> anyhow::Result<()> { let plugin_snapshot: VfsSnapshot = bincode::deserialize(PLUGIN_BINCODE) .expect("Rojo's plugin was not properly packed into Rojo's binary"); diff --git a/src/cli/serve.rs b/src/cli/serve.rs index 0e35785e3..9b8ca73f6 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -13,8 +13,8 @@ use crate::{serve_session::ServeSession, web::LiveServer}; use super::{resolve_path, GlobalOptions}; -const DEFAULT_BIND_ADDRESS: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1); -const DEFAULT_PORT: u16 = 34872; +pub(in crate::cli) const DEFAULT_BIND_ADDRESS: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1); +pub(in crate::cli) const DEFAULT_PORT: u16 = 34872; /// Expose a Rojo project to the Rojo Studio plugin. #[derive(Debug, Parser)] @@ -60,7 +60,11 @@ impl ServeCommand { } } -fn show_start_message(bind_address: IpAddr, port: u16, color: ColorChoice) -> io::Result<()> { +pub(in crate::cli) fn show_start_message( + bind_address: IpAddr, + port: u16, + color: ColorChoice, +) -> io::Result<()> { let mut green = ColorSpec::new(); green.set_fg(Some(Color::Green)).set_bold(true); diff --git a/src/project.rs b/src/project.rs index 330cbd23a..f80d2ca4d 100644 --- a/src/project.rs +++ b/src/project.rs @@ -10,7 +10,7 @@ use thiserror::Error; use crate::{glob::Glob, resolution::UnresolvedValue}; -static PROJECT_FILENAME: &str = "default.project.json"; +pub(crate) static PROJECT_FILENAME: &str = "default.project.json"; /// Error type returned by any function that handles projects. #[derive(Debug, Error)]