qastor 0.1.0

A tiny bot designed to patrol github projects and publish issues/pending requests on slack.
Documentation
use std::{ops::Not, sync::Arc};

use anyhow::{anyhow, Context};
use log::debug;
use reqwest::Client;
use serde::Serialize;
use url::Url;

#[derive(Serialize)]
pub struct Section {
    title: Text,
    fields: Vec<Text>,
}

#[derive(Serialize, Clone)]
struct Text {
    #[serde(rename = "type")]
    typ: &'static str,
    text: Arc<str>,
}

impl Section {
    pub fn new(title: String) -> Self {
        Section {
            title: Text {
                typ: "mrkdwn",
                text: title.into(),
            },
            fields: vec![],
        }
    }

    pub fn append_fields(&mut self, headers: &[String]) {
        self.fields.extend(headers.iter().map(|header| Text {
            typ: "mrkdwn",
            text: header.clone().into(),
        }));
    }

    pub async fn send(&self, client: &Client, hook: &Url) -> Result<(), anyhow::Error> {
        #[derive(Serialize)]
        struct Payload {
            blocks: [Section; 1],
        }
        #[derive(Serialize)]
        struct Section {
            #[serde(rename = "type")]
            typ_: &'static str,
            text: Text,
            fields: Vec<Text>,
        }
        let payload = Payload {
            blocks: [Section {
                typ_: "section",
                text: self.title.clone(),
                fields: self.fields.clone(),
            }],
        };
        debug!(
            "Sending: {}",
            serde_json::to_string_pretty(&payload).unwrap()
        );
        let response = client
            .post(hook.to_string())
            .json(&payload)
            .send()
            .await
            .context("Error while posting message to Slack")?;
        let status = response.status();
        if status.is_success().not() {
            let text = response.text().await.context("Could not gather response")?;
            return Err(anyhow!(
                "Slack responded with an error {}: {}",
                status,
                text
            ));
        }
        Ok(())
    }
}

pub fn link(url: &Url, text: Option<&str>) -> String {
    match text {
        None => format!("[{url}]({url})"),
        Some(text) => {
            format!(
                "<{url}|{escaped}>",
                escaped = html_escape::encode_text(&text)
            )
        }
    }
}