kagi_cli/
render.rs

1/// Render an API result as one of several [OutputFormat]s.
2pub trait Render {
3    /// Render an API result as one of several [OutputFormat]s.
4    ///
5    /// The `compact` parameter truncates the rendering to a single line:
6    /// - When there are multiple results, it picks a single one.
7    /// - When the main output contains linebreaks, trim and replace them with spaces.
8    fn render(&self, args: crate::Args) -> String;
9}
10
11#[derive(Debug, Copy, Clone, Default)]
12pub enum OutputFormat {
13    #[default]
14    Markdown,
15    Json,
16    Debug,
17}
18
19impl std::fmt::Display for OutputFormat {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        match self {
22            OutputFormat::Markdown => write!(f, "markdown"),
23            OutputFormat::Json => write!(f, "json"),
24            OutputFormat::Debug => write!(f, "debug"),
25        }
26    }
27}
28
29impl std::str::FromStr for OutputFormat {
30    type Err = String;
31
32    fn from_str(s: &str) -> Result<Self, Self::Err> {
33        match s {
34            "markdown" => Ok(OutputFormat::Markdown),
35            "json" => Ok(OutputFormat::Json),
36            "debug" => Ok(OutputFormat::Debug),
37            _ => Err(format!("Unknown output format '{s}'")),
38        }
39    }
40}
41
42impl Render for kagi_api::v0::fastgpt::Answer {
43    fn render(&self, args: crate::Args) -> String {
44        match args.format {
45            OutputFormat::Markdown => {
46                if args.compact {
47                    let mut markdown = self.data.output.replace('\n', " ").trim().to_string();
48
49                    if let Some(references) = self.data.references.as_ref() {
50                        if let Some(first_reference) = references.get(0) {
51                            markdown.push(' ');
52                            markdown.push_str(&first_reference.url);
53                        }
54                    }
55
56                    markdown
57                } else {
58                    let mut markdown = self.data.output.clone();
59
60                    if let Some(references) = self.data.references.as_ref() {
61                        markdown.push_str("\n\n");
62
63                        for (n, reference) in references.iter().enumerate() {
64                            let reference_str = format!("\n- {} [{}]\n", reference.snippet, n + 1);
65                            markdown.push_str(&reference_str);
66                        }
67
68                        for (n, reference) in references.iter().enumerate() {
69                            let reference_str = format!("[{}]: {}\n", n + 1, reference.url);
70                            markdown.push_str(&reference_str);
71                        }
72                    }
73                    markdown
74                }
75            }
76
77            // FIXME: Replace `.unwrap()` with pretty error handling.
78            OutputFormat::Json => {
79                if args.compact {
80                    serde_json::to_string(&self).unwrap()
81                } else {
82                    serde_json::to_string_pretty(&self).unwrap()
83                }
84            }
85
86            OutputFormat::Debug => {
87                if args.compact {
88                    format!("{:?}", self)
89                } else {
90                    format!("{:#?}", self)
91                }
92            }
93        }
94    }
95}