stmo_cli/commands/
discover.rs1#![allow(clippy::missing_errors_doc)]
2
3use crate::api::RedashClient;
4use anyhow::Result;
5use std::path::Path;
6
7fn format_resource_line(id: u64, name: &str, url: &str, is_draft: bool) -> String {
8 let draft = if is_draft { " [DRAFT]" } else { "" };
9 format!(" {id} - {name}{draft}\n {url}")
10}
11
12fn next_steps(queries_dir_exists: bool, first_query_id: Option<u64>) -> String {
15 if let Some(id) = first_query_id {
16 if queries_dir_exists {
17 format!("\nNext: stmo-cli fetch {id}")
18 } else {
19 format!(
20 "\nNo queries/ directory here.\nRun 'stmo-cli init <dir>' to set one up, \
21 then 'stmo-cli fetch {id}'."
22 )
23 }
24 } else {
25 "\nNo queries of your own on Redash yet.\nTry 'stmo-cli discover --search <term>' \
26 to find other people's."
27 .to_string()
28 }
29}
30
31pub async fn discover(client: &RedashClient, search: Option<&str>, limit: usize) -> Result<()> {
32 if let Some(term) = search {
33 discover_by_search(client, term, limit).await
34 } else {
35 discover_own_queries(client).await
36 }
37}
38
39async fn discover_own_queries(client: &RedashClient) -> Result<()> {
40 println!("Fetching your queries from Redash...\n");
41
42 let queries = client.fetch_all_queries().await?;
43
44 println!("=== QUERIES ({}) ===\n", queries.len());
45 for query in &queries {
46 let archived = if query.is_archived { " [ARCHIVED]" } else { "" };
47 let draft = if query.is_draft { " [DRAFT]" } else { "" };
48 println!(" {} - {}{}{}", query.id, query.name, archived, draft);
49 }
50
51 println!(
52 "{}",
53 next_steps(Path::new("queries").exists(), queries.first().map(|q| q.id))
54 );
55
56 Ok(())
57}
58
59async fn discover_by_search(client: &RedashClient, term: &str, limit: usize) -> Result<()> {
60 println!("Searching for '{term}'...\n");
61
62 let (queries, dashboards) = tokio::join!(
63 client.search_queries(term, limit),
64 client.search_dashboards(term, limit),
65 );
66 let queries = queries?;
67 let dashboards = dashboards?;
68
69 let base = client.base_url();
70
71 println!("=== QUERIES ({}) ===\n", queries.len());
72 if queries.is_empty() {
73 println!(" No queries found for '{term}'.");
74 } else {
75 for query in &queries {
76 let url = format!("{base}/queries/{}", query.id);
77 println!(
78 "{}",
79 format_resource_line(query.id, &query.name, &url, query.is_draft)
80 );
81 }
82 if queries.len() == limit {
83 println!("\n (showing first {limit}; raise --limit for more)");
84 }
85 }
86
87 println!("\n=== DASHBOARDS ({}) ===\n", dashboards.len());
88 if dashboards.is_empty() {
89 println!(" No dashboards found for '{term}'.");
90 } else {
91 for dashboard in &dashboards {
92 let url = format!("{base}/dashboard/{}", dashboard.slug);
93 println!(
94 "{}",
95 format_resource_line(dashboard.id, &dashboard.name, &url, dashboard.is_draft)
96 );
97 }
98 if dashboards.len() == limit {
99 println!("\n (showing first {limit}; raise --limit for more)");
100 }
101 }
102
103 Ok(())
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn format_resource_line_plain() {
112 let line = format_resource_line(
113 42,
114 "My Dashboard",
115 "https://example.com/dashboard/my",
116 false,
117 );
118 assert!(line.contains("42"));
119 assert!(line.contains("My Dashboard"));
120 assert!(line.contains("https://example.com/dashboard/my"));
121 assert!(!line.contains("[DRAFT]"));
122 }
123
124 #[test]
125 fn format_resource_line_draft() {
126 let line = format_resource_line(7, "WIP", "https://example.com/dashboard/wip", true);
127 assert!(line.contains("[DRAFT]"));
128 assert!(line.contains("WIP"));
129 }
130
131 #[test]
132 fn next_steps_dir_exists_suggests_fetch() {
133 let message = next_steps(true, Some(123));
134 assert!(message.contains("fetch 123"));
135 assert!(!message.contains("init"));
136 }
137
138 #[test]
139 fn next_steps_dir_missing_suggests_init() {
140 let message = next_steps(false, Some(123));
141 assert!(message.contains("queries/"));
142 assert!(message.contains("stmo-cli init"));
143 }
144
145 #[test]
146 fn next_steps_no_queries_suggests_search() {
147 let message = next_steps(false, None);
148 assert!(message.contains("--search"));
149 assert!(!message.contains("stmo-cli init"));
150 }
151}