struct Provenance {
name: String,
stored: String,
}
#[derive(Default)]
pub struct SavedSearchBreadcrumb {
pinned: Option<Provenance>,
}
impl SavedSearchBreadcrumb {
pub fn set(&mut self, name: Option<String>, stored_query: &str) {
self.pinned = match name {
Some(name) if !stored_query.trim().is_empty() => Some(Provenance {
name,
stored: stored_query.trim().to_string(),
}),
_ => None,
};
}
pub fn on_query_consumed(
&mut self,
accepted: Option<String>,
query: &str,
query_is_blank: bool,
) {
match accepted {
Some(name) => self.set(if query_is_blank { None } else { Some(name) }, query),
None if query_is_blank => self.pinned = None,
None => {}
}
}
pub fn name(&self) -> Option<&str> {
self.pinned.as_ref().map(|p| p.name.as_str())
}
pub fn label(&self, query: &str) -> Option<String> {
let p = self.pinned.as_ref()?;
Some(if query.trim() != p.stored {
format!("{} • edited", p.name)
} else {
p.name.clone()
})
}
pub fn border_title(&self, query: &str, fallback: &str) -> String {
match self.label(query) {
Some(label) => format!(" ‹ {label} › "),
None => fallback.to_string(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn pinned(stored: &str) -> SavedSearchBreadcrumb {
let mut b = SavedSearchBreadcrumb::default();
b.set(Some("todo".into()), stored);
b
}
#[test]
fn unedited_label_is_the_name() {
assert_eq!(pinned("#todo").label("#todo").as_deref(), Some("todo"));
}
#[test]
fn diverged_query_marks_edited() {
assert_eq!(
pinned("#todo").label("#todox").as_deref(),
Some("todo • edited")
);
}
#[test]
fn order_directive_change_is_edited() {
assert_eq!(
pinned("#todo").label("#todo or:title").as_deref(),
Some("todo • edited")
);
}
#[test]
fn name_returns_pinned_provenance_without_edited_marker() {
let b = pinned("#todo");
assert_eq!(b.name(), Some("todo"));
let empty = SavedSearchBreadcrumb::default();
assert_eq!(empty.name(), None);
}
#[test]
fn set_with_blank_query_does_not_pin() {
let mut b = SavedSearchBreadcrumb::default();
b.set(Some("todo".into()), " ");
assert_eq!(b.label(" "), None);
}
#[test]
fn accept_with_blank_expansion_clears_rather_than_pins() {
let mut b = SavedSearchBreadcrumb::default();
b.on_query_consumed(Some("empty".into()), "", true);
assert_eq!(b.label(""), None);
}
#[test]
fn blank_query_clears_sticky_breadcrumb() {
let mut b = pinned("#todo");
b.on_query_consumed(None, "", true);
assert_eq!(b.label(""), None);
}
#[test]
fn non_blank_edit_keeps_sticky_breadcrumb() {
let mut b = pinned("#todo");
b.on_query_consumed(None, "#todox", false);
assert_eq!(b.label("#todox").as_deref(), Some("todo • edited"));
}
#[test]
fn border_title_chevrons_label_else_fallback() {
assert_eq!(
pinned("#todo").border_title("#todo", " Query"),
" ‹ todo › "
);
assert_eq!(
SavedSearchBreadcrumb::default().border_title("#todo", " Query"),
" Query"
);
}
}