saas-rs-sdk 0.6.2

The SaaS RS SDK
use super::{deserialize::deserialize, schema::Table};
use crate::bson::Array;
use crate::storage::Error;
use crate::storage::associations::HasMany;
use change_case::snake_case;
use futures_util::TryStreamExt;
use pbbson::Model;
use sqlx::{Pool, Postgres};
use std::collections::HashMap;
use std::fmt::Debug;
use std::str::FromStr;

pub(super) async fn perform<B: Clone + Debug + FromStr + Send + Sync + ToString>(
    pool: &Pool<Postgres>,
    table: &Table,
    id: &str,
    has_manys_by_bucket: &HashMap<String, Vec<HasMany<B>>>,
) -> Result<Model, Error> {
    // Prepare query
    let sql = {
        let table_name = &table.name;
        let maybe_id_field = table.fields_by_name.get("id");
        let param = match maybe_id_field {
            None => "$1".to_string(),
            Some(field) => format!("$1::{}", field.data_type),
        };
        format!("SELECT * FROM {table_name} WHERE id = {param}")
    };
    log::debug!(sql; "Finding one");

    // Perform query
    let mut rows = sqlx::query(&sql).bind(id).fetch(pool);

    // Find row
    let row = match rows.try_next().await? {
        None => return Err(Error::not_found("No such record")),
        Some(row) => row,
    };

    // Translate into model
    let mut model = deserialize(row, table)?;

    // Lookup has-manys
    let has_manys = match has_manys_by_bucket.get(&table.name) {
        None => vec![],
        Some(has_manys) => has_manys.clone(),
    };
    for has_many in has_manys.iter() {
        let inverse = match &has_many.inverse {
            None => continue,
            Some(inverse) => match super::create::find_field(table, inverse) {
                None => {
                    continue;
                }
                Some(field) => field,
            },
        };
        let remote_table_name = snake_case(&has_many.remote.to_string());
        let pk_column_name = match table.primary_key.first() {
            None => continue,
            Some(pk_column_name) => pk_column_name,
        };
        let sql = {
            let maybe_id_field = table.fields_by_name.get("id");
            let param = match maybe_id_field {
                None => "$1".to_string(),
                Some(field) => format!("$1::{}", field.data_type),
            };
            let inverse_column_name = &inverse.column_name;
            format!("SELECT {pk_column_name} FROM {remote_table_name} WHERE {inverse_column_name} = {param}")
        };
        log::debug!(sql; "Finding one");

        // Perform the query
        let mut rows = sqlx::query(&sql).bind(id).fetch(pool);
        let mut array = Array::new();
        while let Some(row) = rows.try_next().await? {
            let model = deserialize(row, table)?;
            if let Some(pk_value) = model.get(pk_column_name) {
                array.push(pk_value.clone());
            }
        }
        model.insert(has_many.local.clone(), array);
    }

    Ok(model)
}