diff --git a/src/cli.rs b/src/cli.rs index 3f12aaf..2f05778 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,6 +1,7 @@ use crate::config::CreatorType; use clap::{Args, Parser, Subcommand}; use clap_verbosity_flag::{InfoLevel, Verbosity}; +use std::path::PathBuf; #[derive(Parser)] #[command(version, about = "Upload and reference Roblox assets in code.")] @@ -65,6 +66,10 @@ pub struct SyncArgs { /// Provides Roblox with the amount of Robux that you are willing to spend on each non-free asset upload. #[arg(long)] pub expected_price: Option, + + /// Path to the project directory. Defaults to the current directory. + #[arg(short, long, default_value = ".")] + pub project: PathBuf, } impl SyncArgs { @@ -108,4 +113,8 @@ pub struct UploadArgs { pub struct MigrateLockfileArgs { /// The default input name to use. Only applies when upgrading from V0 to V1. pub input_name: Option, + + /// Path to the project directory. Defaults to the current directory. + #[arg(short, long, default_value = ".")] + pub project: PathBuf, } diff --git a/src/config.rs b/src/config.rs index fe18b8f..8db4f58 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,6 +16,9 @@ pub struct Config { /// A map of input names to input configurations pub inputs: HashMap, + + #[serde(skip)] + pub project_dir: PathBuf, } pub type InputMap = HashMap; @@ -23,11 +26,14 @@ pub type InputMap = HashMap; pub const FILE_NAME: &str = "asphalt.toml"; impl Config { - pub async fn read() -> anyhow::Result { - let config = fs::read_to_string(FILE_NAME) + pub async fn read_from(project_dir: PathBuf) -> anyhow::Result { + let config_path = project_dir.join(FILE_NAME); + let config_str = fs::read_to_string(&config_path) .await .context("Failed to read config file")?; - let config: Config = toml::from_str(&config)?; + + let mut config: Config = toml::from_str(&config_str)?; + config.project_dir = project_dir; Ok(config) } diff --git a/src/lockfile.rs b/src/lockfile.rs index e5c8aaf..9068255 100644 --- a/src/lockfile.rs +++ b/src/lockfile.rs @@ -41,11 +41,12 @@ impl Lockfile { .insert(hash.to_owned(), entry); } - pub async fn write(&self, filename: Option<&Path>) -> anyhow::Result<()> { + pub async fn write_to(&self, project_dir: &Path) -> anyhow::Result<()> { let mut content = toml::to_string(self)?; content.insert_str(0, "# This file is automatically @generated by Asphalt.\n# It is not intended for manual editing.\n"); - fs::write(filename.unwrap_or(Path::new(FILE_NAME)), content).await?; + let lockfile_path = project_dir.join(FILE_NAME); + fs::write(lockfile_path, content).await?; Ok(()) } } @@ -82,8 +83,9 @@ impl Default for RawLockfile { } impl RawLockfile { - pub async fn read() -> anyhow::Result { - let content = fs::read_to_string(FILE_NAME).await; + pub async fn read_from(project_dir: &Path) -> anyhow::Result { + let lockfile_path = project_dir.join(FILE_NAME); + let content = fs::read_to_string(&lockfile_path).await; let content = match content { Err(_) => return Ok(Self::default()), diff --git a/src/migrate_lockfile.rs b/src/migrate_lockfile.rs index a0e0000..40b4cc0 100644 --- a/src/migrate_lockfile.rs +++ b/src/migrate_lockfile.rs @@ -1,9 +1,10 @@ -use crate::{cli::MigrateLockfileArgs, lockfile::RawLockfile}; +use crate::{cli::MigrateLockfileArgs, config::Config, lockfile::RawLockfile}; pub async fn migrate_lockfile(args: MigrateLockfileArgs) -> anyhow::Result<()> { - let file = RawLockfile::read().await?; + let config = Config::read_from(args.project).await?; + let file = RawLockfile::read_from(&config.project_dir).await?; let migrated = file.migrate(args.input_name.as_deref()).await?; - migrated.write(None).await?; + migrated.write_to(&config.project_dir).await?; Ok(()) } diff --git a/src/sync/backend/debug.rs b/src/sync/backend/debug.rs index 2b01520..4904afb 100644 --- a/src/sync/backend/debug.rs +++ b/src/sync/backend/debug.rs @@ -3,18 +3,18 @@ use crate::{asset::Asset, lockfile::LockfileEntry, sync::backend::Params}; use anyhow::Context; use fs_err::tokio as fs; use log::info; -use std::{env, path::PathBuf}; +use std::path::PathBuf; pub struct Debug { sync_path: PathBuf, } impl Backend for Debug { - async fn new(_: Params) -> anyhow::Result + async fn new(params: Params) -> anyhow::Result where Self: Sized, { - let debug_path = env::current_dir()?.join(".asphalt-debug"); + let debug_path = params.project_dir.join(".asphalt-debug"); info!("Assets will be synced to: {}", debug_path.display()); if debug_path.exists() { diff --git a/src/sync/backend/mod.rs b/src/sync/backend/mod.rs index 4c32d2a..961a94d 100644 --- a/src/sync/backend/mod.rs +++ b/src/sync/backend/mod.rs @@ -3,6 +3,7 @@ use crate::{ config, lockfile::LockfileEntry, }; +use std::path::PathBuf; mod cloud; pub use cloud::Cloud; @@ -29,4 +30,5 @@ pub struct Params { pub api_key: Option, pub creator: config::Creator, pub expected_price: Option, + pub project_dir: PathBuf, } diff --git a/src/sync/collect.rs b/src/sync/collect.rs index bc2c6c1..fdb8c9f 100644 --- a/src/sync/collect.rs +++ b/src/sync/collect.rs @@ -25,6 +25,7 @@ pub async fn collect_events( target: SyncTarget, inputs: InputMap, mp: MultiProgress, + base_dir: &std::path::Path, ) -> anyhow::Result { let mut new_lockfile = Lockfile::default(); @@ -81,7 +82,7 @@ pub async fn collect_events( if new { progress.new += 1; if target.write_on_sync() { - new_lockfile.write(None).await?; + new_lockfile.write_to(base_dir).await?; } } } diff --git a/src/sync/mod.rs b/src/sync/mod.rs index be86392..223b988 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -61,10 +61,12 @@ enum EventState { } pub async fn sync(args: SyncArgs, mp: MultiProgress) -> anyhow::Result<()> { - let config = Config::read().await?; + let config = Config::read_from(args.project.clone()).await?; let target = args.target(); - let existing_lockfile = RawLockfile::read().await?.into_lockfile()?; + let existing_lockfile = RawLockfile::read_from(&config.project_dir) + .await? + .into_lockfile()?; let font_db = Arc::new({ let mut db = fontdb::Database::new(); @@ -76,7 +78,8 @@ pub async fn sync(args: SyncArgs, mp: MultiProgress) -> anyhow::Result<()> { let collector_handle = tokio::spawn({ let inputs = config.inputs.clone(); - async move { collect_events(event_rx, target, inputs, mp).await } + let project_dir = config.project_dir.clone(); + async move { collect_events(event_rx, target, inputs, mp, &project_dir).await } }); let params = walk::Params { @@ -88,6 +91,7 @@ pub async fn sync(args: SyncArgs, mp: MultiProgress) -> anyhow::Result<()> { api_key: args.api_key, creator: config.creator.clone(), expected_price: args.expected_price, + project_dir: config.project_dir.clone(), }; match &target { SyncTarget::Cloud { dry_run: false } => { @@ -117,7 +121,7 @@ pub async fn sync(args: SyncArgs, mp: MultiProgress) -> anyhow::Result<()> { } if target.write_on_sync() { - results.new_lockfile.write(None).await?; + results.new_lockfile.write_to(&config.project_dir).await?; } for (input_name, source) in results.input_sources { @@ -140,8 +144,9 @@ pub async fn sync(args: SyncArgs, mp: MultiProgress) -> anyhow::Result<()> { }; let code = codegen::generate_code(lang, &input_name, &node)?; - fs::create_dir_all(&input.output_path).await?; - fs::write(input.output_path.join(format!("{input_name}.{ext}")), code).await?; + let output_path = config.project_dir.join(&input.output_path); + fs::create_dir_all(&output_path).await?; + fs::write(output_path.join(format!("{input_name}.{ext}")), code).await?; } } diff --git a/src/sync/walk.rs b/src/sync/walk.rs index 205700f..3e83d8f 100644 --- a/src/sync/walk.rs +++ b/src/sync/walk.rs @@ -43,10 +43,12 @@ pub async fn walk(params: Params, config: &Config, tx: &UnboundedSender