logup 0.1.0

Logup is a UNIX-style command that can be used to pipe stdout logs to location on disk or in the cloud without the need of an agent, logrotate, systemd or other configuration files
Documentation
use crate::writer::AsyncLogWriter;
use async_trait::async_trait;
use clap::{Args, ValueEnum};
use std::time::SystemTime;

#[derive(Args)]
#[group()]
pub struct NewRelicArgs {
    #[arg(
        long,
        help = "Enable uploading logs to NewRelic",
        requires = "newrelic_region",
        requires = "newrelic_api_key"
    )]
    newrelic: bool,

    #[arg(
        value_enum,
        value_name = "NEW_RELIC_REGION",
        long,
        env = "NEW_RELIC_REGION",
        hide_env_values = true,
        requires = "newrelic"
    )]
    newrelic_region: Option<NewRelicRegion>,

    #[arg(
        long,
        value_name = "NEW_RELIC_API_KEY",
        env = "NEW_RELIC_API_KEY",
        hide_env_values = true,
        requires = "newrelic"
    )]
    newrelic_api_key: Option<String>,
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
#[clap(rename_all = "UPPER")]
enum NewRelicRegion {
    US,
    EU,
}

pub struct NewRelicWriter {
    client: reqwest::Client,
    endpoint: String,
    api_key: String,
}

impl NewRelicWriter {
    pub fn new(args: &NewRelicArgs) -> Option<Self> {
        if !args.newrelic {
            return None;
        }

        Some(Self {
            client: reqwest::Client::new(),
            endpoint: match args.newrelic_region.as_ref()? {
                NewRelicRegion::US => "https://log-api.newrelic.com/log/v1".to_string(),
                NewRelicRegion::EU => "https://log-api.eu.newrelic.com/log/v1".to_string(),
            },
            api_key: args.newrelic_api_key.as_ref()?.to_string(),
        })
    }
}

#[async_trait]
impl AsyncLogWriter for NewRelicWriter {
    async fn write_logs(&mut self, time: SystemTime, buf: &[u8]) -> std::io::Result<()> {
        let json = serde_json::json!({
            "timestamp": time.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_millis(),
            "message": String::from_utf8_lossy(buf).to_string()
        });
        self.client
            .post(&self.endpoint)
            .header("Api-Key", &self.api_key)
            .json(&json)
            .send()
            .await
            .unwrap();
        Ok(())
    }
}