mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-04 08:56:59 +01:00
The trash is a flat list of uids, but a delete has a shape: a folder, and often items inside it that were trashed separately beforehand. Restoring one of those in isolation is not what it means — a folder came back without the contents deleted with it, and a file came back inside a folder that is still trashed, which is to say nowhere the user can see. - Schema 29: `trash.parent_uid`, filled by the trash refresh from the node's parent, plus an index for the walk. - `expand_restore` takes the connected piece of the trashed tree around what was asked for (every trashed descendant, every trashed ancestor) and orders it into waves, shallowest first: the server cannot put a node back under a parent that is still trashed. A uid the listing does not know about is restored on its own, because the listing is materialised in chunks and can be stale. - `Core::restore` restores wave by wave, still streaming per-node outcomes, so one node the server refuses leaves the rest restored. The SDK bump brings the 6cbf2f44 port: permanent API errors are no longer retried on block transfers, expired block URLs are re-minted on the whole-file and public-link download paths, and a server-supplied Retry-After is capped at 120 s.
86 lines
2.7 KiB
Rust
86 lines
2.7 KiB
Rust
//! Trashed nodes, persisted so the trash view opens without a round-trip.
|
|
|
|
use rusqlite::params;
|
|
|
|
use super::Db;
|
|
use crate::Result;
|
|
|
|
pub struct StoredTrash {
|
|
pub uid: String,
|
|
pub name: String,
|
|
pub is_dir: bool,
|
|
pub size: i64,
|
|
pub mtime: i64,
|
|
/// The folder this node was trashed from, when it is known. `None` for a
|
|
/// row written before schema v29, and for a node whose parent the server
|
|
/// did not report.
|
|
pub parent_uid: Option<String>,
|
|
}
|
|
|
|
impl Db {
|
|
pub fn trash_replace(&self, items: &[StoredTrash]) -> Result<()> {
|
|
let mut conn = self.conn.lock();
|
|
let tx = conn.transaction()?;
|
|
tx.execute("DELETE FROM trash", [])?;
|
|
{
|
|
let mut stmt = tx.prepare(
|
|
"INSERT INTO trash (uid, name, is_dir, size, mtime, parent_uid) \
|
|
VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
|
|
)?;
|
|
for item in items {
|
|
stmt.execute(params![
|
|
item.uid,
|
|
item.name,
|
|
item.is_dir as i64,
|
|
item.size,
|
|
item.mtime,
|
|
item.parent_uid
|
|
])?;
|
|
}
|
|
}
|
|
tx.commit()?;
|
|
Ok(())
|
|
}
|
|
|
|
/// The persisted trash listing, folders first then by name — the order the
|
|
/// Trash page shows it in.
|
|
pub fn trash_list(&self) -> Result<Vec<StoredTrash>> {
|
|
let conn = self.read();
|
|
let mut stmt = conn.prepare(
|
|
"SELECT uid, name, is_dir, size, mtime, parent_uid FROM trash
|
|
ORDER BY is_dir DESC, name COLLATE NOCASE",
|
|
)?;
|
|
let rows = stmt.query_map([], |r| {
|
|
Ok(StoredTrash {
|
|
uid: r.get(0)?,
|
|
name: r.get(1)?,
|
|
is_dir: r.get::<_, i64>(2)? != 0,
|
|
size: r.get(3)?,
|
|
mtime: r.get(4)?,
|
|
parent_uid: r.get(5)?,
|
|
})
|
|
})?;
|
|
let mut items = Vec::new();
|
|
for row in rows {
|
|
items.push(row?);
|
|
}
|
|
Ok(items)
|
|
}
|
|
|
|
/// Every trashed node paired with the folder it was trashed from, for the
|
|
/// restore's tree walk. Kept separate from [`Db::trash_list`] because the
|
|
/// restore wants only the links, not the names, sizes or ordering.
|
|
pub fn trash_parents(&self) -> Result<Vec<(String, Option<String>)>> {
|
|
let conn = self.read();
|
|
let mut stmt = conn.prepare("SELECT uid, parent_uid FROM trash")?;
|
|
let rows = stmt.query_map([], |r| Ok((r.get(0)?, r.get(1)?)))?;
|
|
let mut pairs = Vec::new();
|
|
for row in rows {
|
|
pairs.push(row?);
|
|
}
|
|
Ok(pairs)
|
|
}
|
|
|
|
// ---- device sync (devices.md) -----------------------------------------
|
|
}
|