aphomie_db_client 0.1.2

Common DTOs for communication over network for the BMS Uni project
Documentation
use anyhow::bail;
use bms_dtos::{MetadataDb, TrendDataDb};
use chrono::{DateTime, Utc};
use tokio_postgres::{Client, Config, NoTls, Row};

type Result<T> = core::result::Result<T, anyhow::Error>;

pub struct AphomieDbClient {
    db_client: Client,
}

impl AphomieDbClient {
    pub async fn new(host: Option<&str>, port: Option<u16>) -> Result<Self> {
        let host = if let Some(v) = host { v } else { "localhost" };

        let port = if let Some(v) = port { v } else { 5432 };

        let mut config = Config::new();
        config
            .user("dat7")
            .password("dat7")
            .dbname("dat7")
            .host(host)
            .port(port);

        let (db_client, connection) = config.connect(NoTls).await?;

        tokio::spawn(async move {
            if let Err(e) = connection.await {
                eprintln!("connection error: {}", e);
            }
        });

        Ok(Self { db_client })
    }

    pub async fn insert_metadata(&self, metadata: &MetadataDb) -> Result<Vec<MetadataDb>> {
        insert_metadata(
            &self.db_client,
            &metadata.log_id,
            &metadata.source,
            &metadata.unit,
        )
        .await
        .map(|v| v.iter().map(|r| MetadataDb::from(r)).collect())
    }

    pub async fn get_metadata_by_source(&self, source: &str) -> Result<Vec<MetadataDb>> {
        get_metadata_by_source(&self.db_client, source)
            .await
            .map(|v| v.iter().map(|r| MetadataDb::from(r)).collect())
    }

    pub async fn get_all_metadata(&self) -> Result<Vec<MetadataDb>> {
        get_all_metadata(&self.db_client)
            .await
            .map(|v| v.iter().map(|r| MetadataDb::from(r)).collect())
    }

    pub async fn insert_trend_data(&self, trend_data: &TrendDataDb) -> Result<Vec<TrendDataDb>> {
        insert_trend_data(
            &self.db_client,
            &trend_data.log_id,
            &trend_data.timestamp,
            &trend_data.value,
        )
        .await
        .map(|v| v.iter().map(|r| TrendDataDb::from(r)).collect())
    }
}

async fn insert_metadata(
    client: &Client,
    log_id: &i64,
    source: &str,
    unit: &str,
) -> Result<Vec<Row>> {
    let stmt = "INSERT INTO metadata (log_id, source, unit) VALUES ($1, $2, $3) RETURNING *;";

    match client.query(stmt, &[log_id, &source, &unit]).await {
        Ok(v) => Ok(v),
        Err(e) => {
            bail!("error querying database: {e}");
        }
    }
}

async fn get_metadata_by_source(client: &Client, source: &str) -> Result<Vec<Row>> {
    let stmt = "SELECT * FROM metadata WHERE source = $1;";

    match client.query(stmt, &[&source]).await {
        Ok(v) => Ok(v),
        Err(e) => {
            bail!("error querying database: {e}");
        }
    }
}

async fn get_all_metadata(client: &Client) -> Result<Vec<Row>> {
    let stmt = "SELECT * FROM metadata;";

    match client.query(stmt, &[]).await {
        Ok(v) => Ok(v),
        Err(e) => {
            bail!("error querying database: {e}");
        }
    }
}

// CREATE TABLE trend_data (
//     log_id BIGINT NOT NULL,
//     timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
//     value DOUBLE PRECISION,
//     PRIMARY KEY (log_id, timestamp),
//     FOREIGN KEY (log_id) REFERENCES metadata(log_id)
// );

async fn insert_trend_data(
    client: &Client,
    log_id: &i64,
    timestamp: &DateTime<Utc>,
    value: &Option<f64>,
) -> Result<Vec<Row>> {
    let stmt = "INSERT INTO trend_data (log_id, timestamp, value) VALUES ($1, $2, $3) RETURNING *;";

    match client.query(stmt, &[log_id, &timestamp, &value]).await {
        Ok(v) => Ok(v),
        Err(e) => {
            bail!("error querying database: {e}");
        }
    }
}