Add location filter to library search
LibraryFilter.location does a case-insensitive substring match against the stored location (e.g. "Box 1"), mirroring the existing text filter. Tested for substring/case-insensitivity and the empty-string no-op case. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -81,6 +81,10 @@ impl Catalog {
|
||||
);
|
||||
vals.push(Value::Integer(deck_id));
|
||||
}
|
||||
if let Some(loc) = filter.location.as_ref().map(|l| l.trim()).filter(|l| !l.is_empty()) {
|
||||
sql.push_str(" AND LOWER(lc.location) LIKE ?");
|
||||
vals.push(Value::Text(format!("%{}%", loc.to_lowercase())));
|
||||
}
|
||||
|
||||
sql.push_str(" ORDER BY oc.name COLLATE NOCASE");
|
||||
|
||||
@@ -226,4 +230,39 @@ mod tests {
|
||||
}).unwrap());
|
||||
assert_eq!(in_deck, vec!["Lightning Bolt"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn location_filter_is_case_insensitive_substring() {
|
||||
let fx = owned_fixture();
|
||||
let cat = &fx.catalog;
|
||||
cat.set_location("lightning-bolt", "Box 1").unwrap();
|
||||
cat.set_location("sol-ring", "Box 12").unwrap();
|
||||
cat.set_location("serra-angel", "Binder A").unwrap();
|
||||
// llanowar-elves and deathrite-shaman are left with no location.
|
||||
|
||||
let box_matches = names(&cat.search_library(&LibraryFilter {
|
||||
location: Some("box".into()),
|
||||
..Default::default()
|
||||
}).unwrap());
|
||||
assert_eq!(box_matches, vec!["Lightning Bolt", "Sol Ring"]);
|
||||
|
||||
let exact = names(&cat.search_library(&LibraryFilter {
|
||||
location: Some("Box 1".into()),
|
||||
..Default::default()
|
||||
}).unwrap());
|
||||
assert_eq!(exact, vec!["Lightning Bolt", "Sol Ring"]); // substring, so "Box 1" ⊆ "Box 12" too
|
||||
|
||||
let none = cat.search_library(&LibraryFilter {
|
||||
location: Some("nonexistent".into()),
|
||||
..Default::default()
|
||||
}).unwrap();
|
||||
assert!(none.is_empty());
|
||||
|
||||
// Empty/whitespace location text is treated as "no filter".
|
||||
let all = cat.search_library(&LibraryFilter {
|
||||
location: Some(" ".into()),
|
||||
..Default::default()
|
||||
}).unwrap();
|
||||
assert_eq!(all.len(), 5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,4 +181,7 @@ pub struct LibraryFilter {
|
||||
pub keywords: Vec<String>,
|
||||
/// Restrict to cards assigned to this deck id.
|
||||
pub in_deck: Option<i64>,
|
||||
/// Case-insensitive substring match against the card's stored location
|
||||
/// (e.g. "Box 1"). Cards with no location set never match a non-empty value.
|
||||
pub location: Option<String>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user