kagi-cli 0.1.0

A command-line interface for Kagi.com's APIs
Documentation
/// Render an API result as one of several [OutputFormat]s.
pub trait Render {
    /// Render an API result as one of several [OutputFormat]s.
    ///
    /// The `compact` parameter truncates the rendering to a single line:
    /// - When there are multiple results, it picks a single one.
    /// - When the main output contains linebreaks, trim and replace them with spaces.
    fn render(&self, args: crate::Args) -> String;
}

#[derive(Debug, Copy, Clone, Default)]
pub enum OutputFormat {
    #[default]
    Markdown,
    Json,
    Debug,
}

impl std::fmt::Display for OutputFormat {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            OutputFormat::Markdown => write!(f, "markdown"),
            OutputFormat::Json => write!(f, "json"),
            OutputFormat::Debug => write!(f, "debug"),
        }
    }
}

impl std::str::FromStr for OutputFormat {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "markdown" => Ok(OutputFormat::Markdown),
            "json" => Ok(OutputFormat::Json),
            "debug" => Ok(OutputFormat::Debug),
            _ => Err(format!("Unknown output format '{s}'")),
        }
    }
}

impl Render for kagi_api::v0::fastgpt::Answer {
    fn render(&self, args: crate::Args) -> String {
        match args.format {
            OutputFormat::Markdown => {
                if args.compact {
                    let mut markdown = self.data.output.replace('\n', " ").trim().to_string();

                    if let Some(references) = self.data.references.as_ref() {
                        if let Some(first_reference) = references.get(0) {
                            markdown.push(' ');
                            markdown.push_str(&first_reference.url);
                        }
                    }

                    markdown
                } else {
                    let mut markdown = self.data.output.clone();

                    if let Some(references) = self.data.references.as_ref() {
                        markdown.push_str("\n\n");

                        for (n, reference) in references.iter().enumerate() {
                            let reference_str = format!("\n- {} [{}]\n", reference.snippet, n + 1);
                            markdown.push_str(&reference_str);
                        }

                        for (n, reference) in references.iter().enumerate() {
                            let reference_str = format!("[{}]: {}\n", n + 1, reference.url);
                            markdown.push_str(&reference_str);
                        }
                    }
                    markdown
                }
            }

            // FIXME: Replace `.unwrap()` with pretty error handling.
            OutputFormat::Json => {
                if args.compact {
                    serde_json::to_string(&self).unwrap()
                } else {
                    serde_json::to_string_pretty(&self).unwrap()
                }
            }

            OutputFormat::Debug => {
                if args.compact {
                    format!("{:?}", self)
                } else {
                    format!("{:#?}", self)
                }
            }
        }
    }
}