Skip to main content

brows3r_lib/commands/
locks_cmd.rs

1//! Tauri commands for the resource lock registry.
2//!
3//! # Commands
4//!
5//! - `locks_list`           — return active locks, optionally filtered by scope.
6//! - `lock_release_stale`   — manual last-resort override to release a specific
7//!                            lock by ID (e.g. for a stuck operation).
8
9use tauri::State;
10
11use crate::{
12    error::AppError,
13    ids::{BucketId, ObjectKey, ProfileId},
14    locks::{LockId, LockRegistryHandle, LockScope, ResourceLock},
15};
16
17// ---------------------------------------------------------------------------
18// LockScopeDto — IPC-friendly version of LockScope
19// ---------------------------------------------------------------------------
20
21/// IPC-friendly (camelCase) mirror of `LockScope` used as a command argument.
22///
23/// The frontend passes `{ profile, bucket?, prefix?, key? }` in camelCase;
24/// this DTO converts to the domain type.
25#[derive(Debug, serde::Deserialize)]
26#[serde(rename_all = "camelCase")]
27pub struct LockScopeDto {
28    pub profile: ProfileId,
29    pub bucket: Option<BucketId>,
30    pub prefix: Option<String>,
31    pub key: Option<ObjectKey>,
32}
33
34impl From<LockScopeDto> for LockScope {
35    fn from(dto: LockScopeDto) -> Self {
36        LockScope {
37            profile: dto.profile,
38            bucket: dto.bucket,
39            prefix: dto.prefix,
40            key: dto.key,
41        }
42    }
43}
44
45// ---------------------------------------------------------------------------
46// locks_list
47// ---------------------------------------------------------------------------
48
49/// Return all active locks, optionally filtered to those whose scope
50/// intersects `scope`.
51///
52/// When `scope` is `None` every active lock is returned.
53#[tauri::command]
54pub async fn locks_list(
55    scope: Option<LockScopeDto>,
56    registry: State<'_, LockRegistryHandle>,
57) -> Result<Vec<ResourceLock>, AppError> {
58    let filter = scope.map(LockScope::from);
59    Ok(registry.inner().list(filter.as_ref()))
60}
61
62// ---------------------------------------------------------------------------
63// lock_release_stale
64// ---------------------------------------------------------------------------
65
66/// Manually release a specific lock by ID.
67///
68/// This is a last-resort override (e.g. to unstick a crashed operation).
69/// The caller is responsible for understanding the consequences.
70///
71/// Returns `AppError::NotFound` if the lock does not exist.
72#[tauri::command]
73pub async fn lock_release_stale(
74    lock_id: LockId,
75    registry: State<'_, LockRegistryHandle>,
76) -> Result<(), AppError> {
77    // We discard the returned lock; the caller does not need the scope here.
78    registry.inner().release(&lock_id)?;
79    Ok(())
80}