| 203 | } |
| 204 | |
| 205 | fn scan_range<'a>(&'a self, array: &str, from: Hlc, to: Hlc) -> ArrayResult<OpIter<'a>> { |
| 206 | let prefix = name_prefix(array).ok_or_else(|| ArrayError::InvalidOp { |
| 207 | detail: format!("array name too long: '{array}'"), |
| 208 | })?; |
| 209 | |
| 210 | let txn = self.db.begin_read().map_err(|e| ArrayError::InvalidOp { |
| 211 | detail: format!("scan_range begin_read '{array}': {e}"), |
| 212 | })?; |
| 213 | let table = txn |
| 214 | .open_table(ARRAY_OP_LOG) |
| 215 | .map_err(|e| ArrayError::InvalidOp { |
| 216 | detail: format!("scan_range open_table '{array}': {e}"), |
| 217 | })?; |
| 218 | |
| 219 | let mut results: Vec<ArrayOp> = Vec::new(); |
| 220 | let range = table.iter().map_err(|e| ArrayError::InvalidOp { |
| 221 | detail: format!("scan_range iter '{array}': {e}"), |
| 222 | })?; |
| 223 | for entry in range { |
| 224 | let (k, v) = entry.map_err(|e| ArrayError::InvalidOp { |
| 225 | detail: format!("scan_range entry '{array}': {e}"), |
| 226 | })?; |
| 227 | let key = k.value(); |
| 228 | if !key.starts_with(prefix.as_slice()) { |
| 229 | continue; |
| 230 | } |
| 231 | if let Some(hlc) = hlc_from_key(key, prefix.len()) |
| 232 | && hlc >= from |
| 233 | && hlc <= to |
| 234 | { |
| 235 | match op_codec::decode_op(v.value()) { |
| 236 | Ok(op) => results.push(op), |
| 237 | Err(e) => warn!(error = %e, "scan_range: skipping corrupt entry for '{array}'"), |
| 238 | } |
| 239 | } |
| 240 | } |
| 241 | Ok(Box::new(results.into_iter().map(Ok))) |
| 242 | } |
| 243 | |
| 244 | fn len(&self) -> ArrayResult<u64> { |
| 245 | let txn = self.db.begin_read().map_err(|e| ArrayError::InvalidOp { |