diff --git a/Cargo.lock b/Cargo.lock index 280c4f3..ac3da9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,6 +224,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "errno" version = "0.3.14" @@ -284,12 +290,28 @@ dependencies = [ "wasip2", ] +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "indicatif" version = "0.18.3" @@ -374,14 +396,18 @@ name = "minecraft-sync" version = "0.1.0" dependencies = [ "clap", + "console", "ctrlc", "dirs", "flate2", "indicatif", + "serde", + "shellexpand", "tar", "tempfile", "thiserror", "thiserror-ext", + "toml", "tracing", "tracing-subscriber", "walkdir", @@ -552,6 +578,45 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +dependencies = [ + "serde_core", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -561,6 +626,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shellexpand" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb" +dependencies = [ + "dirs", +] + [[package]] name = "simd-adler32" version = "0.3.8" @@ -665,6 +739,45 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "toml" +version = "0.9.11+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" + [[package]] name = "tracing" version = "0.1.44" @@ -860,6 +973,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" + [[package]] name = "wit-bindgen" version = "0.51.0" diff --git a/Cargo.toml b/Cargo.toml index 229021d..030bf9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,14 +5,18 @@ edition = "2024" [dependencies] clap = { version = "4.5.56", features = ["derive"] } +console = "0.16.2" ctrlc = "3.5.1" dirs = "6.0.0" flate2 = "1.1.8" indicatif = "0.18.3" +serde = { version = "1.0.228", features = ["derive"] } +shellexpand = "3.1.1" tar = "0.4.44" tempfile = "3.24.0" thiserror = "2.0.18" thiserror-ext = "0.3.0" +toml = "0.9.11" tracing = "0.1.44" tracing-subscriber = { version = "0.3.22", features = ["env-filter"] } walkdir = "2.5.0" diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..dde93bf --- /dev/null +++ b/src/config.rs @@ -0,0 +1,34 @@ +use std::{fs, io}; + +use serde::{Deserialize, Serialize}; + +use crate::{ + error::{Error, Result}, + storage::fs::base_path, +}; + +#[derive(Serialize, Deserialize)] +pub struct Config { + pub sync: Sync, +} + +#[derive(Serialize, Deserialize)] +pub struct Sync { + pub fs_dir: Option, +} + +impl Config { + pub fn new() -> Result { + let path = base_path().join("config.toml"); + let bytes = match fs::read(&path) { + Ok(b) => b, + Err(e) => match e.kind() { + io::ErrorKind::NotFound => Vec::new(), + _ => return Err(Error::read_config(e, &path)), + }, + }; + let config = + toml::from_slice::(&bytes).map_err(|e| Error::parse_config(e, &path))?; + Ok(config) + } +} diff --git a/src/error.rs b/src/error.rs index f3949ff..97833e2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -34,6 +34,18 @@ pub enum ErrorKind { #[source] source: tempfile::PersistError, }, + #[error("error parsing configuration at {path}")] + ParseConfig { + path: PathBuf, + #[source] + source: toml::de::Error, + }, + #[error("error reading configuration at {path}")] + ReadConfig { + path: PathBuf, + #[source] + source: std::io::Error, + }, } pub type Result = core::result::Result; diff --git a/src/main.rs b/src/main.rs index fc83578..620c829 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod archive; +mod config; mod error; mod reporter; mod storage; @@ -6,6 +7,7 @@ mod storage; use std::{ops::Deref, sync::Arc}; use clap::{CommandFactory, Parser, Subcommand}; +use console::style; use indicatif::MultiProgress; use thiserror_ext::AsReport; use tracing::level_filters::LevelFilter; @@ -14,6 +16,7 @@ use tracing_subscriber::{ }; use crate::{ + config::Config, reporter::Reporter, storage::{Storage, StorageImpl, get_storage_from_env}, }; @@ -34,15 +37,7 @@ enum Commands { struct AppState { multi: MultiProgress, storage: Storage, -} - -impl AppState { - pub fn new() -> Self { - Self { - multi: MultiProgress::new(), - storage: get_storage_from_env(), - } - } + config: Config, } #[derive(Clone)] @@ -63,7 +58,12 @@ impl Data { fn run() -> crate::error::Result<()> { let cli = Cli::parse(); - let app_state = Data::new(AppState::new()); + let config = Config::new()?; + let app_state = Data::new(AppState { + multi: MultiProgress::new(), + storage: get_storage_from_env(), + config, + }); match cli.command { Some(Commands::Push) => { @@ -88,7 +88,7 @@ fn main() -> std::io::Result<()> { ) .init(); if let Err(e) = run() { - eprintln!("Error: {}", e.as_report()); + eprintln!("{}: {}", style("Error").red(), e.as_report()); } Ok(()) diff --git a/src/storage/fs.rs b/src/storage/fs.rs index 41b617f..8ad2af5 100644 --- a/src/storage/fs.rs +++ b/src/storage/fs.rs @@ -31,8 +31,12 @@ impl StorageImpl for FSStorage { let revision = self.get_revision()?; let new_revision = revision + 1; let reporter = Reporter::new(app_state.clone()); - let result = create_archive(app_state, &reporter)?; - let archive_path = base_path().join(&format!("revision_{new_revision}.tar.gz")); + let result = create_archive(app_state.clone(), &reporter)?; + let archive_path = match &app_state.config.sync.fs_dir { + Some(dir) => PathBuf::from(shellexpand::tilde(&dir).as_ref()) + .join(&format!("revision_{new_revision}.tar.gz")), + _ => base_path().join(&format!("revision_{new_revision}.tar.gz")), + }; result .temp_file