elasticctl_api/rules/
window.rs1use std::collections::HashSet;
2
3use super::{RuleFilter, find};
4use crate::model::Rule;
5use elasticctl_core::{Error, ErrorKind, Result, Transport};
6
7pub const WINDOW_PAGE: u32 = 500;
8
9pub const WINDOW_MAX_RULES: u64 = 4_000;
10
11const SORT: &str = "&sort_field=created_at&sort_order=asc";
12
13const PASSES: usize = 2;
14
15pub async fn find_window(t: &Transport, filter: &RuleFilter) -> Result<Vec<Rule>> {
16 for _ in 0..PASSES {
17 if let Some(rules) = read_pass(t, filter).await? {
18 return Ok(rules);
19 }
20 }
21 Err(Error::new(
22 ErrorKind::Conflict,
23 "the matching rules changed during both corpus read passes; retry the read",
24 ))
25}
26
27async fn read_pass(t: &Transport, filter: &RuleFilter) -> Result<Option<Vec<Rule>>> {
28 let (mut rules, total) = find(t, filter, 1, WINDOW_PAGE, SORT).await?;
29 if total > WINDOW_MAX_RULES {
30 return Err(Error::new(
31 ErrorKind::Unsupported,
32 format!(
33 "{total} rules match, more than the {WINDOW_MAX_RULES} a corpus read accepts. \
34 Narrow the selection with a filter."
35 ),
36 ));
37 }
38 let last_page = total.div_ceil(u64::from(WINDOW_PAGE)) as u32;
39 let mut same_total = true;
40 for page in 2..=last_page {
41 let (more, page_total) = find(t, filter, page, WINDOW_PAGE, SORT).await?;
42 same_total &= page_total == total;
43 rules.extend(more);
44 }
45 let ids = rules
46 .iter()
47 .map(Rule::rule_id)
48 .collect::<Result<HashSet<_>>>()?;
49 let verified = same_total && rules.len() as u64 == total && ids.len() == rules.len();
50 Ok(verified.then_some(rules))
51}