prompt refactor

This commit is contained in:
nvrl committed 2026-07-22 11:27:57 +02:00
1 parent 39ab4aae48
commit ea56818a80
5 files changed
+161 -2

No files matched your search

+6
View File
@@ -678,6 +678,11 @@ pub struct SearchHit {
pub pinned: bool,
/// Node uid in `volume~link` form.
pub uid: String,
/// Absolute path through the most specific active on-demand mount, when the
/// node is covered by one. Clients should prefer this for folders and
/// streamable media, and fall back to `mountpoint/path` when absent.
#[serde(default)]
pub mounted_path: Option<String>,
/// Relevance assigned by the daemon. Higher scores sort first. Legacy
/// daemons omit it, in which case clients retain the daemon's result order.
#[serde(default)]
@@ -1369,6 +1374,7 @@ mod tests {
modified: 123,
pinned: true,
uid: "vol~link".into(),
mounted_path: Some("/home/me/Videos/report.pdf".into()),
score: 985,
}],
local_hits: vec![LocalHit {
+34
View File
@@ -160,6 +160,40 @@ impl Db {
Ok(count > 0)
}
/// Resolve `uid` relative to an ancestor node.
///
/// This is used when a remote subtree has its own local sync mount: the
/// sync folder stores the ancestor UID, while search results identify the
/// selected descendant. Returning `None` for an unrelated or incomplete
/// chain keeps callers from accidentally joining a Drive-wide path onto the
/// wrong mountpoint. The ancestor itself resolves to the empty path.
pub fn path_relative_to(&self, ancestor_uid: &str, uid: &str) -> Result<Option<String>> {
let conn = self.conn.lock();
let mut stmt = conn.prepare(
"WITH RECURSIVE anc(uid, parent_uid, name, depth) AS (
SELECT uid, parent_uid, name, 0 FROM nodes WHERE uid = ?1
UNION ALL
SELECT n.uid, n.parent_uid, n.name, anc.depth + 1
FROM nodes n JOIN anc ON n.uid = anc.parent_uid
WHERE anc.depth < 1024
)
SELECT uid, name FROM anc ORDER BY depth",
)?;
let rows = stmt.query_map(params![uid], |row| {
Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?))
})?;
let mut parts = Vec::new();
for row in rows {
let (current_uid, name) = row?;
if current_uid == ancestor_uid {
parts.reverse();
return Ok(Some(parts.join("/")));
}
parts.push(name);
}
Ok(None)
}
/// Full-text search over node names, newest schema's trigram index giving
/// substring (not just prefix) matches. Returns up to `limit` non-trashed
/// hits, each with its mountpoint-relative path resolved. Queries shorter
+91
View File
@@ -5,6 +5,7 @@ use crate::control::{ActivityEntry, ActivityKind};
use crate::localindex::LocalEntry;
use proton_drive_rs::proton_sdk::ids::NodeUid;
use proton_drive_rs::{Node, NodeKind};
use serde_json::json;
/// `Db::open` applies the pragmas that `open_in_memory` skips, so the WAL
/// settings can only be checked against a real file.
@@ -1181,6 +1182,96 @@ fn pending_mode_is_queued_until_the_mode_is_reached() {
assert_eq!(folder.pending_mode, None);
}
#[test]
fn node_path_resolves_relative_to_a_sync_root_uid() {
let db = Db::open_in_memory().unwrap();
let root = node_from(serde_json::Value::Null, "root", "My Files", json!("Folder"));
let videos = node_from(json!(uid("root")), "videos", "Videos", json!("Folder"));
let anime = node_from(json!(uid("videos")), "anime", "anime", json!("Folder"));
let show = node_from(json!(uid("anime")), "show", "Oshi no Ko", json!("Folder"));
let episode = node_from(
json!(uid("show")),
"episode",
"episode 01.mkv",
json!({"File": {
"media_type": "video/x-matroska",
"total_size_on_storage": 42,
"claimed_size": 42,
"claimed_modification_time": null
}}),
);
db.upsert_nodes(&[root, videos, anime, show, episode])
.unwrap();
assert_eq!(
db.path_relative_to(&uid("videos").to_string(), &uid("episode").to_string())
.unwrap()
.as_deref(),
Some("anime/Oshi no Ko/episode 01.mkv")
);
assert_eq!(
db.path_relative_to(&uid("videos").to_string(), &uid("videos").to_string())
.unwrap()
.as_deref(),
Some("")
);
assert_eq!(
db.path_relative_to(&uid("anime").to_string(), &uid("episode").to_string())
.unwrap()
.as_deref(),
Some("Oshi no Ko/episode 01.mkv")
);
}
#[test]
fn node_path_rejects_unrelated_missing_and_broken_ancestor_chains() {
let db = Db::open_in_memory().unwrap();
let root = node_from(serde_json::Value::Null, "root", "My Files", json!("Folder"));
let videos = node_from(json!(uid("root")), "videos", "Videos", json!("Folder"));
let episode = node_from(
json!(uid("videos")),
"episode",
"episode.mkv",
json!({"File": {
"media_type": "video/x-matroska",
"total_size_on_storage": 42,
"claimed_size": 42,
"claimed_modification_time": null
}}),
);
let unrelated = node_from(
json!(uid("root")),
"documents",
"Documents",
json!("Folder"),
);
let orphan = node_from(
json!(uid("missing-parent")),
"orphan",
"orphan.mkv",
json!({"File": {
"media_type": "video/x-matroska",
"total_size_on_storage": 42,
"claimed_size": 42,
"claimed_modification_time": null
}}),
);
db.upsert_nodes(&[root, videos, episode, unrelated, orphan])
.unwrap();
for (ancestor, descendant) in [
("documents", "episode"),
("videos", "missing-node"),
("videos", "orphan"),
] {
assert_eq!(
db.path_relative_to(&uid(ancestor).to_string(), &uid(descendant).to_string())
.unwrap(),
None
);
}
}
#[test]
fn cache_index_touch_access_and_lru_order() {
let db = Db::open_in_memory().unwrap();
+20
View File
@@ -1784,6 +1784,24 @@ impl Core {
.collect())
}
/// Return an absolute path through the most specific live on-demand mount
/// covering `uid`. A node may be below several configured roots, so prefer
/// the root leaving the shortest descendant path.
fn mounted_search_path(&self, uid: &str) -> Option<String> {
let live_mounts = self.mounts.lock();
let folders = self.db.sync_folder_list().ok()?;
folders
.into_iter()
.filter(|folder| folder.mode == "ondemand" && live_mounts.contains_key(&folder.id))
.filter_map(|folder| {
let relative = self.db.path_relative_to(&folder.remote_uid, uid).ok()??;
let depth = relative.split('/').count();
Some((depth, PathBuf::from(folder.local_path).join(relative)))
})
.min_by_key(|(depth, _)| *depth)
.map(|(_, path)| path.to_string_lossy().into_owned())
}
/// Full-text search node names against the local SQLite index, mapping each
/// DB hit to the wire [`SearchHit`] (resolving live pin state from the cache,
/// which the DB doesn't track). Pure local lookup — never hits the network.
@@ -1802,6 +1820,7 @@ impl Core {
modified: h.node.modification_time,
pinned: self.cache.is_pinned(&h.node.uid),
uid: h.node.uid.to_string(),
mounted_path: self.mounted_search_path(&h.node.uid.to_string()),
score: 0,
})
.collect())
@@ -1865,6 +1884,7 @@ impl Core {
modified: hit.node.modification_time,
pinned,
uid: hit.node.uid.to_string(),
mounted_path: self.mounted_search_path(&hit.node.uid.to_string()),
score,
})
})
+10 -2
View File
@@ -756,6 +756,7 @@ impl Ui {
modified: 0,
pinned: true,
uid: pin.uid,
mounted_path: None,
score: 0,
})
.collect(),
@@ -1090,14 +1091,20 @@ impl Ui {
// this handles non-recursively pinned folders without mistaking
// them for files and sending an invalid OpenFile request.
if self.rendered_query.borrow().is_empty() {
let path = mounted_path(&self.mountpoint.borrow(), &drive.path);
let path = drive.mounted_path.as_deref().map_or_else(
|| mounted_path(&self.mountpoint.borrow(), &drive.path),
PathBuf::from,
);
xdg_open(&path);
self.dismiss();
return;
}
match drive_activation(&drive.name, drive.is_dir) {
DriveActivation::Folder | DriveActivation::MountedMedia => {
let path = mounted_path(&self.mountpoint.borrow(), &drive.path);
let path = drive.mounted_path.as_deref().map_or_else(
|| mounted_path(&self.mountpoint.borrow(), &drive.path),
PathBuf::from,
);
xdg_open(&path);
self.dismiss();
return;
@@ -1442,6 +1449,7 @@ mod tests {
modified: 1,
pinned: false,
uid: "v~a".into(),
mounted_path: None,
score: 100,
}),
Hit::Local(LocalHit {