diff --git a/Cargo.lock b/Cargo.lock index 4ff7b9d..0ef9a19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,9 +43,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.14" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", @@ -58,33 +58,33 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -98,9 +98,9 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arrayref" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" [[package]] name = "arrayvec" @@ -110,7 +110,7 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "asset-tool" -version = "0.4.4" +version = "0.4.5" dependencies = [ "anyhow", "clap", @@ -181,9 +181,9 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "blake3" -version = "1.5.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52" +checksum = "e9ec96fe9a81b5e365f9db71fe00edc4fe4ca2cc7dcb7861f0603012a7caa210" dependencies = [ "arrayref", "arrayvec", @@ -206,19 +206,19 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cc" -version = "1.0.104" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490" +checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" dependencies = [ "jobserver", "libc", - "once_cell", + "shlex", ] [[package]] @@ -239,14 +239,14 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] name = "clap" -version = "4.5.8" +version = "4.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" +checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" dependencies = [ "clap_builder", "clap_derive", @@ -254,9 +254,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.8" +version = "4.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" +checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" dependencies = [ "anstream", "anstyle", @@ -266,9 +266,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.8" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ "heck", "proc-macro2", @@ -278,15 +278,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "constant_time_eq" @@ -306,9 +306,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "crc32fast" @@ -383,9 +383,9 @@ checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" dependencies = [ "crc32fast", "miniz_oxide", @@ -592,9 +592,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", @@ -621,9 +621,9 @@ checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "hyper" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4fe55fb7a772d59a5ff1dfbff4fe0258d19b89fec4b233e75d35d5d2316badc" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" dependencies = [ "bytes", "futures-channel", @@ -674,9 +674,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" dependencies = [ "bytes", "futures-channel", @@ -727,9 +727,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" dependencies = [ "equivalent", "hashbrown", @@ -743,9 +743,9 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is_terminal_polyfill" -version = "1.70.0" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" @@ -755,27 +755,27 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy-regex" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d12be4595afdf58bd19e4a9f4e24187da2a66700786ff660a418e9059937a4c" +checksum = "576c8060ecfdf2e56995cf3274b4f2d71fa5e4fa3607c1c0b63c10180ee58741" dependencies = [ "lazy-regex-proc_macros", "once_cell", @@ -784,9 +784,9 @@ dependencies = [ [[package]] name = "lazy-regex-proc_macros" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bcd58e6c97a7fcbaffcdc95728b393b8d98933bfadad49ed4097845b57ef0b" +checksum = "9efb9e65d4503df81c615dc33ff07042a9408ac7f26b45abee25566f7fbfd12c" dependencies = [ "proc-macro2", "quote", @@ -802,9 +802,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.156" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a" [[package]] name = "libgit2-sys" @@ -836,9 +836,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.18" +version = "1.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" +checksum = "fdc53a7799a7496ebc9fd29f31f7df80e83c9bda5299768af5f9e59eeea74647" dependencies = [ "cc", "libc", @@ -860,9 +860,9 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lz4" -version = "1.25.0" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6eab492fe7f8651add23237ea56dbf11b3c4ff762ab83d40a47f11433421f91" +checksum = "958b4caa893816eea05507c20cfe47574a43d9a697138a7872990bba8a0ece68" dependencies = [ "libc", "lz4-sys", @@ -870,9 +870,9 @@ dependencies = [ [[package]] name = "lz4-sys" -version = "1.9.5" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9764018d143cc854c9f17f0b907de70f14393b1f502da6375dce70f00514eb3" +checksum = "109de74d5d2353660401699a4174a4ff23fcc649caf553df71933c7fb45ad868" dependencies = [ "cc", "libc", @@ -892,9 +892,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ "mime", "unicase", @@ -911,13 +911,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.11" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -946,21 +947,11 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "object" -version = "0.36.1" +version = "0.36.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" dependencies = [ "memchr", ] @@ -973,9 +964,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ "bitflags 2.6.0", "cfg-if", @@ -1005,9 +996,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -1073,9 +1064,12 @@ checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "proc-macro2" @@ -1178,9 +1172,9 @@ dependencies = [ [[package]] name = "rbx_binary" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6314dd6bf5c21d0598cdb53cf5d241aa643ba41da8b8abf7402b4a35096f03f6" +checksum = "49ee5134b59834b17940d20dd2e057b6fe6902c1e566900e83106c50503b970e" dependencies = [ "log", "lz4", @@ -1193,9 +1187,9 @@ dependencies = [ [[package]] name = "rbx_dom_weak" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b67b56bac99849c2e3c57547b036927f71c57cf7f4d900d04e3e4ee774ec316" +checksum = "34d35df0f09290d32976f655366342676a6645b87c39b6949473b9d28a969733" dependencies = [ "rbx_types", "serde", @@ -1203,9 +1197,9 @@ dependencies = [ [[package]] name = "rbx_reflection" -version = "4.5.0" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d41509c991b53a7276a746a795eae2b9204f398164920f61976995b47fe1722" +checksum = "04ca5496737668378b17bacc9090ad361fc9c8b5f346bbd33162e083c98fa248" dependencies = [ "rbx_types", "serde", @@ -1214,9 +1208,9 @@ dependencies = [ [[package]] name = "rbx_reflection_database" -version = "0.2.10+roblox-607" +version = "0.2.11+roblox-634" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12e20c06fa41f7aadc79005c8354f592b2c2f4d0c61e1080ed5718dafc30aea0" +checksum = "399ab2e1fa27c8428fe43fc4148d8085d187881f1c59cefea3711a2112e9cccc" dependencies = [ "lazy_static", "rbx_reflection", @@ -1226,9 +1220,9 @@ dependencies = [ [[package]] name = "rbx_types" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca23bfd469d067d81ef14f65fe09aeddc25abcf576a889d1a7664fe021cf18c" +checksum = "6ed7bbc0e1864143546b12ee0cf64a1a6f447d8ce7baf4fae755e4581929d230" dependencies = [ "base64 0.13.1", "bitflags 1.3.2", @@ -1241,9 +1235,9 @@ dependencies = [ [[package]] name = "rbx_xml" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c03f95500961c32340791d1fabd4587f6873bdbff077ecca6ae32db7960dea" +checksum = "1c2abac6e71c97a56243f00c9c2def504fe4b698019d854dd8720da700a80d7c" dependencies = [ "base64 0.13.1", "log", @@ -1255,9 +1249,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.5" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -1396,9 +1390,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.10" +version = "0.23.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" dependencies = [ "once_cell", "rustls-pki-types", @@ -1409,9 +1403,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" dependencies = [ "base64 0.22.1", "rustls-pki-types", @@ -1419,15 +1413,15 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" [[package]] name = "rustls-webpki" -version = "0.102.4" +version = "0.102.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" dependencies = [ "ring", "rustls-pki-types", @@ -1451,9 +1445,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags 2.6.0", "core-foundation", @@ -1464,9 +1458,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" dependencies = [ "core-foundation-sys", "libc", @@ -1474,18 +1468,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.203" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" dependencies = [ "proc-macro2", "quote", @@ -1494,11 +1488,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -1515,6 +1510,12 @@ dependencies = [ "serde", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "slab" version = "0.4.9" @@ -1560,9 +1561,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.68" +version = "2.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" dependencies = [ "proc-macro2", "quote", @@ -1598,30 +1599,31 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", @@ -1630,9 +1632,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -1645,26 +1647,25 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.38.0" +version = "1.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "pin-project-lite", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", @@ -1722,15 +1723,15 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -1818,9 +1819,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "want" @@ -1839,19 +1840,20 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", @@ -1864,9 +1866,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" dependencies = [ "cfg-if", "js-sys", @@ -1876,9 +1878,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1886,9 +1888,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", @@ -1899,15 +1901,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -1919,7 +1921,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -1937,7 +1939,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -1957,18 +1968,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -1979,9 +1990,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -1991,9 +2002,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -2003,15 +2014,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -2021,9 +2032,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -2033,9 +2044,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -2045,9 +2056,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -2057,9 +2068,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winreg" @@ -2073,9 +2084,30 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" +checksum = "539a77ee7c0de333dcc6da69b177380a0b81e0dacfa4f7344c465a36871ee601" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "zeroize" diff --git a/Cargo.toml b/Cargo.toml index de0b750..b6834b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ workspace = { members = ["rbx_asset", "rox_compiler"] } [package] name = "asset-tool" -version = "0.4.4" +version = "0.4.5" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/rbx_asset/src/cloud.rs b/rbx_asset/src/cloud.rs index 4206497..bf8a7a1 100644 --- a/rbx_asset/src/cloud.rs +++ b/rbx_asset/src/cloud.rs @@ -14,9 +14,33 @@ pub struct CreateAssetRequest{ pub displayName:String, } #[derive(Debug)] +pub enum AssetOperationError{ + Operation(OperationError), + Serialize(serde_json::Error), +} +impl std::fmt::Display for AssetOperationError{ + fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ + write!(f,"{self:?}") + } +} +impl std::error::Error for AssetOperationError{} +#[derive(Debug)] +pub struct AssetOperation{ + operation:RobloxOperation, +} +impl AssetOperation{ + pub async fn try_get_asset(&self,context:&CloudContext)->Result{ + serde_json::from_value( + self.operation + .try_get_reponse(context).await + .map_err(AssetOperationError::Operation)? + ).map_err(AssetOperationError::Serialize) + } +} +#[derive(Debug)] pub enum CreateError{ - ParseError(url::ParseError), - SerializeError(serde_json::Error), + Parse(url::ParseError), + Serialize(serde_json::Error), Reqwest(reqwest::Error), } impl std::fmt::Display for CreateError{ @@ -35,7 +59,7 @@ pub struct UpdateAssetRequest{ } //woo nested roblox stuff -#[derive(Debug,serde::Deserialize,serde::Serialize)] +#[derive(Clone,Debug,serde::Deserialize,serde::Serialize)] #[allow(nonstandard_style,dead_code)] pub enum Creator{ userId(String),//u64 string @@ -82,13 +106,15 @@ pub enum UpdateError{ Reqwest(reqwest::Error), } impl std::fmt::Display for UpdateError{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ write!(f,"{self:?}") } } impl std::error::Error for UpdateError{} -#[allow(nonstandard_style,dead_code)] +struct GetAssetOperationRequest{ + operation_id:String, +} pub struct GetAssetInfoRequest{ pub asset_id:u64, } @@ -145,7 +171,7 @@ pub enum GetError{ IO(std::io::Error) } impl std::fmt::Display for GetError{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ write!(f,"{self:?}") } } @@ -180,7 +206,7 @@ pub enum AssetVersionsError{ Reqwest(reqwest::Error), } impl std::fmt::Display for AssetVersionsError{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ write!(f,"{self:?}") } } @@ -215,20 +241,52 @@ pub enum InventoryPageError{ Reqwest(reqwest::Error), } impl std::fmt::Display for InventoryPageError{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ write!(f,"{self:?}") } } impl std::error::Error for InventoryPageError{} +#[derive(Debug)] +pub enum OperationError{ + Get(GetError), + NoOperationId, + NotDone, +} +impl std::fmt::Display for OperationError{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f,"{self:?}") + } +} +impl std::error::Error for OperationError{} #[derive(Debug,serde::Deserialize,serde::Serialize)] #[allow(nonstandard_style,dead_code)] -pub struct RobloxOperation{ - pub path:Option, +struct RobloxOperation{ + pub path:Option, pub metadata:Option, pub done:Option, pub error:Option, - pub response:Option, + pub response:Option, + pub operationId:Option, +} +impl RobloxOperation{ + pub fn operation_id(&self)->Option<&str>{ + match self.operationId.as_deref(){ + //try getting it from undocumented operationId first + Some(operation_id)=>Some(operation_id), + //skip the first 11 characters + //operations/[uuid] + None=>self.path.as_deref()?.get(11..), + } + } + pub async fn try_get_reponse(&self,context:&CloudContext)->Result{ + context.get_asset_operation(GetAssetOperationRequest{ + operation_id:self.operation_id() + .ok_or(OperationError::NoOperationId)? + .to_owned(), + }).await.map_err(OperationError::Get)? + .response.ok_or(OperationError::NotDone) + } } //idk how to do this better @@ -297,21 +355,29 @@ impl CloudContext{ .multipart(form) .send().await } - pub async fn create_asset(&self,config:CreateAssetRequest,body:impl Into>)->Result{ - let url=reqwest::Url::parse("https://apis.roblox.com/assets/v1/assets").map_err(CreateError::ParseError)?; + pub async fn create_asset(&self,config:CreateAssetRequest,body:impl Into>)->Result{ + let url=reqwest::Url::parse("https://apis.roblox.com/assets/v1/assets").map_err(CreateError::Parse)?; - let request_config=serde_json::to_string(&config).map_err(CreateError::SerializeError)?; + let request_config=serde_json::to_string(&config).map_err(CreateError::Serialize)?; + + let part=reqwest::multipart::Part::bytes(body) + //you must have a file name or roblox will 400!!!!!!!!! + .file_name("image"); let form=reqwest::multipart::Form::new() .text("request",request_config) - .part("fileContent",reqwest::multipart::Part::bytes(body)); + .part("fileContent",part); - let resp=self.post_form(url,form).await.map_err(CreateError::Reqwest)? - .error_for_status().map_err(CreateError::Reqwest)?; + let operation=self.post_form(url,form).await + .map_err(CreateError::Reqwest)? + .error_for_status().map_err(CreateError::Reqwest)? + .json::().await.map_err(CreateError::Reqwest)?; - Ok(resp.json::().await.map_err(CreateError::Reqwest)?) + Ok(AssetOperation{ + operation, + }) } - pub async fn update_asset(&self,config:UpdateAssetRequest,body:impl Into>)->Result{ + pub async fn update_asset(&self,config:UpdateAssetRequest,body:impl Into>)->Result{ let raw_url=format!("https://apis.roblox.com/assets/v1/assets/{}",config.assetId); let url=reqwest::Url::parse(raw_url.as_str()).map_err(UpdateError::ParseError)?; @@ -321,20 +387,31 @@ impl CloudContext{ .text("request",request_config) .part("fileContent",reqwest::multipart::Part::bytes(body)); - let resp=self.patch_form(url,form).await + let operation=self.patch_form(url,form).await .map_err(UpdateError::Reqwest)? //roblox api documentation is very poor, just give the status code and drop the json - .error_for_status().map_err(UpdateError::Reqwest)?; + .error_for_status().map_err(UpdateError::Reqwest)? + .json::().await.map_err(UpdateError::Reqwest)?; - Ok(resp.json::().await.map_err(UpdateError::Reqwest)?) + Ok(AssetOperation{ + operation, + }) + } + async fn get_asset_operation(&self,config:GetAssetOperationRequest)->Result{ + let raw_url=format!("https://apis.roblox.com/assets/v1/operations/{}",config.operation_id); + let url=reqwest::Url::parse(raw_url.as_str()).map_err(GetError::ParseError)?; + + self.get(url).await.map_err(GetError::Reqwest)? + .error_for_status().map_err(GetError::Reqwest)? + .json::().await.map_err(GetError::Reqwest) } pub async fn get_asset_info(&self,config:GetAssetInfoRequest)->Result{ let raw_url=format!("https://apis.roblox.com/assets/v1/assets/{}",config.asset_id); let url=reqwest::Url::parse(raw_url.as_str()).map_err(GetError::ParseError)?; - Ok(self.get(url).await.map_err(GetError::Reqwest)? + self.get(url).await.map_err(GetError::Reqwest)? .error_for_status().map_err(GetError::Reqwest)? - .json::().await.map_err(GetError::Reqwest)?) + .json::().await.map_err(GetError::Reqwest) } pub async fn get_asset_version(&self,config:GetAssetVersionRequest)->Result,GetError>{ let raw_url=format!("https://apis.roblox.com/assets/v1/assets/{}/versions/{}",config.asset_id,config.version); @@ -364,9 +441,9 @@ impl CloudContext{ let raw_url=format!("https://apis.roblox.com/assets/v1/assets/{}/versions",config.asset_id); let url=reqwest::Url::parse(raw_url.as_str()).map_err(AssetVersionsError::ParseError)?; - Ok(self.get(url).await.map_err(AssetVersionsError::Reqwest)? + self.get(url).await.map_err(AssetVersionsError::Reqwest)? .error_for_status().map_err(AssetVersionsError::Reqwest)? - .json::().await.map_err(AssetVersionsError::Reqwest)?) + .json::().await.map_err(AssetVersionsError::Reqwest) } pub async fn inventory_page(&self,config:InventoryPageRequest)->Result{ let mut url=reqwest::Url::parse(format!("https://apis.roblox.com/toolbox-service/v1/creations/group/{}/10?limit=50",config.group).as_str()).map_err(InventoryPageError::ParseError)?; @@ -378,9 +455,9 @@ impl CloudContext{ } } - Ok(self.get(url).await.map_err(InventoryPageError::Reqwest)? + self.get(url).await.map_err(InventoryPageError::Reqwest)? .error_for_status().map_err(InventoryPageError::Reqwest)? - .json::().await.map_err(InventoryPageError::Reqwest)?) + .json::().await.map_err(InventoryPageError::Reqwest) } pub async fn update_place(&self,config:UpdatePlaceRequest,body:impl Into+Clone)->Result{ let raw_url=format!("https://apis.roblox.com/universes/v1/{}/places/{}/versions",config.universeId,config.placeId); @@ -391,8 +468,8 @@ impl CloudContext{ query.append_pair("versionType","Published"); } - Ok(self.post(url,body).await.map_err(UpdateError::Reqwest)? + self.post(url,body).await.map_err(UpdateError::Reqwest)? .error_for_status().map_err(UpdateError::Reqwest)? - .json::().await.map_err(UpdateError::Reqwest)?) + .json::().await.map_err(UpdateError::Reqwest) } } diff --git a/rbx_asset/src/cookie.rs b/rbx_asset/src/cookie.rs index b0c8e72..3557c7d 100644 --- a/rbx_asset/src/cookie.rs +++ b/rbx_asset/src/cookie.rs @@ -228,15 +228,14 @@ impl CookieContext{ query.append_pair("description",config.description.as_str()); query.append_pair("ispublic",if config.ispublic{"True"}else{"False"}); query.append_pair("allowComments",if config.allowComments{"True"}else{"False"}); - match config.groupId{ - Some(group_id)=>{query.append_pair("groupId",group_id.to_string().as_str());}, - None=>(), + if let Some(group_id)=config.groupId{ + query.append_pair("groupId",group_id.to_string().as_str()); } } - Ok(self.post(url,body).await.map_err(CreateError::PostError)? + self.post(url,body).await.map_err(CreateError::PostError)? .error_for_status().map_err(CreateError::Reqwest)? - .json::().await.map_err(CreateError::Reqwest)?) + .json::().await.map_err(CreateError::Reqwest) } pub async fn upload(&self,config:UploadRequest,body:impl Into+Clone)->Result{ let mut url=reqwest::Url::parse("https://data.roblox.com/Data/Upload.ashx?json=1&type=Model&genreTypeId=1").map_err(UploadError::ParseError)?; @@ -265,9 +264,9 @@ impl CookieContext{ } } - Ok(self.post(url,body).await.map_err(UploadError::PostError)? + self.post(url,body).await.map_err(UploadError::PostError)? .error_for_status().map_err(UploadError::Reqwest)? - .json::().await.map_err(UploadError::Reqwest)?) + .json::().await.map_err(UploadError::Reqwest) } pub async fn get_asset(&self,config:GetAssetRequest)->Result,GetError>{ let mut url=reqwest::Url::parse("https://assetdelivery.roblox.com/v1/asset/").map_err(GetError::ParseError)?; @@ -302,9 +301,9 @@ impl CookieContext{ } } - Ok(self.get(url).await.map_err(AssetVersionsPageError::Reqwest)? + self.get(url).await.map_err(AssetVersionsPageError::Reqwest)? .error_for_status().map_err(AssetVersionsPageError::Reqwest)? - .json::().await.map_err(AssetVersionsPageError::Reqwest)?) + .json::().await.map_err(AssetVersionsPageError::Reqwest) } pub async fn get_inventory_page(&self,config:InventoryPageRequest)->Result{ let mut url=reqwest::Url::parse(format!("https://apis.roblox.com/toolbox-service/v1/creations/group/{}/10?limit=50",config.group).as_str()).map_err(InventoryPageError::ParseError)?; @@ -316,8 +315,8 @@ impl CookieContext{ } } - Ok(self.get(url).await.map_err(InventoryPageError::Reqwest)? + self.get(url).await.map_err(InventoryPageError::Reqwest)? .error_for_status().map_err(InventoryPageError::Reqwest)? - .json::().await.map_err(InventoryPageError::Reqwest)?) + .json::().await.map_err(InventoryPageError::Reqwest) } } diff --git a/rox_compiler/src/common.rs b/rox_compiler/src/common.rs index 3183e4d..2df4074 100644 --- a/rox_compiler/src/common.rs +++ b/rox_compiler/src/common.rs @@ -28,6 +28,6 @@ impl std::fmt::Display for PropertiesOverride{ } } -pub(crate) fn sanitize<'a>(s:&'a str)->std::borrow::Cow<'a,str>{ +pub(crate) fn sanitize(s:&str)->std::borrow::Cow<'_,str>{ lazy_regex::regex!(r"[^A-Za-z0-9.-]").replace_all(s,"_") } diff --git a/rox_compiler/src/compile.rs b/rox_compiler/src/compile.rs index 8fde920..ffa1c21 100644 --- a/rox_compiler/src/compile.rs +++ b/rox_compiler/src/compile.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::path::{Path,PathBuf}; use futures::{StreamExt, TryStreamExt}; use tokio::io::AsyncReadExt; @@ -55,9 +55,9 @@ struct QuerySingle{ script:QueryHandle, } impl QuerySingle{ - fn rox(search_path:&PathBuf,search_name:&str)->Self{ + fn rox(search_path:&Path,search_name:&str)->Self{ Self{ - script:tokio::spawn(get_file_async(search_path.clone(),format!("{}.lua",search_name))) + script:tokio::spawn(get_file_async(search_path.to_owned(),format!("{}.lua",search_name))) } } } @@ -76,7 +76,7 @@ struct QueryTriple{ client:QueryHandle, } impl QueryTriple{ - fn rox_rojo(search_path:&PathBuf,search_name:&str,search_module:bool)->Self{ + fn rox_rojo(search_path:&Path,search_name:&str,search_module:bool)->Self{ //this should be implemented as constructors of Triplet and Quadruplet to fully support Trey's suggestion let module_name=if search_module{ format!("{}.module.lua",search_name) @@ -84,12 +84,12 @@ impl QueryTriple{ format!("{}.lua",search_name) }; Self{ - module:tokio::spawn(get_file_async(search_path.clone(),module_name)), - server:tokio::spawn(get_file_async(search_path.clone(),format!("{}.server.lua",search_name))), - client:tokio::spawn(get_file_async(search_path.clone(),format!("{}.client.lua",search_name))), + module:tokio::spawn(get_file_async(search_path.to_owned(),module_name)), + server:tokio::spawn(get_file_async(search_path.to_owned(),format!("{}.server.lua",search_name))), + client:tokio::spawn(get_file_async(search_path.to_owned(),format!("{}.client.lua",search_name))), } } - fn rojo(search_path:&PathBuf)->Self{ + fn rojo(search_path:&Path)->Self{ QueryTriple::rox_rojo(search_path,"init",false) } } @@ -146,9 +146,9 @@ impl Query for QueryTriple{ async fn resolve(self)->QueryHintResult{ let (module,server,client)=tokio::join!(self.module,self.server,self.client); mega_triple_join(( - module.map_err(|e|QueryResolveError::JoinError(e))?.map(|file|FileHint{file,hint:ScriptHint::ModuleScript}), - server.map_err(|e|QueryResolveError::JoinError(e))?.map(|file|FileHint{file,hint:ScriptHint::Script}), - client.map_err(|e|QueryResolveError::JoinError(e))?.map(|file|FileHint{file,hint:ScriptHint::LocalScript}), + module.map_err(QueryResolveError::JoinError)?.map(|file|FileHint{file,hint:ScriptHint::ModuleScript}), + server.map_err(QueryResolveError::JoinError)?.map(|file|FileHint{file,hint:ScriptHint::Script}), + client.map_err(QueryResolveError::JoinError)?.map(|file|FileHint{file,hint:ScriptHint::LocalScript}), )) } } @@ -159,7 +159,7 @@ struct QueryQuad{ client:QueryHandle, } impl QueryQuad{ - fn rox_rojo(search_path:&PathBuf,search_name:&str)->Self{ + fn rox_rojo(search_path:&Path,search_name:&str)->Self{ let fill=QueryTriple::rox_rojo(search_path,search_name,true); Self{ module_implicit:QuerySingle::rox(search_path,search_name).script,//Script.lua @@ -173,10 +173,10 @@ impl Query for QueryQuad{ async fn resolve(self)->QueryHintResult{ let (module_implicit,module_explicit,server,client)=tokio::join!(self.module_implicit,self.module_explicit,self.server,self.client); mega_quadruple_join(( - module_implicit.map_err(|e|QueryResolveError::JoinError(e))?.map(|file|FileHint{file,hint:ScriptHint::ModuleScript}), - module_explicit.map_err(|e|QueryResolveError::JoinError(e))?.map(|file|FileHint{file,hint:ScriptHint::ModuleScript}), - server.map_err(|e|QueryResolveError::JoinError(e))?.map(|file|FileHint{file,hint:ScriptHint::Script}), - client.map_err(|e|QueryResolveError::JoinError(e))?.map(|file|FileHint{file,hint:ScriptHint::LocalScript}), + module_implicit.map_err(QueryResolveError::JoinError)?.map(|file|FileHint{file,hint:ScriptHint::ModuleScript}), + module_explicit.map_err(QueryResolveError::JoinError)?.map(|file|FileHint{file,hint:ScriptHint::ModuleScript}), + server.map_err(QueryResolveError::JoinError)?.map(|file|FileHint{file,hint:ScriptHint::Script}), + client.map_err(QueryResolveError::JoinError)?.map(|file|FileHint{file,hint:ScriptHint::LocalScript}), )) } } @@ -338,10 +338,7 @@ impl CompileNode{ .into_string() .map_err(CompileNodeError::FileName)?; //reject goobers - let is_goober=match style{ - Some(Style::Rojo)=>true, - _=>false, - }; + let is_goober=matches!(style,Some(Style::Rojo)); let (ext_len,file_discernment)={ if let Some(captures)=lazy_regex::regex!(r"^.*(\.module\.lua|\.client\.lua|\.server\.lua)$") .captures(file_name.as_str()){ @@ -439,7 +436,7 @@ impl std::error::Error for CompileError{} pub async fn compile(config:CompileConfig,mut dom:&mut rbx_dom_weak::WeakDom)->Result<(),CompileError>{ //hack to traverse root folder as the root object - dom.root_mut().name="src".to_owned(); + "src".clone_into(&mut dom.root_mut().name); //add in scripts and models let mut folder=config.input_folder.clone(); let mut stack:Vec=vec![CompileStackInstruction::TraverseReferent(dom.root_ref(),None)]; @@ -459,9 +456,9 @@ pub async fn compile(config:CompileConfig,mut dom:&mut rbx_dom_weak::WeakDom)->R let mut exist_names:std::collections::HashSet={ let item=dom.get_by_ref(item_ref).ok_or(CompileError::NullChildRef)?; //push existing dom children objects onto stack (unrelated to exist_names) - stack.extend(item.children().into_iter().map(|&referent|CompileStackInstruction::TraverseReferent(referent,None))); + stack.extend(item.children().iter().map(|&referent|CompileStackInstruction::TraverseReferent(referent,None))); //get names of existing objects - item.children().into_iter().map(|&child_ref|{ + item.children().iter().map(|&child_ref|{ let child=dom.get_by_ref(child_ref).ok_or(CompileError::NullChildRef)?; Ok::<_,CompileError>(sanitize(child.name.as_str()).to_string()) }).collect::>()? @@ -478,7 +475,7 @@ pub async fn compile(config:CompileConfig,mut dom:&mut rbx_dom_weak::WeakDom)->R let ret1={ //capture a scoped mutable reference so we can forward dir to the next call even on an error let dir2=&mut dir1; - (||async move{//error catcher so I can use ? + async move{//error catcher so I can use ? let ret2=if let Some(entry)=dir2.next_entry().await?{ //cull early even if supporting things with identical names is possible if exist_names.contains(entry.file_name().to_str().unwrap()){ @@ -490,7 +487,7 @@ pub async fn compile(config:CompileConfig,mut dom:&mut rbx_dom_weak::WeakDom)->R TooComplicated::Stop }; Ok(ret2) - })().await + }.await }; match ret1{ Ok(TooComplicated::Stop)=>None, diff --git a/rox_compiler/src/decompile.rs b/rox_compiler/src/decompile.rs index e0f73da..d800216 100644 --- a/rox_compiler/src/decompile.rs +++ b/rox_compiler/src/decompile.rs @@ -147,10 +147,7 @@ impl DecompiledContext{ "Model"=>Class::Model, _=>Class::Folder, }; - let skip=match class{ - Class::Model=>true, - _=>false, - }; + let skip=class==Class::Model; if let Some(parent_node)=tree_refs.get_mut(&item.parent()){ let referent=item.referent(); let node=TreeNode::new(item.name.clone(),referent,parent_node.referent,class); @@ -182,14 +179,14 @@ impl DecompiledContext{ if node.class==Class::Folder&&script_count!=0{ node.class=Class::Model } - if node.class==Class::Folder&&node.children.len()==0{ + if node.class==Class::Folder&&node.children.is_empty(){ delete=Some(node.parent); }else{ //how the hell do I do this better without recursion - let is_script=match node.class{ - Class::ModuleScript|Class::LocalScript|Class::Script=>true, - _=>false, - }; + let is_script=matches!( + node.class, + Class::ModuleScript|Class::LocalScript|Class::Script + ); //stack is popped from back if is_script{ stack.push(TrimStackInstruction::DecrementScript); @@ -237,7 +234,7 @@ impl DecompiledContext{ WriteStackInstruction::Node(node,name_count)=>{ //track properties that must be overriden to compile folder structure back into a place file let mut properties=PropertiesOverride::default(); - let has_children=node.children.len()!=0; + let has_children=!node.children.is_empty(); match node.class{ Class::Folder=>(), Class::ModuleScript=>(),//.lua files are ModuleScript by default @@ -297,7 +294,7 @@ impl DecompiledContext{ let write_models=config.write_models; let write_scripts=config.write_scripts; let results:Vec>=rayon::iter::ParallelIterator::collect(rayon::iter::ParallelIterator::map(rayon::iter::IntoParallelIterator::into_par_iter(write_queue),|(write_path,node,node_name_override,properties,style)|{ - write_item(&dom,write_path,node,node_name_override,properties,style,write_models,write_scripts) + write_item(dom,write_path,node,node_name_override,properties,style,write_models,write_scripts) })); for result in results{ result?; diff --git a/src/main.rs b/src/main.rs index 6a76fc5..00ab723 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,6 +26,7 @@ enum Commands{ DownloadGroupInventoryJson(DownloadGroupInventoryJsonSubcommand), CreateAsset(CreateAssetSubcommand), CreateAssetMedia(CreateAssetMediaSubcommand), + CreateAssetMedias(CreateAssetMediasSubcommand), UploadAsset(UpdateAssetSubcommand), UploadAssetMedia(UpdateAssetMediaSubcommand), UploadPlace(UpdatePlaceSubcommand), @@ -128,6 +129,32 @@ struct CreateAssetMediaSubcommand{ expected_price:Option, } #[derive(Args)] +/// Automatically detect the media type from file extension and generate asset name and description +struct CreateAssetMediasSubcommand{ + #[arg(long,group="api_key",required=true)] + api_key_literal:Option, + #[arg(long,group="api_key",required=true)] + api_key_envvar:Option, + #[arg(long,group="api_key",required=true)] + api_key_file:Option, + #[arg(long,group="cookie",required=true)] + cookie_literal:Option, + #[arg(long,group="cookie",required=true)] + cookie_envvar:Option, + #[arg(long,group="cookie",required=true)] + cookie_file:Option, + #[arg(long)] + description:Option, + #[arg(long,group="creator",required=true)] + creator_user_id:Option, + #[arg(long,group="creator",required=true)] + creator_group_id:Option, + /// Expected price limits how much robux can be spent to create the asset (defaults to 0) + #[arg(long)] + expected_price:Option, + input_files:Vec, +} +#[derive(Args)] struct UpdateAssetSubcommand{ #[arg(long)] asset_id:AssetID, @@ -424,6 +451,26 @@ async fn main()->AResult<()>{ description:subcommand.description.unwrap_or_else(||String::with_capacity(0)), expected_price:subcommand.expected_price, }).await, + Commands::CreateAssetMedias(subcommand)=>create_asset_medias(CreateAssetMediasConfig{ + api_key:api_key_from_args( + subcommand.api_key_literal, + subcommand.api_key_envvar, + subcommand.api_key_file, + ).await?, + cookie:cookie_from_args( + subcommand.cookie_literal, + subcommand.cookie_envvar, + subcommand.cookie_file, + ).await?, + creator:match (subcommand.creator_user_id,subcommand.creator_group_id){ + (Some(user_id),None)=>rbx_asset::cloud::Creator::userId(user_id.to_string()), + (None,Some(group_id))=>rbx_asset::cloud::Creator::groupId(group_id.to_string()), + other=>Err(anyhow!("Invalid creator {other:?}"))?, + }, + description:subcommand.description.unwrap_or_else(||String::with_capacity(0)), + input_files:subcommand.input_files, + expected_price:subcommand.expected_price, + }).await, Commands::UploadAsset(subcommand)=>upload_asset(UploadAssetConfig{ cookie:cookie_from_args( subcommand.cookie_literal, @@ -575,8 +622,28 @@ struct CreateAssetMediaConfig{ expected_price:Option, } +async fn get_asset_exp_backoff( + context:&CloudContext, + asset_operation:&rbx_asset::cloud::AssetOperation +)->Result{ + const BACKOFF_MUL:f32=1.3956124250860895286;//exp(1/3) + let mut backoff=1000f32; + loop{ + match asset_operation.try_get_asset(&context).await{ + //try again when the operation is not done + Err(rbx_asset::cloud::AssetOperationError::Operation(rbx_asset::cloud::OperationError::NotDone))=>(), + //return all other results + other_result=>return other_result, + } + println!("Operation not complete; waiting {:.0}ms...",backoff); + tokio::time::sleep(std::time::Duration::from_millis(backoff as u64)).await; + backoff*=BACKOFF_MUL; + } +} + async fn create_asset_media(config:CreateAssetMediaConfig)->AResult<()>{ - let resp=CloudContext::new(config.api_key) + let context=CloudContext::new(config.api_key); + let asset_response=context .create_asset(rbx_asset::cloud::CreateAssetRequest{ assetType:config.asset_type, displayName:config.model_name, @@ -586,7 +653,149 @@ async fn create_asset_media(config:CreateAssetMediaConfig)->AResult<()>{ expectedPrice:Some(config.expected_price.unwrap_or(0)), } },tokio::fs::read(config.input_file).await?).await?; - println!("CreateResponse={:?}",resp); + //hardcode a 2 second sleep because roblox be slow + println!("Asset submitted, waiting 2s..."); + tokio::time::sleep(std::time::Duration::from_secs(2)).await; + let asset=get_asset_exp_backoff(&context,&asset_response).await?; + println!("CreateResponse={:?}",asset); + Ok(()) +} + +// complex operation requires both api key and cookie! how horrible! roblox please fix! +struct CreateAssetMediasConfig{ + api_key:ApiKey, + cookie:Cookie, + description:String, + input_files:Vec, + creator:rbx_asset::cloud::Creator, + expected_price:Option, +} + +#[derive(Debug)] +#[allow(dead_code)] +enum CreateAssetMediasError{ + NoFileStem(PathBuf), + IO(std::io::Error), + UnknownFourCC(Option<[u8;4]>), + Create(rbx_asset::cloud::CreateError), +} +impl std::fmt::Display for CreateAssetMediasError{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>)->std::fmt::Result{ + write!(f,"{self:?}") + } +} +impl std::error::Error for CreateAssetMediasError{} + +#[derive(Debug)] +#[allow(dead_code)] +enum PollOperationError{ + CreateAssetMedias(CreateAssetMediasError), + AssetOperation(rbx_asset::cloud::AssetOperationError), +} +impl std::fmt::Display for PollOperationError{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>)->std::fmt::Result{ + write!(f,"{self:?}") + } +} +impl std::error::Error for PollOperationError{} + +#[derive(Debug)] +#[allow(dead_code)] +enum DownloadDecalError{ + PollOperation(PollOperationError), + ParseInt(std::num::ParseIntError), + Get(rbx_asset::cookie::GetError), + LoadDom(LoadDomError), + NoFirstInstance, + NoTextureProperty, + TexturePropertyInvalid, +} +impl std::fmt::Display for DownloadDecalError{ + fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ + write!(f,"{self:?}") + } +} +impl std::error::Error for DownloadDecalError{} + +async fn create_asset_medias(config:CreateAssetMediasConfig)->AResult<()>{ + let context=CloudContext::new(config.api_key); + let cookie_context=CookieContext::new(config.cookie); + let expected_price=Some(config.expected_price.unwrap_or(0)); + futures::stream::iter(config.input_files.into_iter() + //step 1: read file, make create request + .map(|path|{ + let description=&config.description; + let creator=&config.creator; + let context=&context; + async move{(path.clone(), + async move{ + let model_name=path.file_stem() + .and_then(std::ffi::OsStr::to_str) + .ok_or_else(||CreateAssetMediasError::NoFileStem(path.clone()))? + .to_owned(); + let file=tokio::fs::read(path).await.map_err(CreateAssetMediasError::IO)?; + let asset_type=match file.get(0..4){ + //png + Some(b"\x89PNG")=>rbx_asset::cloud::AssetType::Decal, + //jpeg + Some(b"\xFF\xD8\xFF\xE0")=>rbx_asset::cloud::AssetType::Decal, + //Some("fbx")=>rbx_asset::cloud::AssetType::Model, + //Some("ogg")=>rbx_asset::cloud::AssetType::Audio, + fourcc=>Err(CreateAssetMediasError::UnknownFourCC(fourcc.map(|s|s.try_into().unwrap())))?, + }; + context.create_asset(rbx_asset::cloud::CreateAssetRequest{ + assetType:asset_type, + displayName:model_name, + description:description.clone(), + creationContext:rbx_asset::cloud::CreationContext{ + creator:creator.clone(), + expectedPrice:expected_price, + } + },file).await.map_err(CreateAssetMediasError::Create) + } + .await)} + })) + //parallel requests + .buffer_unordered(CONCURRENT_REQUESTS) + //step 2: poll operation until it completes + .then(|(path,create_result)|{ + let context=&context; + async{(path, + async{ + let asset_operation=create_result.map_err(PollOperationError::CreateAssetMedias)?; + get_asset_exp_backoff(context,&asset_operation).await.map_err(PollOperationError::AssetOperation) + } + .await)} + }) + //step 3: read decal id from operation and download it, decode it as a roblox file and extract the texture content url + .then(|(path,asset_response_result)|{ + let cookie_context=&cookie_context; + async move{(path, + async move{ + let asset_response=asset_response_result.map_err(DownloadDecalError::PollOperation)?; + let file=cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{ + asset_id:asset_response.assetId.parse().map_err(DownloadDecalError::ParseInt)?, + version:None, + }).await.map_err(DownloadDecalError::Get)?; + let dom=load_dom(std::io::Cursor::new(file)).map_err(DownloadDecalError::LoadDom)?; + let instance=dom.get_by_ref( + *dom.root().children().first().ok_or(DownloadDecalError::NoFirstInstance)? + ).ok_or(DownloadDecalError::NoFirstInstance)?; + let texture=instance.properties.get("Texture").ok_or(DownloadDecalError::NoTextureProperty)?; + let asset_url=match texture{ + rbx_dom_weak::types::Variant::Content(url)=>url.clone().into_string(), + _=>Err(DownloadDecalError::TexturePropertyInvalid)?, + }; + Ok::<_,DownloadDecalError>((asset_response.displayName,asset_url)) + } + .await)} + }) + .for_each(|(path,download_decal_result)|async move{ + match download_decal_result{ + Ok((file_name,asset_url))=>println!("{}={}",file_name,asset_url), + Err(e)=>eprintln!("ERROR file={:?} error={e}",path), + } + }).await; Ok(()) } @@ -657,11 +866,8 @@ async fn download_list(cookie:Cookie,asset_id_file_map:AssetIDFileMap)->AResult< .buffer_unordered(CONCURRENT_REQUESTS) .for_each(|b:AResult<_>|async{ match b{ - Ok((dest,data))=>{ - match tokio::fs::write(dest,data).await{ - Err(e)=>eprintln!("fs error: {}",e), - _=>(), - } + Ok((dest,data))=>if let Err(e)=tokio::fs::write(dest,data).await{ + eprintln!("fs error: {}",e); }, Err(e)=>eprintln!("dl error: {}",e), } @@ -838,18 +1044,34 @@ async fn download_history(mut config:DownloadHistoryConfig)->AResult<()>{ Ok(()) } -fn load_dom(input:R)->AResult{ +#[derive(Debug)] +#[allow(dead_code)] +enum LoadDomError{ + IO(std::io::Error), + RbxBinary(rbx_binary::DecodeError), + RbxXml(rbx_xml::DecodeError), + UnknownRobloxFile([u8;4]), + UnsupportedFile, +} +impl std::fmt::Display for LoadDomError{ + fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ + write!(f,"{self:?}") + } +} +impl std::error::Error for LoadDomError{} + +fn load_dom(input:R)->Result{ let mut buf=std::io::BufReader::new(input); - let peek=std::io::BufRead::fill_buf(&mut buf)?; + let peek=std::io::BufRead::fill_buf(&mut buf).map_err(LoadDomError::IO)?; match &peek[0..4]{ b"{ match &peek[4..8]{ - b"lox!"=>rbx_binary::from_reader(buf).map_err(anyhow::Error::msg), - b"lox "=>rbx_xml::from_reader_default(buf).map_err(anyhow::Error::msg), - other=>Err(anyhow::Error::msg(format!("Unknown Roblox file type {:?}",other))), + b"lox!"=>rbx_binary::from_reader(buf).map_err(LoadDomError::RbxBinary), + b"lox "=>rbx_xml::from_reader_default(buf).map_err(LoadDomError::RbxXml), + other=>Err(LoadDomError::UnknownRobloxFile(other.try_into().unwrap())), } }, - _=>Err(anyhow::Error::msg("unsupported file type")), + _=>Err(LoadDomError::UnsupportedFile), } }