Skip to main content

warden/commands/
query.rs

1//! `warden query`.
2
3use crate::output::{emit, Report};
4use crate::reports::query::{self, DEFAULT_GROUP_BY};
5use crate::reports::ReportError;
6use crate::store::Scanner;
7
8use super::Env;
9
10/// Roll the window up over the requested dimensions.
11pub fn run(env: &Env<'_>, group_by: Option<&str>) -> Result<Report, ReportError> {
12    let dims = query::parse_dimensions(group_by.unwrap_or(DEFAULT_GROUP_BY))?;
13    env.pre_ingest()?;
14    let report = query::build(&Scanner::new(env.paths.clone()), &env.ctx(), &dims)?;
15    emit(&report, env.json)?;
16    Ok(report)
17}
18
19#[cfg(test)]
20mod tests {
21    use super::*;
22    use crate::cli::TimeWindow;
23    use crate::config::Config;
24    use crate::reports::testkit::*;
25    use crate::store::StorePaths;
26
27    fn env<'a>(paths: &'a StorePaths, config: &'a Config) -> Env<'a> {
28        Env {
29            config,
30            paths,
31            window: TimeWindow::all(),
32            project: None,
33            json: false,
34            no_ingest: true,
35            include_sidechain: true,
36        }
37    }
38
39    #[test]
40    fn defaults_to_grouping_by_project() {
41        let (_dir, paths) = store(&[used("a", ms(2026, 8, 4, 8), "acme", "opus", 10, 1)]);
42        let config = Config::default();
43        let report = run(&env(&paths, &config), None).unwrap();
44        assert_eq!(report.name, "query");
45        assert_eq!(report.json_rows[0]["project"], "acme");
46    }
47
48    #[test]
49    fn an_unknown_dimension_fails_before_anything_is_scanned() {
50        let (_dir, paths) = store(&[used("a", ms(2026, 8, 4, 8), "acme", "opus", 10, 1)]);
51        let config = Config::default();
52        let err = run(&env(&paths, &config), Some("project,colour")).unwrap_err();
53        assert!(err.to_string().contains("colour"), "{err}");
54    }
55}