LCOV - code coverage report
Current view: top level - dashql-cli/src/agent - commands.rs Coverage Total Hit
Test: DashQL 6e74948 Lines: 100.0 % 46 46
Test Date: 2026-11-06 17:46:50

            Line data    Source code
       1              : //! The commands of `dashql agent` that agents call. The table makes the usage of the command line tool and the
       2              : //! command table of the guide and of the instructions of harness runs.
       3              : 
       4              : /// A command of `dashql agent` for the open DashQL session
       5              : pub struct AgentCommand {
       6              :     /// The words of the command, e.g. `vis edit`
       7              :     pub name: &'static str,
       8              :     /// The arguments and the options of the command
       9              :     pub args: &'static str,
      10              :     /// What the command does, Markdown
      11              :     pub does: &'static str,
      12              : }
      13              : 
      14              : impl AgentCommand {
      15              :     /// The command of a call to the session, e.g. `vis.edit`. None for `status`, which the server answers, and for
      16              :     /// `guide`, which the command line tool answers.
      17          109 :     pub fn call(&self) -> Option<String> {
      18          109 :         (self.name != "status" && self.name != "guide").then(|| self.name.replace(' ', "."))
      19          109 :     }
      20              : 
      21          804 :     fn synopsis(&self) -> String {
      22          804 :         if self.args.is_empty() { self.name.to_string() } else { format!("{} {}", self.name, self.args) }
      23          804 :     }
      24              : }
      25              : 
      26              : pub const AGENT_COMMANDS: [AgentCommand; 12] = [
      27              :     AgentCommand { name: "status", args: "", does: "Connected clients, sessions, the focused session" },
      28              :     AgentCommand { name: "guide", args: "", does: "The commands and how DashQL works, read it first" },
      29              :     AgentCommand { name: "scripts", args: "", does: "Scripts of the session with their connections and attachments" },
      30              :     AgentCommand { name: "read", args: "<script>", does: "Script text" },
      31              :     AgentCommand {
      32              :         name: "edit",
      33              :         args: "<script> [--file f] [--connection c]",
      34              :         does: "Replace or create a script with the text of the file or stdin",
      35              :     },
      36              :     AgentCommand {
      37              :         name: "catalog",
      38              :         args: "[connection]",
      39              :         does: "`CREATE TABLE` statements of the tables you can query",
      40              :     },
      41              :     AgentCommand { name: "lint", args: "<script>", does: "Parser and analyzer diagnostics of a script" },
      42              :     AgentCommand {
      43              :         name: "run",
      44              :         args: "<script> [--max-rows N] [--force]",
      45              :         does: "Run a script: columns, first rows, row count or error",
      46              :     },
      47              :     AgentCommand { name: "result", args: "<script> [--page N]", does: "More rows of the last result of a script" },
      48              :     AgentCommand {
      49              :         name: "vis read",
      50              :         args: "<script>",
      51              :         does: "The chart of a script: its renderer and Vega-Lite spec",
      52              :     },
      53              :     AgentCommand {
      54              :         name: "vis edit",
      55              :         args: "<script> [--file f]",
      56              :         does: "Chart the last query of a script with the Vega-Lite spec of the file or stdin",
      57              :     },
      58              :     AgentCommand {
      59              :         name: "vis image",
      60              :         args: "<script> [--out f] [--width N] [--height N] [--scale N]",
      61              :         does: "Render the chart of a script with its last result as PNG, default `<script>.png`",
      62              :     },
      63              : ];
      64              : 
      65              : /// Is this a command that a client runs in its session?
      66           13 : pub fn is_session_command(call: &str) -> bool {
      67           97 :     AGENT_COMMANDS.iter().any(|command| command.call().as_deref() == Some(call))
      68           13 : }
      69              : 
      70              : /// The subcommands of `vis`
      71           14 : pub fn vis_commands() -> impl Iterator<Item = &'static str> {
      72          160 :     AGENT_COMMANDS.iter().filter_map(|command| command.name.strip_prefix("vis "))
      73           14 : }
      74              : 
      75              : /// The commands as a Markdown table, for the guide and the instructions
      76           52 : pub fn command_table() -> String {
      77           52 :     let mut lines = vec!["| Command | Does |".to_string(), "|---|---|".to_string()];
      78          624 :     lines.extend(AGENT_COMMANDS.iter().map(|command| format!("| `dashql agent {}` | {} |", command.synopsis(), command.does)));
      79           52 :     lines.join("\n")
      80           52 : }
      81              : 
      82              : /// The width of the first column of the usage
      83              : const USAGE_COLUMN: usize = 38;
      84              : 
      85              : /// The commands in the usage of the command line tool
      86           15 : pub fn usage_lines() -> String {
      87           15 :     let mut lines = Vec::new();
      88          180 :     for command in &AGENT_COMMANDS {
      89          180 :         let synopsis = format!("  {}", command.synopsis());
      90          180 :         let does = command.does.replace('`', "");
      91          180 :         if synopsis.len() < USAGE_COLUMN {
      92          135 :             lines.push(format!("{synopsis:<USAGE_COLUMN$}{does}"));
      93          135 :         } else {
      94           45 :             lines.push(synopsis);
      95           45 :             lines.push(format!("{:USAGE_COLUMN$}{does}", ""));
      96           45 :         }
      97              :     }
      98           15 :     lines.join("\n")
      99           15 : }
     100              : 
     101              : #[cfg(test)]
     102              : mod tests {
     103              :     use super::*;
     104              : 
     105              :     #[test]
     106            1 :     fn names_the_calls_of_the_commands() {
     107            1 :         assert!(is_session_command("vis.edit"));
     108            1 :         assert!(is_session_command("catalog"));
     109            1 :         assert!(!is_session_command("guide"));
     110            1 :         assert!(!is_session_command("status"));
     111            1 :         assert!(!is_session_command("vis edit"));
     112            1 :         assert_eq!(vis_commands().collect::<Vec<_>>(), ["read", "edit", "image"]);
     113            1 :     }
     114              : 
     115              :     #[test]
     116            1 :     fn lists_the_commands_for_the_usage() {
     117            1 :         let usage = usage_lines();
     118            1 :         assert!(usage.contains("\n  read <script>                       Script text\n"));
     119            1 :         assert!(usage.contains(
     120            1 :             "\n  vis image <script> [--out f] [--width N] [--height N] [--scale N]\n                                      Render"
     121            1 :         ));
     122            1 :         assert!(usage.contains("CREATE TABLE statements"));
     123            1 :     }
     124              : }
        

Generated by: LCOV version 2.0-1