From 814a5c6e523ec25e4b7bbd8eea753796a958143e Mon Sep 17 00:00:00 2001 From: jeparlefrancais Date: Thu, 3 Dec 2020 01:36:53 -0500 Subject: [PATCH 1/5] remove roblox CLA bot --- .github/workflows/clabot.yml | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 .github/workflows/clabot.yml diff --git a/.github/workflows/clabot.yml b/.github/workflows/clabot.yml deleted file mode 100644 index cc8327d..0000000 --- a/.github/workflows/clabot.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: "CLA Signature Bot" -on: - issue_comment: - types: [created] - pull_request: - types: [opened,closed,synchronize] - -jobs: - clabot: - runs-on: ubuntu-latest - steps: - - name: "CLA Signature Bot" - uses: roblox/cla-signature-bot@v2.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - whitelist: "LPGhatguy,ZoteTheMighty,cliffchapmanrbx,MagiMaster,MisterUncloaked,amatosov-rbx" - use-remote-repo: true - remote-repo-name: "roblox/cla-bot-store" - remote-repo-pat: ${{ secrets.CLA_REMOTE_REPO_PAT }} - url-to-cladocument: "https://roblox.github.io/cla-bot-store/" From 73890e02ebb80c84357de763106ecf2f3b9bf6a7 Mon Sep 17 00:00:00 2001 From: jeparlefrancais Date: Sun, 22 Nov 2020 15:53:14 -0500 Subject: [PATCH 2/5] implement sync to local content folder --- Cargo.lock | 79 ++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 ++ src/codegen.rs | 16 ++++----- src/commands/sync.rs | 54 ++++++++++++++++++++++-------- src/data/sync.rs | 27 +++++++++++++-- src/options.rs | 6 +++- src/sync_backend.rs | 71 ++++++++++++++++++++++++++++++++++++--- 7 files changed, 226 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 499e75f..aa69d84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -76,11 +76,26 @@ dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "blake2b_simd" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "blake3" version = "0.1.3" @@ -293,6 +308,25 @@ name = "difference" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "dirs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dirs-sys 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dirs-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_users 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "dtoa" version = "0.4.4" @@ -828,6 +862,11 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "path-slash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "percent-encoding" version = "1.0.1" @@ -1062,6 +1101,16 @@ name = "redox_syscall" version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "redox_users" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-argon2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "regex" version = "1.3.3" @@ -1119,6 +1168,26 @@ dependencies = [ "winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "roblox_install" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rust-argon2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2b_simd 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rustc-demangle" version = "0.1.16" @@ -1343,9 +1412,11 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "packos 0.1.0", + "path-slash 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "png 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", + "roblox_install 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1753,7 +1824,9 @@ dependencies = [ "checksum backtrace 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)" = "b1e692897359247cc6bb902933361652380af0f1b7651ae5c5013407f30e109e" "checksum backtrace-sys 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "7de8aba10a69c8e8d7622c5710229485ec32e9d55fdad160ea559c086fdcd118" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +"checksum base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +"checksum blake2b_simd 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" "checksum blake3 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "41b598ce988ade113c05bc970a8c9102b59dfac0b318289971cba3379471cae4" "checksum bstr 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "fe8a65814ca90dfc9705af76bb6ba3c6e2534489a72270e797e603783bb4990b" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" @@ -1778,6 +1851,8 @@ dependencies = [ "checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" "checksum deflate 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)" = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4" "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" +"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +"checksum dirs-sys 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" "checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e" "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum encode_unicode 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" @@ -1837,6 +1912,7 @@ dependencies = [ "checksum openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)" = "465d16ae7fc0e313318f7de5cecf57b2fbe7511fd213978b457e1c96ff46736f" "checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" "checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" +"checksum path-slash 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ff65715a17cba8979903db6294baef56c5d39e05c8b054cffa31e69e61f24c68" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" @@ -1864,10 +1940,13 @@ dependencies = [ "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum redox_users 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" "checksum regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b5508c1941e4e7cb19965abef075d35a9a8b5cdf0846f30b4050e9b55dc55e87" "checksum regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e734e891f5b408a29efbf8309e656876276f49ab6a6ac208600b4419bd893d90" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "f88643aea3c1343c804950d7bf983bd2067f5ab59db6d613a08e05572f2714ab" +"checksum roblox_install 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96d8c86f81b83db9b127bf66b51b9a24c844fe755f43310e56c09e2303148527" +"checksum rust-argon2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9dab61250775933275e84053ac235621dfb739556d5c54a2f2e9313b7cf43a19" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rustversion 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6" diff --git a/Cargo.toml b/Cargo.toml index 2f1ec5a..c5d82c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,9 +40,11 @@ fs-err = "2.3.0" globset = "0.4.4" lazy_static = "1.4.0" log = "0.4.8" +path-slash = "0.1.3" png = "0.15.3" regex = "1.3.3" reqwest = "0.9.20" +roblox_install = "0.3.0" serde = { version = "1.0", features = ["derive", "rc"] } serde_json = "1.0" structopt = { version = "0.3", default-features = false } diff --git a/src/codegen.rs b/src/codegen.rs index 33eff79..d8d87e1 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -12,7 +12,7 @@ use fs_err::File; use crate::{ data::ImageSlice, - data::SyncInput, + data::{AssetId, SyncInput}, lua_ast::{Block, Expression, Function, IfBlock, Statement, Table}, }; @@ -143,7 +143,7 @@ fn codegen_grouped(output_path: &Path, inputs: &[&SyncInput]) -> io::Result<()> let input = inputs_by_dpi_scale.values().next().unwrap(); - match (input.id, input.slice) { + match (&input.id, input.slice) { (Some(id), Some(slice)) => Some(codegen_url_and_slice(id, slice)), (Some(id), None) => Some(codegen_just_asset_url(id)), _ => None, @@ -175,7 +175,7 @@ fn codegen_grouped(output_path: &Path, inputs: &[&SyncInput]) -> io::Result<()> /// defined, and so generate individual files. fn codegen_individual(inputs: &[&SyncInput]) -> io::Result<()> { for input in inputs { - let expression = match (input.id, input.slice) { + let expression = match (&input.id, input.slice) { (Some(id), Some(slice)) => codegen_url_and_slice(id, slice), (Some(id), None) => codegen_just_asset_url(id), _ => continue, @@ -193,12 +193,12 @@ fn codegen_individual(inputs: &[&SyncInput]) -> io::Result<()> { Ok(()) } -fn codegen_url_and_slice(id: u64, slice: ImageSlice) -> Expression { +fn codegen_url_and_slice(id: &AssetId, slice: ImageSlice) -> Expression { let offset = slice.min(); let size = slice.size(); let mut table = Table::new(); - table.add_entry("Image", format!("rbxassetid://{}", id)); + table.add_entry("Image", id.to_string()); table.add_entry( "ImageRectOffset", Expression::Raw(format!("Vector2.new({}, {})", offset.0, offset.1)), @@ -212,8 +212,8 @@ fn codegen_url_and_slice(id: u64, slice: ImageSlice) -> Expression { Expression::Table(table) } -fn codegen_just_asset_url(id: u64) -> Expression { - Expression::String(format!("rbxassetid://{}", id)) +fn codegen_just_asset_url(id: &AssetId) -> Expression { + Expression::String(id.to_string()) } fn codegen_dpi_option(input: &SyncInput) -> (Expression, Block) { @@ -221,7 +221,7 @@ fn codegen_dpi_option(input: &SyncInput) -> (Expression, Block) { // FIXME: We should probably pull data out of SyncInput at the start of // codegen so that we can handle invariants like this. - let id = input.id.unwrap(); + let id = input.id.as_ref().unwrap(); let value = match input.slice { Some(slice) => codegen_url_and_slice(id, slice), diff --git a/src/commands/sync.rs b/src/commands/sync.rs index c3521c3..ace54f3 100644 --- a/src/commands/sync.rs +++ b/src/commands/sync.rs @@ -16,14 +16,14 @@ use crate::{ asset_name::AssetName, auth_cookie::get_auth_cookie, codegen::perform_codegen, - data::{Config, ConfigError, ImageSlice, InputManifest, Manifest, ManifestError, SyncInput}, + data::{AssetId, Config, ConfigError, ImageSlice, InputManifest, Manifest, ManifestError, SyncInput}, dpi_scale, image::Image, options::{GlobalOptions, SyncOptions, SyncTarget}, roblox_web_api::{RobloxApiClient, RobloxApiError}, sync_backend::{ - DebugSyncBackend, Error as SyncBackendError, NoneSyncBackend, RetryBackend, - RobloxSyncBackend, SyncBackend, UploadInfo, + DebugSyncBackend, Error as SyncBackendError, LocalSyncBackend, NoneSyncBackend, + RetryBackend, RobloxSyncBackend, SyncBackend, UploadInfo, }, }; @@ -47,6 +47,7 @@ pub fn sync(global: GlobalOptions, options: SyncOptions) -> Result<(), SyncError let mut session = SyncSession::new(&fuzzy_config_path)?; + let project_name = session.root_config().name.to_string(); session.discover_configs()?; session.discover_inputs()?; @@ -59,6 +60,13 @@ pub fn sync(global: GlobalOptions, options: SyncOptions) -> Result<(), SyncError RobloxSyncBackend::new(&mut api_client, group_id), ); } + SyncTarget::Local => { + sync_session( + &mut session, + &options, + LocalSyncBackend::new(Some(project_name))?, + ); + } SyncTarget::None => { sync_session(&mut session, &options, NoneSyncBackend); } @@ -257,7 +265,9 @@ impl SyncSession { // If this input was known during the last sync operation, // pull the information we knew about it out. let (id, slice) = match self.original_manifest.inputs.get(&name) { - Some(original) => (original.id, original.slice), + Some(original) => { + (original.id.map(AssetId::Id), original.slice) + } None => (None, None), }; @@ -459,7 +469,7 @@ impl SyncSession { for (asset_name, slice) in &packed_image.slices { let input = self.inputs.get_mut(asset_name).unwrap(); - input.id = Some(id); + input.id = Some(id.clone()); input.slice = Some(*slice); } @@ -538,11 +548,17 @@ impl SyncSession { .inputs .iter() .map(|(name, input)| { + let id = input.id.as_ref().and_then(|asset_id| { + match asset_id { + AssetId::Id(id) => Some(*id), + _ => None, + } + }); ( name.clone(), InputManifest { hash: input.hash.clone(), - id: input.id, + id, slice: input.slice, packable: input.config.packable, }, @@ -603,10 +619,10 @@ impl SyncSession { let mut file = BufWriter::new(fs_err::File::create(list_path)?); - let known_ids: BTreeSet = self.inputs.values().filter_map(|input| input.id).collect(); + let known_ids: BTreeSet<&AssetId> = self.inputs.values().filter_map(|input| input.id.as_ref()).collect(); for id in known_ids { - writeln!(file, "rbxassetid://{}", id)?; + writeln!(file, "{}", id.to_string())?; } file.flush()?; @@ -623,7 +639,14 @@ impl SyncSession { fs_err::create_dir_all(&cache_path)?; - let known_ids: HashSet = self.inputs.values().filter_map(|input| input.id).collect(); + let known_ids: HashSet = self.inputs.values() + .filter_map(|input| input.id.as_ref().and_then(|asset_id| { + match asset_id { + AssetId::Id(id) => Some(*id), + _ => None, + } + })) + .collect(); // Clean up cache items that aren't present in our current project. for entry in fs_err::read_dir(&cache_path)? { @@ -659,7 +682,7 @@ impl SyncSession { } for input in self.inputs.values() { - if let Some(id) = input.id { + if let Some(id) = &input.id { let input_path = cache_path.join(format!("{}", id)); match fs_err::metadata(&input_path) { @@ -674,10 +697,15 @@ impl SyncSession { } } - log::debug!("Downloading asset ID {}", id); + match id { + AssetId::Id(id) => { + log::debug!("Downloading asset ID {}", id); - let contents = api_client.download_image(id)?; - fs_err::write(input_path, contents)?; + let contents = api_client.download_image(*id)?; + fs_err::write(input_path, contents)?; + } + _ => {} + } } } diff --git a/src/data/sync.rs b/src/data/sync.rs index 6832e4c..d9ea9c7 100644 --- a/src/data/sync.rs +++ b/src/data/sync.rs @@ -1,10 +1,33 @@ -use std::path::PathBuf; +use std::{fmt, path::PathBuf}; use crate::{ asset_name::AssetName, data::{ImageSlice, InputConfig, InputManifest}, }; +use path_slash::PathBufExt; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum AssetId { + Id(u64), + Path(PathBuf), +} + +impl AssetId { + pub fn to_string(&self) -> String { + match &self { + Self::Id(id) => format!("rbxassetid://{}", id), + Self::Path(path) => format!("rbxasset://{}", path.to_slash().expect("error while converting path to slash")), + } + } +} + +impl fmt::Display for AssetId { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "{}", self.to_string()) + } +} + /// In-memory representation of a Tarmac Input during the sync process. /// /// SyncInput structs are gradually created and filled in from the filesystem, @@ -35,7 +58,7 @@ pub struct SyncInput { /// If this input has been part of an upload to Roblox.com, contains the /// asset ID that contains the data from this input. - pub id: Option, + pub id: Option, /// If this input has been packed into a spritesheet, contains the slice of /// the spritesheet that this input is located in. diff --git a/src/options.rs b/src/options.rs index cb84d5e..3cb166d 100644 --- a/src/options.rs +++ b/src/options.rs @@ -68,6 +68,8 @@ pub struct SyncOptions { /// unsynced assets. /// /// - debug: Copy to local debug directory for debugging output + /// + /// - local: Copy to locally installed Roblox content folder. #[structopt(long)] pub target: SyncTarget, @@ -89,6 +91,7 @@ pub enum SyncTarget { Roblox, None, Debug, + Local, } impl FromStr for SyncTarget { @@ -99,9 +102,10 @@ impl FromStr for SyncTarget { "roblox" => Ok(SyncTarget::Roblox), "none" => Ok(SyncTarget::None), "debug" => Ok(SyncTarget::Debug), + "local" => Ok(SyncTarget::Local), _ => Err(String::from( - "Invalid sync target. Valid options are roblox, none, and debug.", + "Invalid sync target. Valid options are roblox, local, none, and debug.", )), } } diff --git a/src/sync_backend.rs b/src/sync_backend.rs index 03e8e83..440c941 100644 --- a/src/sync_backend.rs +++ b/src/sync_backend.rs @@ -1,9 +1,11 @@ -use std::{borrow::Cow, io, path::Path, thread, time::Duration}; +use std::{borrow::Cow, io, path::{Path, PathBuf}, thread, time::Duration}; use fs_err as fs; use reqwest::StatusCode; use thiserror::Error; +use roblox_install::RobloxStudio; +use crate::data::AssetId; use crate::roblox_web_api::{ImageUploadData, RobloxApiClient, RobloxApiError}; pub trait SyncBackend { @@ -12,7 +14,7 @@ pub trait SyncBackend { #[derive(Clone, Debug, PartialEq, Eq)] pub struct UploadResponse { - pub id: u64, + pub id: AssetId, } #[derive(Clone, Debug)] @@ -58,7 +60,7 @@ impl<'a> SyncBackend for RobloxSyncBackend<'a> { ); Ok(UploadResponse { - id: response.backing_asset_id, + id: AssetId::Id(response.backing_asset_id), }) } @@ -72,6 +74,59 @@ impl<'a> SyncBackend for RobloxSyncBackend<'a> { } } +pub struct LocalSyncBackend { + content_path: PathBuf, + scope: Option, +} + +impl LocalSyncBackend { + pub fn new(scope: Option) -> Result { + RobloxStudio::locate() + .map(|studio| { + + LocalSyncBackend { + content_path: studio.content_path().into(), + scope, + } + }) + .map_err(|error| error.into()) + } + + fn get_asset_path(&self, data: &UploadInfo) -> PathBuf { + let mut path = PathBuf::from(".tarmac"); + if let Some(scope) = &self.scope { + path.push(scope); + } + path.push(self.get_asset_file_name(data)); + path + } + + fn get_asset_file_name(&self, data: &UploadInfo) -> String { + format!("{}.png", data.name) + } +} + +impl SyncBackend for LocalSyncBackend { + fn upload(&mut self, data: UploadInfo) -> Result { + let asset_path = self.get_asset_path(&data); + let file_path = self.content_path.join(&asset_path); + let parent = file_path.parent().expect("content folder should have a parent"); + + fs::create_dir_all(parent)?; + fs::write(&file_path, &data.contents)?; + + log::info!( + "Written {} to path {}", + &data.name, + file_path.display() + ); + + Ok(UploadResponse { + id: AssetId::Path(asset_path), + }) + } +} + pub struct NoneSyncBackend; impl SyncBackend for NoneSyncBackend { @@ -103,7 +158,7 @@ impl SyncBackend for DebugSyncBackend { let file_path = path.join(id.to_string()); fs::write(&file_path, &data.contents)?; - Ok(UploadResponse { id }) + Ok(UploadResponse { id: AssetId::Id(id) }) } } @@ -160,6 +215,12 @@ pub enum Error { #[error("Tarmac was rate-limited trying to upload assets. Try again in a little bit.")] RateLimited, + #[error(transparent)] + StudioInstall { + #[from] + source: roblox_install::Error, + }, + #[error(transparent)] Io { #[from] @@ -249,7 +310,7 @@ mod test { #[test] fn upload_returns_first_success_result() { let mut counter = 0; - let success = UploadResponse { id: 10 }; + let success = UploadResponse { id: AssetId::Id(10) }; let inner = CountUploads::new(&mut counter).with_results(vec![ Err(Error::RateLimited), Err(Error::RateLimited), From 49a5a6b8289e5cb8ecb4afc03c8b236566be8daa Mon Sep 17 00:00:00 2001 From: jeparlefrancais Date: Thu, 3 Dec 2020 01:41:38 -0500 Subject: [PATCH 3/5] run cargo fmt --- src/alpha_bleed.rs | 22 ++++++++++------------ src/commands/sync.rs | 34 +++++++++++++++++++--------------- src/data/sync.rs | 22 ++++++++++++---------- src/sync_backend.rs | 37 +++++++++++++++++++++---------------- 4 files changed, 62 insertions(+), 53 deletions(-) diff --git a/src/alpha_bleed.rs b/src/alpha_bleed.rs index cd30807..63fa610 100644 --- a/src/alpha_bleed.rs +++ b/src/alpha_bleed.rs @@ -27,18 +27,16 @@ pub(crate) fn alpha_bleed(image: &mut Image) { // An iterator of in-bounds positions adjacent to the given one. let adjacent_positions = |x, y| { - DIRECTIONS - .into_iter() - .filter_map(move |(x_offset, y_offset)| { - let x_source = (x as i32) + x_offset; - let y_source = (y as i32) + y_offset; - - if x_source < 0 || y_source < 0 || x_source >= w as i32 || y_source >= h as i32 { - return None; - } - - Some((x_source as u32, y_source as u32)) - }) + DIRECTIONS.iter().filter_map(move |(x_offset, y_offset)| { + let x_source = (x as i32) + x_offset; + let y_source = (y as i32) + y_offset; + + if x_source < 0 || y_source < 0 || x_source >= w as i32 || y_source >= h as i32 { + return None; + } + + Some((x_source as u32, y_source as u32)) + }) }; // Populate the set of initial positions to visit as well as positions that diff --git a/src/commands/sync.rs b/src/commands/sync.rs index ace54f3..3d367be 100644 --- a/src/commands/sync.rs +++ b/src/commands/sync.rs @@ -16,7 +16,9 @@ use crate::{ asset_name::AssetName, auth_cookie::get_auth_cookie, codegen::perform_codegen, - data::{AssetId, Config, ConfigError, ImageSlice, InputManifest, Manifest, ManifestError, SyncInput}, + data::{ + AssetId, Config, ConfigError, ImageSlice, InputManifest, Manifest, ManifestError, SyncInput, + }, dpi_scale, image::Image, options::{GlobalOptions, SyncOptions, SyncTarget}, @@ -265,9 +267,7 @@ impl SyncSession { // If this input was known during the last sync operation, // pull the information we knew about it out. let (id, slice) = match self.original_manifest.inputs.get(&name) { - Some(original) => { - (original.id.map(AssetId::Id), original.slice) - } + Some(original) => (original.id.map(AssetId::Id), original.slice), None => (None, None), }; @@ -548,11 +548,9 @@ impl SyncSession { .inputs .iter() .map(|(name, input)| { - let id = input.id.as_ref().and_then(|asset_id| { - match asset_id { - AssetId::Id(id) => Some(*id), - _ => None, - } + let id = input.id.as_ref().and_then(|asset_id| match asset_id { + AssetId::Id(id) => Some(*id), + _ => None, }); ( name.clone(), @@ -619,7 +617,11 @@ impl SyncSession { let mut file = BufWriter::new(fs_err::File::create(list_path)?); - let known_ids: BTreeSet<&AssetId> = self.inputs.values().filter_map(|input| input.id.as_ref()).collect(); + let known_ids: BTreeSet<&AssetId> = self + .inputs + .values() + .filter_map(|input| input.id.as_ref()) + .collect(); for id in known_ids { writeln!(file, "{}", id.to_string())?; @@ -639,13 +641,15 @@ impl SyncSession { fs_err::create_dir_all(&cache_path)?; - let known_ids: HashSet = self.inputs.values() - .filter_map(|input| input.id.as_ref().and_then(|asset_id| { - match asset_id { + let known_ids: HashSet = self + .inputs + .values() + .filter_map(|input| { + input.id.as_ref().and_then(|asset_id| match asset_id { AssetId::Id(id) => Some(*id), _ => None, - } - })) + }) + }) .collect(); // Clean up cache items that aren't present in our current project. diff --git a/src/data/sync.rs b/src/data/sync.rs index d9ea9c7..526b54a 100644 --- a/src/data/sync.rs +++ b/src/data/sync.rs @@ -13,18 +13,20 @@ pub enum AssetId { Path(PathBuf), } -impl AssetId { - pub fn to_string(&self) -> String { - match &self { - Self::Id(id) => format!("rbxassetid://{}", id), - Self::Path(path) => format!("rbxasset://{}", path.to_slash().expect("error while converting path to slash")), - } - } -} - impl fmt::Display for AssetId { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "{}", self.to_string()) + write!( + formatter, + "{}", + match &self { + Self::Id(id) => format!("rbxassetid://{}", id), + Self::Path(path) => format!( + "rbxasset://{}", + path.to_slash() + .expect("error while converting path to slash") + ), + } + ) } } diff --git a/src/sync_backend.rs b/src/sync_backend.rs index 440c941..ec6c329 100644 --- a/src/sync_backend.rs +++ b/src/sync_backend.rs @@ -1,9 +1,15 @@ -use std::{borrow::Cow, io, path::{Path, PathBuf}, thread, time::Duration}; +use std::{ + borrow::Cow, + io, + path::{Path, PathBuf}, + thread, + time::Duration, +}; use fs_err as fs; use reqwest::StatusCode; -use thiserror::Error; use roblox_install::RobloxStudio; +use thiserror::Error; use crate::data::AssetId; use crate::roblox_web_api::{ImageUploadData, RobloxApiClient, RobloxApiError}; @@ -82,12 +88,9 @@ pub struct LocalSyncBackend { impl LocalSyncBackend { pub fn new(scope: Option) -> Result { RobloxStudio::locate() - .map(|studio| { - - LocalSyncBackend { - content_path: studio.content_path().into(), - scope, - } + .map(|studio| LocalSyncBackend { + content_path: studio.content_path().into(), + scope, }) .map_err(|error| error.into()) } @@ -110,16 +113,14 @@ impl SyncBackend for LocalSyncBackend { fn upload(&mut self, data: UploadInfo) -> Result { let asset_path = self.get_asset_path(&data); let file_path = self.content_path.join(&asset_path); - let parent = file_path.parent().expect("content folder should have a parent"); + let parent = file_path + .parent() + .expect("content folder should have a parent"); fs::create_dir_all(parent)?; fs::write(&file_path, &data.contents)?; - log::info!( - "Written {} to path {}", - &data.name, - file_path.display() - ); + log::info!("Written {} to path {}", &data.name, file_path.display()); Ok(UploadResponse { id: AssetId::Path(asset_path), @@ -158,7 +159,9 @@ impl SyncBackend for DebugSyncBackend { let file_path = path.join(id.to_string()); fs::write(&file_path, &data.contents)?; - Ok(UploadResponse { id: AssetId::Id(id) }) + Ok(UploadResponse { + id: AssetId::Id(id), + }) } } @@ -310,7 +313,9 @@ mod test { #[test] fn upload_returns_first_success_result() { let mut counter = 0; - let success = UploadResponse { id: AssetId::Id(10) }; + let success = UploadResponse { + id: AssetId::Id(10), + }; let inner = CountUploads::new(&mut counter).with_results(vec![ Err(Error::RateLimited), Err(Error::RateLimited), From 310e84aa5f4e492a359c7c200738258b7728672a Mon Sep 17 00:00:00 2001 From: jeparlefrancais Date: Thu, 3 Dec 2020 01:47:06 -0500 Subject: [PATCH 4/5] fix clippy warnings --- src/commands/create_cache_map.rs | 2 +- src/commands/sync.rs | 13 +++++-------- src/commands/upload_image.rs | 1 - src/roblox_web_api.rs | 2 +- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/commands/create_cache_map.rs b/src/commands/create_cache_map.rs index 798f307..ff68c52 100644 --- a/src/commands/create_cache_map.rs +++ b/src/commands/create_cache_map.rs @@ -16,7 +16,7 @@ pub fn create_cache_map( let mut api_client = RobloxApiClient::new(global.auth); let project_path = match options.project_path { - Some(path) => path.clone(), + Some(path) => path, None => env::current_dir()?, }; diff --git a/src/commands/sync.rs b/src/commands/sync.rs index 3d367be..4458a5f 100644 --- a/src/commands/sync.rs +++ b/src/commands/sync.rs @@ -460,7 +460,7 @@ impl SyncSession { let upload_data = UploadInfo { name: "spritesheet".to_owned(), contents: encoded_image, - hash: hash.clone(), + hash, }; let id = backend.upload(upload_data)?.id; @@ -701,14 +701,11 @@ impl SyncSession { } } - match id { - AssetId::Id(id) => { - log::debug!("Downloading asset ID {}", id); + if let AssetId::Id(id) = id { + log::debug!("Downloading asset ID {}", id); - let contents = api_client.download_image(*id)?; - fs_err::write(input_path, contents)?; - } - _ => {} + let contents = api_client.download_image(*id)?; + fs_err::write(input_path, contents)?; } } } diff --git a/src/commands/upload_image.rs b/src/commands/upload_image.rs index 984e48f..251943e 100644 --- a/src/commands/upload_image.rs +++ b/src/commands/upload_image.rs @@ -11,7 +11,6 @@ use crate::{ pub fn upload_image(global: GlobalOptions, options: UploadImageOptions) { let auth = global .auth - .clone() .or_else(get_auth_cookie) .expect("no auth cookie found"); diff --git a/src/roblox_web_api.rs b/src/roblox_web_api.rs index 258294c..a834a7f 100644 --- a/src/roblox_web_api.rs +++ b/src/roblox_web_api.rs @@ -108,7 +108,7 @@ impl RobloxApiClient { ..data }; - return self.upload_image(new_data); + self.upload_image(new_data) } else { Err(RobloxApiError::ApiError { message }) } From cea7422528ef97bb7dc2b5d3821c5f68eb4bdf8b Mon Sep 17 00:00:00 2001 From: jeparlefrancais Date: Thu, 3 Dec 2020 01:49:25 -0500 Subject: [PATCH 5/5] add main branch to ci --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e4320b..e08dde9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,10 +3,12 @@ name: CI on: push: branches: + - main - master pull_request: branches: + - main - master jobs: