diff --git a/Cargo.lock b/Cargo.lock index 22cdef5..82b64b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.21" @@ -73,6 +82,28 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-trait" version = "0.1.89" @@ -90,6 +121,12 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + [[package]] name = "base64" version = "0.22.1" @@ -108,15 +145,6 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - [[package]] name = "bumpalo" version = "3.19.1" @@ -160,6 +188,18 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chrono" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + [[package]] name = "clap" version = "4.5.57" @@ -257,15 +297,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - [[package]] name = "crossterm" version = "0.29.0" @@ -293,16 +324,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "crypto-common" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] - [[package]] name = "daemonize" version = "0.5.0" @@ -318,8 +339,18 @@ version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", ] [[package]] @@ -336,13 +367,38 @@ dependencies = [ "syn", ] +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + [[package]] name = "darling_macro" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core", + "darling_core 0.20.11", + "quote", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", "quote", "syn", ] @@ -354,6 +410,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", + "serde_core", ] [[package]] @@ -371,7 +428,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ - "darling", + "darling 0.20.11", "proc-macro2", "quote", "syn", @@ -409,16 +466,6 @@ dependencies = [ "syn", ] -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - [[package]] name = "dirs" version = "6.0.0" @@ -659,16 +706,6 @@ dependencies = [ "thread_local", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getrandom" version = "0.2.17" @@ -728,7 +765,10 @@ dependencies = [ "libc", "mesa-dev", "nix", + "num-traits", "rand", + "reqwest", + "reqwest-middleware", "rustc-hash", "scc", "secrecy", @@ -736,8 +776,9 @@ dependencies = [ "semver", "serde", "serde_json", + "serde_path_to_error", "shellexpand", - "thiserror", + "thiserror 2.0.18", "tokio", "toml", "tracing", @@ -758,13 +799,19 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", "tracing", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.15.5" @@ -911,6 +958,30 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu_collections" version = "2.1.1" @@ -1025,6 +1096,17 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.13.0" @@ -1200,19 +1282,34 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "mesa-dev" -version = "0.1.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecbae940ace30927edd25485c2502b141198cdbe320808ce5740982254486207" +checksum = "fb5d4651070e6257276f86eb0737bfe37bd6a6a73f7de827fca4efaef55da091" dependencies = [ - "bytes", + "async-stream", "futures-core", - "hex", - "http", + "mesa_dev_oapi", + "reqwest", + "reqwest-middleware", + "serde", + "serde_json", + "serde_path_to_error", +] + +[[package]] +name = "mesa_dev_oapi" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266585ec1d9950343ba2a307909003c05342149c4bd0cf5b224591b2621ccfd4" +dependencies = [ "reqwest", + "reqwest-middleware", "serde", "serde_json", - "sha2", - "thiserror", + "serde_path_to_error", + "serde_repr", + "serde_with", + "url", ] [[package]] @@ -1221,6 +1318,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "mio" version = "1.1.1" @@ -1277,6 +1384,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "num_threads" version = "0.1.7" @@ -1483,7 +1599,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2", - "thiserror", + "thiserror 2.0.18", "tokio", "tracing", "web-time", @@ -1504,7 +1620,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror", + "thiserror 2.0.18", "tinyvec", "tracing", "web-time", @@ -1585,7 +1701,27 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.17", "libredox", - "thiserror", + "thiserror 2.0.18", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1640,6 +1776,7 @@ dependencies = [ "js-sys", "log", "mime", + "mime_guess", "native-tls", "percent-encoding", "pin-project-lite", @@ -1663,6 +1800,21 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "reqwest-middleware" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57f17d28a6e6acfe1733fe24bcd30774d13bffa4b8a22535b4c8c98423088d4e" +dependencies = [ + "anyhow", + "async-trait", + "http", + "reqwest", + "serde", + "thiserror 1.0.69", + "tower-service", +] + [[package]] name = "ring" version = "0.17.14" @@ -1777,6 +1929,30 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -1901,6 +2077,28 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "serde_spanned" version = "1.0.4" @@ -1923,14 +2121,34 @@ dependencies = [ ] [[package]] -name = "sha2" -version = "0.10.9" +name = "serde_with" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" dependencies = [ - "cfg-if", - "cpufeatures", - "digest", + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.13.0", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +dependencies = [ + "darling 0.21.3", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2093,13 +2311,33 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2247,7 +2485,7 @@ version = "0.9.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" dependencies = [ - "indexmap", + "indexmap 2.13.0", "serde_core", "serde_spanned", "toml_datetime", @@ -2405,10 +2643,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] -name = "typenum" -version = "1.19.0" +name = "unicase" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" [[package]] name = "unicode-ident" @@ -2536,12 +2774,6 @@ dependencies = [ "rustversion", ] -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - [[package]] name = "vt100" version = "0.16.2" @@ -2672,7 +2904,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap", + "indexmap 2.13.0", "wasm-encoder", "wasmparser", ] @@ -2685,7 +2917,7 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags", "hashbrown 0.15.5", - "indexmap", + "indexmap 2.13.0", "semver", ] @@ -2740,6 +2972,41 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-link" version = "0.2.1" @@ -2974,7 +3241,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap", + "indexmap 2.13.0", "prettyplease", "syn", "wasm-metadata", @@ -3005,7 +3272,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags", - "indexmap", + "indexmap 2.13.0", "log", "serde", "serde_derive", @@ -3024,7 +3291,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap", + "indexmap 2.13.0", "log", "semver", "serde", diff --git a/Cargo.toml b/Cargo.toml index 1b59c12..3c0f8cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,11 @@ clap = { version = "4.5.54", features = ["derive", "env"] } tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } fuser = { version = "0.16.0", features = ["libfuse"] } libc = "0.2" -mesa-dev = "0.1.1" +mesa-dev = "1.8.0" +num-traits = "0.2" +reqwest = { version = "0.12", default-features = false } +reqwest-middleware = "0.4" +serde_path_to_error = "0.1" tokio = { version = "1.49.0", features = ["full"] } tracing = { version = "0.1.44", features = ["release_max_level_debug"] } rand = "0.9.2" diff --git a/src/fs/mescloud/common.rs b/src/fs/mescloud/common.rs index c97d76d..c0f6f6e 100644 --- a/src/fs/mescloud/common.rs +++ b/src/fs/mescloud/common.rs @@ -1,9 +1,49 @@ //! Shared types and helpers used by both `MesaFS` and `RepoFs`. +use mesa_dev::low_level::apis; use thiserror::Error; pub(super) use super::icache::InodeControlBlock; +/// A concrete error type that preserves the structure of `mesa_dev::low_level::apis::Error` +/// without the generic parameter. +#[derive(Debug, Error)] +pub enum MesaApiError { + #[error("HTTP request error")] + Reqwest(#[from] reqwest::Error), + + #[error("HTTP middleware error")] + ReqwestMiddleware(#[from] reqwest_middleware::Error), + + #[error("JSON deserialization error")] + Serde(#[from] serde_json::Error), + + #[error("JSON deserialization error at path")] + SerdePath(#[from] serde_path_to_error::Error), + + #[error("IO error")] + Io(#[from] std::io::Error), + + #[error("API returned HTTP {status}")] + Response { status: u16, body: String }, +} + +impl From> for MesaApiError { + fn from(e: apis::Error) -> Self { + match e { + apis::Error::Reqwest(e) => Self::Reqwest(e), + apis::Error::ReqwestMiddleware(e) => Self::ReqwestMiddleware(e), + apis::Error::Serde(e) => Self::Serde(e), + apis::Error::SerdePathToError(e) => Self::SerdePath(e), + apis::Error::Io(e) => Self::Io(e), + apis::Error::ResponseError(rc) => Self::Response { + status: rc.status.as_u16(), + body: rc.content, + }, + } + } +} + #[derive(Debug, Error)] pub enum LookupError { #[error("inode not found")] @@ -12,8 +52,8 @@ pub enum LookupError { #[error("file does not exist")] FileDoesNotExist, - #[error("remote mesa error: {0}")] - RemoteMesaError(#[from] mesa_dev::error::MesaError), + #[error("remote mesa error")] + RemoteMesaError(#[from] MesaApiError), } impl From for i32 { @@ -61,8 +101,8 @@ pub enum ReadError { #[error("inode not found")] InodeNotFound, - #[error("remote mesa error: {0}")] - RemoteMesaError(#[from] mesa_dev::error::MesaError), + #[error("remote mesa error")] + RemoteMesaError(#[from] MesaApiError), #[error("content is not a file")] NotAFile, @@ -87,8 +127,8 @@ pub enum ReadDirError { #[error("inode not found")] InodeNotFound, - #[error("remote mesa error: {0}")] - RemoteMesaError(#[from] mesa_dev::error::MesaError), + #[error("remote mesa error")] + RemoteMesaError(#[from] MesaApiError), #[error("inode is not a directory")] NotADirectory, diff --git a/src/fs/mescloud/mod.rs b/src/fs/mescloud/mod.rs index 0d0e141..64ea0ad 100644 --- a/src/fs/mescloud/mod.rs +++ b/src/fs/mescloud/mod.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::ffi::OsStr; use bytes::Bytes; -use mesa_dev::Mesa as MesaClient; +use mesa_dev::MesaClient; use secrecy::ExposeSecret as _; use tracing::{instrument, trace, warn}; @@ -67,8 +67,9 @@ impl MesaFS { org_inodes: HashMap::new(), org_slots: orgs .map(|org_conf| { - let client = MesaClient::builder(org_conf.api_key.expose_secret()) - .base_url(MESA_API_BASE_URL) + let client = MesaClient::builder() + .with_api_key(org_conf.api_key.expose_secret()) + .with_base_path(MESA_API_BASE_URL) .build(); let org = OrgFs::new(org_conf.name, client, fs_owner); OrgSlot { diff --git a/src/fs/mescloud/org.rs b/src/fs/mescloud/org.rs index 9e3b56d..fcfd522 100644 --- a/src/fs/mescloud/org.rs +++ b/src/fs/mescloud/org.rs @@ -4,14 +4,14 @@ use std::time::SystemTime; use bytes::Bytes; use futures::TryStreamExt as _; -use mesa_dev::Mesa as MesaClient; +use mesa_dev::MesaClient; use secrecy::SecretString; use tracing::{instrument, trace, warn}; -use super::common::InodeControlBlock; pub use super::common::{ GetAttrError, LookupError, OpenError, ReadDirError, ReadError, ReleaseError, }; +use super::common::{InodeControlBlock, MesaApiError}; use super::icache::MescloudICache; use super::repo::RepoFs; use crate::fs::icache::bridge::HashMapBridge; @@ -278,18 +278,18 @@ impl OrgFs { (ino, attr) } - /// Poll `repos().get()` until the repo is no longer syncing. + /// Fetch a repo by name via the API. async fn wait_for_sync( &self, repo_name: &str, - ) -> Result { - let mut repo = self.client.repos(&self.name).get(repo_name).await?; - while repo.status.is_some() { - trace!(repo = repo_name, "repo is syncing, waiting..."); - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - repo = self.client.repos(&self.name).get(repo_name).await?; - } - Ok(repo) + ) -> Result { + self.client + .org(&self.name) + .repos() + .at(repo_name) + .get() + .await + .map_err(MesaApiError::from) } /// Allocate an org-level file handle and map it through the bridge. @@ -371,7 +371,7 @@ impl Fs for OrgFs { // Children of org root are repos. trace!(repo = name_str, "lookup: resolving repo"); - // Validate repo exists via API, waiting for sync if needed. + // Validate repo exists via API. let repo = self.wait_for_sync(name_str).await?; let (ino, attr) = self.ensure_repo_inode( @@ -404,7 +404,7 @@ impl Fs for OrgFs { "lookup: resolving github repo via owner dir" ); - // Validate via API (uses encoded name), waiting for sync if needed. + // Validate via API (uses encoded name). let repo = self.wait_for_sync(&encoded).await?; let (ino, attr) = @@ -464,17 +464,22 @@ impl Fs for OrgFs { } // List repos via API. - let repos: Vec = self + let repos: Vec = self .client - .repos(&self.name) - .list_all() + .org(&self.name) + .repos() + .list(None) .try_collect() - .await?; + .await + .map_err(MesaApiError::from)?; let repo_infos: Vec<(String, String)> = repos .into_iter() - .filter(|r| r.status.is_none()) // skip repos still syncing - .map(|r| (r.name, r.default_branch)) + .filter_map(|r| { + let name = r.name?; + let branch = r.default_branch.unwrap_or_else(|| "main".to_owned()); + Some((name, branch)) + }) .collect(); trace!(count = repo_infos.len(), "readdir: fetched repo list"); diff --git a/src/fs/mescloud/repo.rs b/src/fs/mescloud/repo.rs index 387415d..94f7ee8 100644 --- a/src/fs/mescloud/repo.rs +++ b/src/fs/mescloud/repo.rs @@ -6,7 +6,9 @@ use std::{collections::HashMap, ffi::OsStr, path::PathBuf, time::SystemTime}; use base64::Engine as _; use bytes::Bytes; -use mesa_dev::Mesa as MesaClient; +use mesa_dev::MesaClient; +use mesa_dev::low_level::content::{Content, DirEntry as MesaDirEntry}; +use num_traits::cast::ToPrimitive as _; use tracing::{instrument, trace, warn}; use crate::fs::r#trait::{ @@ -14,6 +16,7 @@ use crate::fs::r#trait::{ LockOwner, OpenFile, OpenFlags, }; +use super::common::MesaApiError; pub use super::common::{ GetAttrError, LookupError, OpenError, ReadDirError, ReadError, ReleaseError, }; @@ -120,25 +123,47 @@ impl Fs for RepoFs { let content = self .client - .content(&self.org_name, &self.repo_name) - .get(file_path.as_deref(), Some(self.ref_.as_str())) - .await?; - + .org(&self.org_name) + .repos() + .at(&self.repo_name) + .content() + .get(Some(self.ref_.as_str()), file_path.as_deref(), None) + .await + .map_err(MesaApiError::from)?; + + #[expect( + clippy::match_same_arms, + reason = "symlink arm will diverge once readlink is wired up" + )] let kind = match &content { - mesa_dev::models::Content::File { .. } => DirEntryType::RegularFile, - mesa_dev::models::Content::Dir { .. } => DirEntryType::Directory, + Content::File(_) => DirEntryType::RegularFile, + // TODO(MES-712): return DirEntryType::Symlink and FileAttr::Symlink, then wire up readlink. + Content::Symlink(_) => DirEntryType::RegularFile, + Content::Dir(_) => DirEntryType::Directory, }; let (ino, _) = self.icache.ensure_child_inode(parent, name, kind); let now = SystemTime::now(); - let attr = match content { - mesa_dev::models::Content::File { size, .. } => FileAttr::RegularFile { - common: self.icache.make_common_file_attr(ino, 0o644, now, now), - size, - blocks: mescloud_icache::blocks_of_size(Self::BLOCK_SIZE, size), - }, - mesa_dev::models::Content::Dir { .. } => FileAttr::Directory { + let attr = match &content { + Content::File(f) => { + let size = f.size.to_u64().unwrap_or(0); + FileAttr::RegularFile { + common: self.icache.make_common_file_attr(ino, 0o644, now, now), + size, + blocks: mescloud_icache::blocks_of_size(Self::BLOCK_SIZE, size), + } + } + // TODO(MES-712): return FileAttr::Symlink { target, size } and wire up readlink. + Content::Symlink(s) => { + let size = s.size.to_u64().unwrap_or(0); + FileAttr::RegularFile { + common: self.icache.make_common_file_attr(ino, 0o644, now, now), + size, + blocks: mescloud_icache::blocks_of_size(Self::BLOCK_SIZE, size), + } + } + Content::Dir(_) => FileAttr::Directory { common: self.icache.make_common_file_attr(ino, 0o755, now, now), }, }; @@ -179,23 +204,29 @@ impl Fs for RepoFs { let content = self .client - .content(&self.org_name, &self.repo_name) - .get(file_path.as_deref(), Some(self.ref_.as_str())) - .await?; + .org(&self.org_name) + .repos() + .at(&self.repo_name) + .content() + .get(Some(self.ref_.as_str()), file_path.as_deref(), None) + .await + .map_err(MesaApiError::from)?; let mesa_entries = match content { - mesa_dev::models::Content::Dir { entries, .. } => entries, - mesa_dev::models::Content::File { .. } => return Err(ReadDirError::NotADirectory), + Content::Dir(d) => d.entries, + Content::File(_) | Content::Symlink(_) => return Err(ReadDirError::NotADirectory), }; - let collected: Vec<_> = mesa_entries + let collected: Vec<(String, DirEntryType)> = mesa_entries .into_iter() - .map(|e| { - let kind = match e.entry_type { - mesa_dev::models::DirEntryType::File => DirEntryType::RegularFile, - mesa_dev::models::DirEntryType::Dir => DirEntryType::Directory, + .filter_map(|e| { + let (name, kind) = match e { + MesaDirEntry::File(f) => (f.name?, DirEntryType::RegularFile), + // TODO(MES-712): return DirEntryType::Symlink once readlink is wired up. + MesaDirEntry::Symlink(s) => (s.name?, DirEntryType::RegularFile), + MesaDirEntry::Dir(d) => (d.name?, DirEntryType::Directory), }; - (e.name, kind) + Some((name, kind)) }) .collect(); @@ -270,13 +301,20 @@ impl Fs for RepoFs { let content = self .client - .content(&self.org_name, &self.repo_name) - .get(file_path.as_deref(), Some(self.ref_.as_str())) - .await?; + .org(&self.org_name) + .repos() + .at(&self.repo_name) + .content() + .get(Some(self.ref_.as_str()), file_path.as_deref(), None) + .await + .map_err(MesaApiError::from)?; let encoded_content = match content { - mesa_dev::models::Content::File { content, .. } => content, - mesa_dev::models::Content::Dir { .. } => return Err(ReadError::NotAFile), + Content::File(f) => f.content.unwrap_or_default(), + // TODO(MES-712): return ReadError::NotAFile once symlinks are surfaced as + // DirEntryType::Symlink, and implement readlink to return the link target. + Content::Symlink(s) => s.content.unwrap_or_default(), + Content::Dir(_) => return Err(ReadError::NotAFile), }; let decoded = base64::engine::general_purpose::STANDARD.decode(&encoded_content)?;