From f31633cb3a8423617e1c1b4915dee004681d6c38 Mon Sep 17 00:00:00 2001 From: Nils Pukropp Date: Tue, 30 Jun 2026 04:58:40 +0200 Subject: [PATCH] fmt --- crates/pdfs-cli/src/main.rs | 38 +++-- crates/pdfs-core/src/auth.rs | 2 +- crates/pdfs-core/src/cache.rs | 23 ++- crates/pdfs-core/src/control.rs | 5 +- crates/pdfs-fuse/src/lib.rs | 238 +++++++++++++++++++++++--------- crates/pdfs-gui/src/app.rs | 62 ++++++--- crates/pdfs-gui/src/main.rs | 7 +- 7 files changed, 271 insertions(+), 104 deletions(-) diff --git a/crates/pdfs-cli/src/main.rs b/crates/pdfs-cli/src/main.rs index c787036..ff9f4a1 100644 --- a/crates/pdfs-cli/src/main.rs +++ b/crates/pdfs-cli/src/main.rs @@ -11,7 +11,11 @@ use pdfs_core::config::AppDirs; use pdfs_core::control::{Request as CtlRequest, Response as CtlResponse}; #[derive(Parser)] -#[command(name = "pdfs", version, about = "Proton Drive for Linux (Files On-Demand)")] +#[command( + name = "pdfs", + version, + about = "Proton Drive for Linux (Files On-Demand)" +)] struct Cli { #[command(subcommand)] command: Command, @@ -111,8 +115,7 @@ fn cmd_login(username: Option) -> Result<()> { // The 2FA prompt fires lazily — only if the account actually requires it. let get_totp = || -> pdfs_core::Result { - prompt_line("2FA code") - .map_err(|e| pdfs_core::Error::Other(format!("read 2FA code: {e}"))) + prompt_line("2FA code").map_err(|e| pdfs_core::Error::Other(format!("read 2FA code: {e}"))) }; let rt = tokio::runtime::Runtime::new()?; @@ -143,7 +146,9 @@ fn cmd_status() -> Result<()> { } // If a mount daemon is running, report live status from its control socket. match control_request(CtlRequest::Status) { - Ok(CtlResponse::Status { mountpoint, pinned, .. }) => { + Ok(CtlResponse::Status { + mountpoint, pinned, .. + }) => { println!("Mounted at {mountpoint} ({pinned} pinned)"); } Ok(other) => println!("Mount: unexpected response {other:?}"), @@ -209,8 +214,14 @@ fn cmd_mount(mountpoint: Option) -> Result<()> { }); let handle = rt.handle().clone(); - let result = - pdfs_fuse::mount(client, handle, &mountpoint, cache, &control_socket, username); + let result = pdfs_fuse::mount( + client, + handle, + &mountpoint, + cache, + &control_socket, + username, + ); // Clean unmount: flush whatever tokens the store holds now so the keyring // always ends a session with the live refresh token, not a stale one. Runs @@ -224,7 +235,9 @@ fn cmd_mount(mountpoint: Option) -> Result<()> { } fn cmd_pin(path: PathBuf) -> Result<()> { - match control_request(CtlRequest::Pin { path: path_arg(&path)? })? { + match control_request(CtlRequest::Pin { + path: path_arg(&path)?, + })? { CtlResponse::Ok { message } => println!("{message}"), CtlResponse::Error { message } => bail!("{message}"), other => bail!("unexpected response: {other:?}"), @@ -233,7 +246,9 @@ fn cmd_pin(path: PathBuf) -> Result<()> { } fn cmd_unpin(path: PathBuf) -> Result<()> { - match control_request(CtlRequest::Unpin { path: path_arg(&path)? })? { + match control_request(CtlRequest::Unpin { + path: path_arg(&path)?, + })? { CtlResponse::Ok { message } => println!("{message}"), CtlResponse::Error { message } => bail!("{message}"), other => bail!("unexpected response: {other:?}"), @@ -277,7 +292,9 @@ fn cmd_ls(path: Option) -> Result<()> { fn cmd_photos(limit: usize, offset: usize) -> Result<()> { match control_request(CtlRequest::PhotosTimeline { offset, limit })? { - CtlResponse::Photos { available: false, .. } => { + CtlResponse::Photos { + available: false, .. + } => { println!("This account has no photos volume.") } CtlResponse::Photos { items, .. } if items.is_empty() => println!("No photos."), @@ -307,8 +324,7 @@ fn cmd_open_photo(uid: String) -> Result<()> { /// relative paths are passed through and resolved against the mount root. fn path_arg(path: &Path) -> Result { let p = if path.is_absolute() { - std::fs::canonicalize(path) - .with_context(|| format!("resolve {}", path.display()))? + std::fs::canonicalize(path).with_context(|| format!("resolve {}", path.display()))? } else { path.to_path_buf() }; diff --git a/crates/pdfs-core/src/auth.rs b/crates/pdfs-core/src/auth.rs index 3f71e24..966bf29 100644 --- a/crates/pdfs-core/src/auth.rs +++ b/crates/pdfs-core/src/auth.rs @@ -12,7 +12,7 @@ use proton_sdk::config::ProtonClientConfiguration; use proton_sdk::session::{PasswordMode, ProtonApiSession, ResumeParameters}; use serde::{Deserialize, Serialize}; -use crate::config::{AppDirs, APP_VERSION, KEYRING_SERVICE, USER_AGENT}; +use crate::config::{APP_VERSION, AppDirs, KEYRING_SERVICE, USER_AGENT}; use crate::error::{Error, Result}; /// Fixed keyring account name for the single stored session blob. diff --git a/crates/pdfs-core/src/cache.rs b/crates/pdfs-core/src/cache.rs index b829c31..974b755 100644 --- a/crates/pdfs-core/src/cache.rs +++ b/crates/pdfs-core/src/cache.rs @@ -81,7 +81,12 @@ impl ContentCache { if let Some(parent) = pins_path.parent() { std::fs::create_dir_all(parent)?; } - Ok(Self { content_dir, thumb_dir, pins_path, max_bytes }) + Ok(Self { + content_dir, + thumb_dir, + pins_path, + max_bytes, + }) } /// Filesystem-safe, fixed-length cache key for a uid display string: the @@ -192,7 +197,8 @@ impl ContentCache { } fn thumb_meta(&self, uid: &NodeUid, ttype: i32) -> PathBuf { - self.thumb_dir.join(format!("{}.t{ttype}.meta", Self::key(uid))) + self.thumb_dir + .join(format!("{}.t{ttype}.meta", Self::key(uid))) } /// Serve a cached thumbnail of `ttype` for `uid`, or `None` on miss/stale. @@ -267,8 +273,12 @@ impl ContentCache { return; } // Pinned blobs (by cache key) are exempt from eviction. - let pinned: HashSet = - self.load_pins().pins.keys().map(|uid| Self::key_str(uid)).collect(); + let pinned: HashSet = self + .load_pins() + .pins + .keys() + .map(|uid| Self::key_str(uid)) + .collect(); // Scan blobs (the `` files; skip `.meta`/`.tmp` siblings), record // size + last-access (mtime) for each. @@ -336,7 +346,10 @@ impl ContentCache { let key = uid.to_string(); file.pins.insert( key.clone(), - Pin { uid: key, path: path.display().to_string() }, + Pin { + uid: key, + path: path.display().to_string(), + }, ); self.save_pins(&file) } diff --git a/crates/pdfs-core/src/control.rs b/crates/pdfs-core/src/control.rs index 81f6342..3c388b8 100644 --- a/crates/pdfs-core/src/control.rs +++ b/crates/pdfs-core/src/control.rs @@ -99,7 +99,10 @@ pub enum Response { Entries { entries: Vec }, /// A page of the photos timeline. `available` is false when the account /// has no photos volume. - Photos { available: bool, items: Vec }, + Photos { + available: bool, + items: Vec, + }, /// An on-disk path the front-end can open (e.g. a downloaded photo). FilePath { path: String }, /// The request failed. diff --git a/crates/pdfs-fuse/src/lib.rs b/crates/pdfs-fuse/src/lib.rs index f36c9a6..b3be1d7 100644 --- a/crates/pdfs-fuse/src/lib.rs +++ b/crates/pdfs-fuse/src/lib.rs @@ -32,13 +32,13 @@ use std::path::{Component, Path, PathBuf}; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; -use fuser::{ - BsdFileFlags, Config, Errno, FileAttr, FileHandle, FileType, Filesystem, FopenFlags, Generation, - INodeNo, LockOwner, MountOption, Notifier, OpenAccMode, OpenFlags, RenameFlags, ReplyAttr, - ReplyCreate, ReplyData, ReplyDirectory, ReplyEmpty, ReplyEntry, ReplyOpen, ReplyWrite, Request, - Session, TimeOrNow, WriteFlags, -}; use fuser::ReplyXattr; +use fuser::{ + BsdFileFlags, Config, Errno, FileAttr, FileHandle, FileType, Filesystem, FopenFlags, + Generation, INodeNo, LockOwner, MountOption, Notifier, OpenAccMode, OpenFlags, RenameFlags, + ReplyAttr, ReplyCreate, ReplyData, ReplyDirectory, ReplyEmpty, ReplyEntry, ReplyOpen, + ReplyWrite, Request, Session, TimeOrNow, WriteFlags, +}; use pdfs_core::cache::ContentCache; use pdfs_core::control::{DirEntry, PhotoItem, Request as CtlRequest, Response as CtlResponse}; use proton_drive_rs::proton_sdk::ids::{DriveEventId, LinkId, NodeUid, VolumeId}; @@ -120,8 +120,14 @@ impl State { let ino = self.next_ino; self.next_ino += 1; self.by_uid.insert(node.uid.clone(), ino); - self.entries - .insert(ino, Entry { uid: node.uid.clone(), parent, node }); + self.entries.insert( + ino, + Entry { + uid: node.uid.clone(), + parent, + node, + }, + ); ino } @@ -252,14 +258,16 @@ impl Core { let mut ino = ROOT_INO; let mut uid = { let st = self.state.lock().unwrap(); - st.entries.get(&ROOT_INO).map(|e| e.uid.clone()).ok_or(Errno::ENOENT)? + st.entries + .get(&ROOT_INO) + .map(|e| e.uid.clone()) + .ok_or(Errno::ENOENT)? }; for comp in rel.components() { match comp { Component::RootDir | Component::CurDir => continue, Component::Normal(name) => { - let (child_ino, child_uid) = - self.lookup_child(ino, &name.to_string_lossy())?; + let (child_ino, child_uid) = self.lookup_child(ino, &name.to_string_lossy())?; ino = child_ino; uid = child_uid; } @@ -320,14 +328,20 @@ impl Core { /// into the content cache and record it in the pin registry. Returns the /// resolved node name on success. fn pin(&self, rel: &Path) -> Result { - let (ino, uid) = self.resolve_path(rel).map_err(|e| format!("resolve path: {e:?}"))?; + let (ino, uid) = self + .resolve_path(rel) + .map_err(|e| format!("resolve path: {e:?}"))?; let (name, mtime, size) = { let st = self.state.lock().unwrap(); let e = st.entries.get(&ino).ok_or("node vanished")?; if !e.node.is_file() { return Err("not a regular file".into()); } - (e.node.name.clone(), e.node.modification_time, node_size(&e.node)) + ( + e.node.name.clone(), + e.node.modification_time, + node_size(&e.node), + ) }; let bytes = self .rt @@ -336,7 +350,9 @@ impl Core { self.cache .store(&uid, mtime, size, &bytes) .map_err(|e| format!("cache store: {e}"))?; - self.cache.add_pin(&uid, rel).map_err(|e| format!("pin: {e}"))?; + self.cache + .add_pin(&uid, rel) + .map_err(|e| format!("pin: {e}"))?; Ok(name) } @@ -363,19 +379,28 @@ impl Core { Errno::EIO })?; if let Some(bytes) = &bytes { - let _ = self.cache.store_thumbnail(&uid, ttype.as_i32(), mtime, bytes); + let _ = self + .cache + .store_thumbnail(&uid, ttype.as_i32(), mtime, bytes); } Ok(bytes) } /// Unpin the file at `rel`, evicting its cached content. fn unpin(&self, rel: &Path) -> Result { - let (ino, uid) = self.resolve_path(rel).map_err(|e| format!("resolve path: {e:?}"))?; + let (ino, uid) = self + .resolve_path(rel) + .map_err(|e| format!("resolve path: {e:?}"))?; let name = { let st = self.state.lock().unwrap(); - st.entries.get(&ino).map(|e| e.node.name.clone()).unwrap_or_default() + st.entries + .get(&ino) + .map(|e| e.node.name.clone()) + .unwrap_or_default() }; - self.cache.remove_pin(&uid).map_err(|e| format!("unpin: {e}"))?; + self.cache + .remove_pin(&uid) + .map_err(|e| format!("unpin: {e}"))?; Ok(name) } @@ -391,8 +416,11 @@ impl Core { /// the same lazy remote enumeration `readdir` uses, projected into /// serializable [`DirEntry`]s (with per-file pin state). fn list_dir(&self, rel: &Path) -> Result, String> { - let (ino, _uid) = self.resolve_path(rel).map_err(|e| format!("resolve path: {e:?}"))?; - self.ensure_children(ino).map_err(|e| format!("enumerate: {e:?}"))?; + let (ino, _uid) = self + .resolve_path(rel) + .map_err(|e| format!("resolve path: {e:?}"))?; + self.ensure_children(ino) + .map_err(|e| format!("enumerate: {e:?}"))?; // Snapshot the listing, then drop the lock before touching the on-disk // pin registry so a slow disk read doesn't block FUSE metadata ops. let rows: Vec<(String, bool, u64, i64, NodeUid)> = { @@ -480,16 +508,22 @@ impl Core { // Batch-fetch only the thumbnails not already cached fresh. let want: Vec = page .iter() - .filter(|it| self.cache.cached_thumbnail_path(&it.uid, ttype, it.capture_time).is_none()) + .filter(|it| { + self.cache + .cached_thumbnail_path(&it.uid, ttype, it.capture_time) + .is_none() + }) .map(|it| it.uid.clone()) .collect(); if !want.is_empty() { - let cap: HashMap = - page.iter().map(|it| (it.uid.clone(), it.capture_time)).collect(); - match self - .rt - .block_on(self.photos().enumerate_thumbnails(&want, ThumbnailType::Thumbnail)) - { + let cap: HashMap = page + .iter() + .map(|it| (it.uid.clone(), it.capture_time)) + .collect(); + match self.rt.block_on( + self.photos() + .enumerate_thumbnails(&want, ThumbnailType::Thumbnail), + ) { Ok(thumbs) => { for ft in thumbs { if let (Ok(bytes), Some(&tag)) = (ft.result, cap.get(&ft.file_uid)) { @@ -546,7 +580,9 @@ impl Core { /// when a fresh blob already exists). Lets a front-end open the file with /// the user's default application without pinning it. fn open_file(&self, rel: &Path) -> Result { - let (ino, uid) = self.resolve_path(rel).map_err(|e| format!("resolve path: {e:?}"))?; + let (ino, uid) = self + .resolve_path(rel) + .map_err(|e| format!("resolve path: {e:?}"))?; let (mtime, size) = { let st = self.state.lock().unwrap(); let e = st.entries.get(&ino).ok_or("node vanished")?; @@ -593,7 +629,11 @@ impl ProtonFs { st.by_uid.insert(root.uid.clone(), ROOT_INO); st.entries.insert( ROOT_INO, - Entry { uid: root.uid.clone(), parent: ROOT_INO, node: root }, + Entry { + uid: root.uid.clone(), + parent: ROOT_INO, + node: root, + }, ); } // SAFETY: geteuid/getegid are infallible and have no preconditions. @@ -682,9 +722,11 @@ fn media_type_for(name: &str) -> &'static str { fn node_size(node: &Node) -> u64 { match &node.kind { NodeKind::Folder => 0, - NodeKind::File { claimed_size, total_size_on_storage, .. } => { - claimed_size.unwrap_or(*total_size_on_storage).max(0) as u64 - } + NodeKind::File { + claimed_size, + total_size_on_storage, + .. + } => claimed_size.unwrap_or(*total_size_on_storage).max(0) as u64, } } @@ -804,7 +846,13 @@ impl Filesystem for ProtonFs { st.next_fh += 1; st.handles.insert( fh, - WriteHandle { ino: ino.0, uid, buf: Vec::new(), seeded: false, dirty: false }, + WriteHandle { + ino: ino.0, + uid, + buf: Vec::new(), + seeded: false, + dirty: false, + }, ); reply.opened(FileHandle(fh), FopenFlags::empty()); } @@ -925,10 +973,22 @@ impl Filesystem for ProtonFs { st.next_fh += 1; st.handles.insert( fh, - WriteHandle { ino, uid: new_uid, buf: Vec::new(), seeded: true, dirty: false }, + WriteHandle { + ino, + uid: new_uid, + buf: Vec::new(), + seeded: true, + dirty: false, + }, ); let attr = self.attr(ino, &st.entries.get(&ino).unwrap().node); - reply.created(&TTL, &attr, Generation(0), FileHandle(fh), FopenFlags::empty()); + reply.created( + &TTL, + &attr, + Generation(0), + FileHandle(fh), + FopenFlags::empty(), + ); } fn write( @@ -1001,7 +1061,14 @@ impl Filesystem for ProtonFs { st.set_size(ino.0, len); len }; - debug!(ino = ino.0, fh, offset, len = data.len(), new_len, "buffered write"); + debug!( + ino = ino.0, + fh, + offset, + len = data.len(), + new_len, + "buffered write" + ); reply.written(data.len() as u32); } @@ -1101,14 +1168,28 @@ impl Filesystem for ProtonFs { } } - fn flush(&self, _req: &Request, _ino: INodeNo, fh: FileHandle, _lock_owner: LockOwner, reply: ReplyEmpty) { + fn flush( + &self, + _req: &Request, + _ino: INodeNo, + fh: FileHandle, + _lock_owner: LockOwner, + reply: ReplyEmpty, + ) { match self.core.commit(fh.0) { Ok(()) => reply.ok(), Err(e) => reply.error(e), } } - fn fsync(&self, _req: &Request, _ino: INodeNo, fh: FileHandle, _datasync: bool, reply: ReplyEmpty) { + fn fsync( + &self, + _req: &Request, + _ino: INodeNo, + fh: FileHandle, + _datasync: bool, + reply: ReplyEmpty, + ) { match self.core.commit(fh.0) { Ok(()) => reply.ok(), Err(e) => reply.error(e), @@ -1170,18 +1251,19 @@ impl Filesystem for ProtonFs { .duration_since(UNIX_EPOCH) .map(|d| d.as_secs() as i64) .ok(); - let new_uid = match self - .core - .rt - .block_on(self.core.client.create_folder(&parent_uid, &name, now)) - { - Ok(u) => u, - Err(e) => { - error!(%parent_uid, name, error = %e, "create folder failed"); - reply.error(Errno::EIO); - return; - } - }; + let new_uid = + match self + .core + .rt + .block_on(self.core.client.create_folder(&parent_uid, &name, now)) + { + Ok(u) => u, + Err(e) => { + error!(%parent_uid, name, error = %e, "create folder failed"); + reply.error(Errno::EIO); + return; + } + }; let node = match self.core.fetch_node(&new_uid) { Ok(n) => n, Err(e) => { @@ -1340,9 +1422,19 @@ impl Filesystem for ProtonFs { /// The cache is authoritative-by-absence: dropping a directory's `children` /// entry forces the next `lookup`/`readdir` to re-enumerate from the remote, so /// most events only need to invalidate listings rather than re-fetch eagerly. -fn apply_event(state: &Mutex, content: &ContentCache, notifier: &Notifier, event: &DriveEvent) { +fn apply_event( + state: &Mutex, + content: &ContentCache, + notifier: &Notifier, + event: &DriveEvent, +) { match event { - DriveEvent::NodeUpdated { node_uid, parent_node_uid, is_trashed, .. } => { + DriveEvent::NodeUpdated { + node_uid, + parent_node_uid, + is_trashed, + .. + } => { let mut st = state.lock().unwrap(); if *is_trashed { // Trashing makes a node vanish from its parent listing. @@ -1486,19 +1578,25 @@ fn handle_control_conn(core: &Core, username: &str, mountpoint: &Path, stream: U }, Ok(CtlRequest::Pin { path }) => match rel_to_mount(mountpoint, &path) { Ok(rel) => match core.pin(&rel) { - Ok(name) => CtlResponse::Ok { message: format!("pinned {name}") }, + Ok(name) => CtlResponse::Ok { + message: format!("pinned {name}"), + }, Err(e) => CtlResponse::Error { message: e }, }, Err(e) => CtlResponse::Error { message: e }, }, Ok(CtlRequest::Unpin { path }) => match rel_to_mount(mountpoint, &path) { Ok(rel) => match core.unpin(&rel) { - Ok(name) => CtlResponse::Ok { message: format!("unpinned {name}") }, + Ok(name) => CtlResponse::Ok { + message: format!("unpinned {name}"), + }, Err(e) => CtlResponse::Error { message: e }, }, Err(e) => CtlResponse::Error { message: e }, }, - Ok(CtlRequest::ListPins) => CtlResponse::Pins { pins: core.cache.list_pins() }, + Ok(CtlRequest::ListPins) => CtlResponse::Pins { + pins: core.cache.list_pins(), + }, Ok(CtlRequest::ListDir { path }) => match rel_to_mount(mountpoint, &path) { Ok(rel) => match core.list_dir(&rel) { Ok(entries) => CtlResponse::Entries { entries }, @@ -1508,26 +1606,40 @@ fn handle_control_conn(core: &Core, username: &str, mountpoint: &Path, stream: U }, Ok(CtlRequest::PhotosTimeline { offset, limit }) => { match core.photos_timeline(offset, limit) { - Ok(Some(items)) => CtlResponse::Photos { available: true, items }, - Ok(None) => CtlResponse::Photos { available: false, items: Vec::new() }, + Ok(Some(items)) => CtlResponse::Photos { + available: true, + items, + }, + Ok(None) => CtlResponse::Photos { + available: false, + items: Vec::new(), + }, Err(e) => CtlResponse::Error { message: e }, } } Ok(CtlRequest::OpenPhoto { uid }) => match parse_uid(&uid) { Some(u) => match core.open_photo(&u) { - Ok(p) => CtlResponse::FilePath { path: p.display().to_string() }, + Ok(p) => CtlResponse::FilePath { + path: p.display().to_string(), + }, Err(e) => CtlResponse::Error { message: e }, }, - None => CtlResponse::Error { message: format!("bad uid: {uid}") }, + None => CtlResponse::Error { + message: format!("bad uid: {uid}"), + }, }, Ok(CtlRequest::OpenFile { path }) => match rel_to_mount(mountpoint, &path) { Ok(rel) => match core.open_file(&rel) { - Ok(p) => CtlResponse::FilePath { path: p.display().to_string() }, + Ok(p) => CtlResponse::FilePath { + path: p.display().to_string(), + }, Err(e) => CtlResponse::Error { message: e }, }, Err(e) => CtlResponse::Error { message: e }, }, - Err(e) => CtlResponse::Error { message: format!("bad request: {e}") }, + Err(e) => CtlResponse::Error { + message: format!("bad request: {e}"), + }, }; let mut out = match serde_json::to_vec(&response) { Ok(v) => v, @@ -1618,7 +1730,9 @@ pub fn mount( // `mount`'s blocking contract. let session = Session::new(fs, mountpoint, &config)?.spawn()?; let notifier = session.notifier(); - rt.spawn(run_event_sync(client, scope, core.state, core.cache, notifier)); + rt.spawn(run_event_sync( + client, scope, core.state, core.cache, notifier, + )); let result = session.join(); let _ = std::fs::remove_file(control_socket); diff --git a/crates/pdfs-gui/src/app.rs b/crates/pdfs-gui/src/app.rs index b7cd403..f532b71 100644 --- a/crates/pdfs-gui/src/app.rs +++ b/crates/pdfs-gui/src/app.rs @@ -87,8 +87,7 @@ struct Ui { fn main() -> glib::ExitCode { tracing_subscriber::fmt() .with_env_filter( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| "info".into()), + tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()), ) .init(); @@ -152,7 +151,12 @@ fn build_window(app: &adw::Application) { // Login has no title, so it never appears in the switcher; the three signed-in // pages are titled + iconed so the bottom switcher lists them. stack.add_named(&login_page, Some("login")); - stack.add_titled_with_icon(&main_page, Some("main"), "Account", "avatar-default-symbolic"); + stack.add_titled_with_icon( + &main_page, + Some("main"), + "Account", + "avatar-default-symbolic", + ); stack.add_titled_with_icon(&browser_page, Some("browser"), "Files", "folder-symbolic"); stack.add_titled_with_icon( &gallery_page, @@ -364,9 +368,7 @@ fn build_main_page() -> ( storage_box.set_margin_top(6); storage_box.set_margin_bottom(6); let cache_bar = gtk4::ProgressBar::new(); - let cache_label = gtk4::Label::builder() - .halign(gtk4::Align::Start) - .build(); + let cache_label = gtk4::Label::builder().halign(gtk4::Align::Start).build(); cache_label.add_css_class("dim-label"); storage_box.append(&cache_bar); storage_box.append(&cache_label); @@ -388,7 +390,10 @@ fn build_main_page() -> ( inner.append(&storage_group); inner.append(&pins_group); - let clamp = adw::Clamp::builder().maximum_size(560).child(&inner).build(); + let clamp = adw::Clamp::builder() + .maximum_size(560) + .child(&inner) + .build(); let scroll = gtk4::ScrolledWindow::builder().child(&clamp).build(); ( @@ -424,7 +429,10 @@ fn wire_login(ui: &Rc) { let ui = ui.clone(); glib::spawn_future_local(async move { - let result = rx.recv().await.unwrap_or_else(|_| Err("login cancelled".into())); + let result = rx + .recv() + .await + .unwrap_or_else(|_| Err("login cancelled".into())); ui.login_button.set_sensitive(true); match result { Ok(()) => { @@ -635,7 +643,10 @@ fn repaint_pins(ui: &Rc, pins: &[pdfs_core::cache::Pin]) { /// channel that yields the [`Response`] once. Browser/gallery requests reach the /// network through the daemon, so they must not block the GTK main loop the way /// the cheap [`Request::Status`] poll in [`refresh`] can. -fn spawn_request(socket: PathBuf, req: Request) -> async_channel::Receiver> { +fn spawn_request( + socket: PathBuf, + req: Request, +) -> async_channel::Receiver> { let (tx, rx) = async_channel::bounded(1); std::thread::spawn(move || { let result = send(&socket, &req).map_err(|e| e.to_string()); @@ -714,7 +725,10 @@ fn build_browser_page() -> ( .max_columns(10) .build(); grid.add_css_class("file-grid"); - let grid_scroll = gtk4::ScrolledWindow::builder().vexpand(true).child(&grid).build(); + let grid_scroll = gtk4::ScrolledWindow::builder() + .vexpand(true) + .child(&grid) + .build(); // Column list. let column_view = gtk4::ColumnView::builder() @@ -753,7 +767,10 @@ fn build_browser_page() -> ( inner.append(&status); inner.append(&view_stack); - (inner.upcast(), (model, back, crumb, status, grid, column_view)) + ( + inner.upcast(), + (model, back, crumb, status, grid, column_view), + ) } /// Install the entry factories, columns, activation handlers and the back @@ -986,7 +1003,9 @@ fn activate_entry(ui: &Rc, entry: &DirEntry) { glib::spawn_future_local(async move { match rx.recv().await { Ok(Ok(Response::FilePath { path })) => open_path(&path), - Ok(Ok(Response::Error { message })) => tracing::error!("open file failed: {message}"), + Ok(Ok(Response::Error { message })) => { + tracing::error!("open file failed: {message}") + } _ => tracing::error!("open file request failed"), } }); @@ -1063,7 +1082,10 @@ fn icon_base_for(entry: &DirEntry) -> &'static str { /// Format an epoch-seconds modification time as a short local date. fn format_modified(secs: i64) -> String { match glib::DateTime::from_unix_local(secs) { - Ok(dt) => dt.format("%-d %b %Y").map(|s| s.to_string()).unwrap_or_default(), + Ok(dt) => dt + .format("%-d %b %Y") + .map(|s| s.to_string()) + .unwrap_or_default(), Err(_) => String::new(), } } @@ -1086,9 +1108,7 @@ fn load_browser(ui: &Rc) { Ok(Ok(Response::Entries { entries })) => repaint_browser(&ui, &entries), Ok(Ok(Response::Error { message })) => browser_message(&ui, &message), Ok(Ok(_)) => browser_message(&ui, "Unexpected reply from daemon."), - Ok(Err(_)) | Err(_) => { - browser_message(&ui, "Mount Proton Drive to browse your files.") - } + Ok(Err(_)) | Err(_) => browser_message(&ui, "Mount Proton Drive to browse your files."), } }); } @@ -1207,7 +1227,9 @@ fn wire_gallery(ui: &Rc, grid: >k4::GridView) { glib::spawn_future_local(async move { match rx.recv().await { Ok(Ok(Response::FilePath { path })) => open_path(&path), - Ok(Ok(Response::Error { message })) => tracing::error!("open photo failed: {message}"), + Ok(Ok(Response::Error { message })) => { + tracing::error!("open photo failed: {message}") + } _ => tracing::error!("open photo request failed"), } }); @@ -1258,9 +1280,7 @@ fn load_gallery(ui: &Rc, append: bool) { } Ok(Ok(Response::Error { message })) => gallery_message(&ui, &message), Ok(Ok(_)) => gallery_message(&ui, "Unexpected reply from daemon."), - Ok(Err(_)) | Err(_) => { - gallery_message(&ui, "Mount Proton Drive to see your photos.") - } + Ok(Err(_)) | Err(_) => gallery_message(&ui, "Mount Proton Drive to see your photos."), } }); } @@ -1315,4 +1335,4 @@ fn human_bytes(bytes: u64) -> String { } else { format!("{value:.1} {}", UNITS[unit]) } -} \ No newline at end of file +} diff --git a/crates/pdfs-gui/src/main.rs b/crates/pdfs-gui/src/main.rs index fdd39b6..b19b08a 100644 --- a/crates/pdfs-gui/src/main.rs +++ b/crates/pdfs-gui/src/main.rs @@ -42,7 +42,9 @@ struct DriveTray { /// menu can still say "Logged in …" / "Not logged in" when no mount is running. fn poll_state(socket: &Path, default_mountpoint: &Path) -> DriveState { match send(socket, &Request::Status) { - Ok(Response::Status { mountpoint, pinned, .. }) => DriveState { + Ok(Response::Status { + mountpoint, pinned, .. + }) => DriveState { line: format!("Mounted at {mountpoint} ({pinned} pinned)"), mounted: true, mountpoint: PathBuf::from(mountpoint), @@ -174,8 +176,7 @@ impl Tray for DriveTray { fn main() -> Result<()> { tracing_subscriber::fmt() .with_env_filter( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| "info".into()), + tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()), ) .init();