Skip to main content

agent_playground/
listing.rs

1//! Utilities for listing configured playgrounds in a human-readable table.
2//!
3//! This module is used by the CLI `list` subcommand and focuses on
4//! presentation-oriented output.
5
6use std::collections::BTreeMap;
7
8use anyhow::Result;
9
10use crate::config::{AppConfig, PlaygroundDefinition};
11
12/// Loads application configuration and prints all configured playgrounds.
13///
14/// The output is a simple fixed-width table with `PLAYGROUND` and
15/// `DESCRIPTION` columns. If no playgrounds are configured, a fallback
16/// message is printed instead.
17///
18/// # Errors
19///
20/// Returns an error when configuration loading fails.
21pub fn list_playgrounds() -> Result<()> {
22    let config = AppConfig::load()?;
23    print!("{}", format_playgrounds(&config.playgrounds));
24    Ok(())
25}
26
27fn format_playgrounds(playgrounds: &BTreeMap<String, PlaygroundDefinition>) -> String {
28    if playgrounds.is_empty() {
29        return "No playgrounds found.\n".to_string();
30    }
31
32    let id_width = playgrounds
33        .keys()
34        .map(|id| id.len())
35        .max()
36        .unwrap_or("PLAYGROUND".len())
37        .max("PLAYGROUND".len());
38
39    let mut output = String::new();
40    output.push_str(&format!(
41        "{:<id_width$}  {}\n",
42        "PLAYGROUND",
43        "DESCRIPTION",
44        id_width = id_width
45    ));
46
47    for playground in playgrounds.values() {
48        output.push_str(&format!(
49            "{:<id_width$}  {}\n",
50            playground.id,
51            playground.description,
52            id_width = id_width
53        ));
54    }
55
56    output
57}
58
59#[cfg(test)]
60mod tests {
61    use std::{collections::BTreeMap, path::PathBuf};
62
63    use crate::config::{PlaygroundConfig, PlaygroundDefinition};
64
65    use super::format_playgrounds;
66
67    #[test]
68    fn formats_playground_table() {
69        let mut playgrounds = BTreeMap::new();
70        playgrounds.insert(
71            "demo".to_string(),
72            PlaygroundDefinition {
73                id: "demo".to_string(),
74                description: "Demo playground".to_string(),
75                directory: PathBuf::from("/tmp/demo"),
76                config_file: PathBuf::from("/tmp/demo/apg.toml"),
77                playground: PlaygroundConfig::default(),
78            },
79        );
80        playgrounds.insert(
81            "longer-id".to_string(),
82            PlaygroundDefinition {
83                id: "longer-id".to_string(),
84                description: "Longer playground".to_string(),
85                directory: PathBuf::from("/tmp/longer-id"),
86                config_file: PathBuf::from("/tmp/longer-id/apg.toml"),
87                playground: PlaygroundConfig::default(),
88            },
89        );
90
91        assert_eq!(
92            format_playgrounds(&playgrounds),
93            "PLAYGROUND  DESCRIPTION\n\
94demo        Demo playground\n\
95longer-id   Longer playground\n"
96        );
97    }
98
99    #[test]
100    fn formats_empty_playground_list() {
101        assert_eq!(
102            format_playgrounds(&BTreeMap::new()),
103            "No playgrounds found.\n"
104        );
105    }
106}