Skip to main content

elasticctl_api/rules/
window.rs

1use 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}