Commit Diff


commit - 777c402909372bd380b73ddc0019630a789db099
commit + e1d7e9cc5877f5639f4aa6b411816d62dfa11bc2
blob - 9b4b51299dbdf1b1309cf4c534c3de42f082f2af
blob + 0e03dee51a4d785712069a253a754c01ba65ed4a
--- src/db.rs
+++ src/db.rs
@@ -40,7 +40,7 @@ pub struct LibraryDb {
 }
 
 const SEP: u8 = 0x00;
-const BPM_KEY_LEN: usize = 8;
+const BPM_KEY_LEN: usize = 8; // f64 big-endian
 
 fn prefix_index_key(value: &[u8], primary_key: &[u8]) -> Vec<u8> {
     let mut buf = Vec::with_capacity(value.len() + 1 + primary_key.len());
@@ -223,16 +223,19 @@ impl LibraryDb {
         Ok(results)
     }
 
-    /// Find tracks by artist name prefix (case-insensitive).
+    /// Find tracks by artist name prefix (case-insensitive), up to `limit` items.
     ///
     /// # Errors
     ///
     /// Returns an error if the index scan or deserialization fails.
-    pub fn find_by_artist(&self, prefix: &str) -> Result<Vec<(String, TrackReport)>> {
+    pub fn find_by_artist(&self, prefix: &str, limit: usize) -> Result<Vec<(String, TrackReport)>> {
         let prefix_lower = prefix.to_lowercase();
         let mut results = Vec::new();
 
         for item in self.artist_tree.scan_prefix(prefix_lower.as_bytes()) {
+            if results.len() >= limit {
+                break;
+            }
             let (idx_key, _) = item.context("Failed to read artist index")?;
             let primary_key_bytes = extract_primary_key(&idx_key)?;
             let primary_key = String::from_utf8(primary_key_bytes.to_vec())
@@ -246,16 +249,19 @@ impl LibraryDb {
         Ok(results)
     }
 
-    /// Find tracks by key signature prefix (case-insensitive).
+    /// Find tracks by key signature prefix (case-insensitive), up to `limit` items.
     ///
     /// # Errors
     ///
     /// Returns an error if the index scan or deserialization fails.
-    pub fn find_by_key(&self, prefix: &str) -> Result<Vec<(String, TrackReport)>> {
+    pub fn find_by_key(&self, prefix: &str, limit: usize) -> Result<Vec<(String, TrackReport)>> {
         let prefix_lower = prefix.to_lowercase();
         let mut results = Vec::new();
 
         for item in self.key_tree.scan_prefix(prefix_lower.as_bytes()) {
+            if results.len() >= limit {
+                break;
+            }
             let (idx_key, _) = item.context("Failed to read key index")?;
             let primary_key_bytes = extract_primary_key(&idx_key)?;
             let primary_key = String::from_utf8(primary_key_bytes.to_vec())
@@ -269,12 +275,17 @@ impl LibraryDb {
         Ok(results)
     }
 
-    /// Find tracks with BPM in the given range (inclusive).
+    /// Find tracks with BPM in the given range (inclusive), up to `limit` items.
     ///
     /// # Errors
     ///
     /// Returns an error if the index scan or deserialization fails.
-    pub fn find_by_bpm_range(&self, min: f64, max: f64) -> Result<Vec<(String, TrackReport)>> {
+    pub fn find_by_bpm_range(
+        &self,
+        min: f64,
+        max: f64,
+        limit: usize,
+    ) -> Result<Vec<(String, TrackReport)>> {
         let mut results = Vec::new();
 
         // Sled sorts longer keys after their prefix, so a bare 8-byte bound would
@@ -283,7 +294,13 @@ impl LibraryDb {
         upper.extend_from_slice(&max.to_be_bytes());
         upper.push(0xFF);
 
-        for item in self.bpm_tree.range(min.to_be_bytes().as_slice()..upper.as_slice()) {
+        for item in self
+            .bpm_tree
+            .range(min.to_be_bytes().as_slice()..upper.as_slice())
+        {
+            if results.len() >= limit {
+                break;
+            }
             let (idx_key, _) = item.context("Failed to read bpm index")?;
             // Primary key starts after 8-byte BPM + 1-byte separator.
             let primary_key_bytes = &idx_key[BPM_KEY_LEN + 1..];
blob - 72073007134f750cc2ebde9fb63715792d56dbe0
blob + 31c72cec257e8f0347a67a35a95a4b52f6ed786d
--- src/main.rs
+++ src/main.rs
@@ -156,6 +156,7 @@ fn main() -> Result<()> {
 
     if is_db_query {
         let db = LibraryDb::open(std::path::Path::new(&db_path))?;
+        let effective_limit = if limit == 0 { usize::MAX } else { limit };
 
         if let Some(mixxx_path) = import_mixxx_path {
             let count = import_mixxx(&db, mixxx_path)?;
@@ -169,21 +170,21 @@ fn main() -> Result<()> {
                 None => eprintln!("No track found for key: {query_key}"),
             }
         } else if let Some(prefix) = artist_prefix {
-            let entries = db.find_by_artist(prefix)?;
+            let entries = db.find_by_artist(prefix, effective_limit)?;
             print_entries(
                 &entries,
                 &format!("No tracks found for artist prefix: {prefix}"),
                 "track(s) found.",
             );
         } else if let Some(prefix) = key_prefix {
-            let entries = db.find_by_key(prefix)?;
+            let entries = db.find_by_key(prefix, effective_limit)?;
             print_entries(
                 &entries,
                 &format!("No tracks found for key prefix: {prefix}"),
                 "track(s) found.",
             );
         } else if let Some((min, max)) = bpm_range {
-            let entries = db.find_by_bpm_range(min, max)?;
+            let entries = db.find_by_bpm_range(min, max, effective_limit)?;
             print_entries(
                 &entries,
                 &format!("No tracks found with BPM between {min} and {max}"),
@@ -191,8 +192,7 @@ fn main() -> Result<()> {
             );
         } else if let Some(pattern) = search_pattern {
             let re = Regex::new(pattern)?;
-            let list_limit = if limit == 0 { usize::MAX } else { limit };
-            let entries = db.list(list_limit)?;
+            let entries = db.list(effective_limit)?;
             let matches_found: Vec<_> = entries
                 .into_iter()
                 .filter(|(key, report)| re.is_match(key) || re.is_match(&report.to_string()))
@@ -203,7 +203,6 @@ fn main() -> Result<()> {
                 "match(es) found.",
             );
         } else {
-            let effective_limit = if limit == 0 { usize::MAX } else { limit };
             let entries = db.list(effective_limit)?;
             let total = db.count()?;
             if entries.is_empty() {