// SPDX-FileCopyrightText: 2024 Simon Bruder // // SPDX-License-Identifier: AGPL-3.0-or-later use std::collections::HashMap; use sqlx::{query, query_as, query_scalar, PgPool}; use uuid::Uuid; use crate::models::{Item, NewItem}; pub async fn add(pool: &PgPool, new_item: NewItem) -> Result { query_as!( Item, "INSERT INTO items (name, parent, class, original_packaging, description) VALUES ($1, $2, $3, $4, $5) RETURNING *", new_item.name, new_item.parent, new_item.class, new_item.original_packaging, new_item.description ) .fetch_one(pool) .await } pub async fn add_multiple( pool: &PgPool, new_item: NewItem, quantity: usize, ) -> Result, sqlx::Error> { query_as!( Item, r#"INSERT INTO items (name, parent, class, original_packaging, description) SELECT * FROM UNNEST($1::VARCHAR[], $2::UUID[], $3::UUID[], $4::UUID[], $5::VARCHAR[]) RETURNING *"#, &vec![new_item.name; quantity] as &[Option], &vec![new_item.parent; quantity] as &[Option], &vec![new_item.class; quantity], &vec![new_item.original_packaging; quantity] as &[Option], &vec![new_item.description; quantity] ) .fetch_all(pool) .await } pub async fn get(pool: &PgPool, id: Uuid) -> Result { query_as!(Item, "SELECT * FROM items WHERE id = $1", id) .fetch_one(pool) .await } pub async fn get_by_short_id(pool: &PgPool, short_id: i32) -> Result { query_as!(Item, "SELECT * FROM items WHERE short_id = $1", short_id) .fetch_one(pool) .await } pub async fn get_all(pool: &PgPool) -> Result, sqlx::Error> { query_as!(Item, "SELECT * FROM items ORDER BY created_at") .fetch_all(pool) .await } pub async fn get_multiple(pool: &PgPool, ids: &[Uuid]) -> Result, sqlx::Error> { query_as!(Item, "SELECT * FROM items WHERE id = ANY ($1)", ids) .fetch_all(pool) .await } pub async fn get_all_as_map(pool: &PgPool) -> Result, sqlx::Error> { Ok(get_all(pool) .await? .into_iter() .map(|i| (i.id, i)) .collect()) } pub async fn update(pool: &PgPool, id: Uuid, modified_item: NewItem) -> Result { query_as!( Item, "UPDATE items SET name = $2, parent = $3, class = $4, original_packaging = $5, description = $6 WHERE id = $1 RETURNING *", id, modified_item.name, modified_item.parent, modified_item.class, modified_item.original_packaging, modified_item.description ) .fetch_one(pool) .await } pub async fn delete(pool: &PgPool, id: Uuid) -> Result<(), sqlx::Error> { let res = query!("DELETE FROM items WHERE id = $1", id) .execute(pool) .await?; assert_eq!(res.rows_affected(), 1); Ok(()) } pub async fn get_parents(pool: &PgPool, id: Uuid) -> Result, sqlx::Error> { // force nullable is required for all columns in views query_scalar!( r#"SELECT unnest(parents) as "parents!" FROM item_tree WHERE id = $1"#, id ) .fetch_all(pool) .await } pub async fn get_all_parents(pool: &PgPool) -> Result>, sqlx::Error> { let mut parents = HashMap::new(); for row in query!(r#"SELECT id as "id!", parents as "parents!" FROM item_tree"#) .fetch_all(pool) .await? { parents.insert(row.id, row.parents); } Ok(parents) } pub async fn get_parents_details(pool: &PgPool, id: Uuid) -> Result, sqlx::Error> { query_as!( Item, "SELECT items.* FROM items INNER JOIN unnest((SELECT parents FROM item_tree WHERE id = $1)) WITH ORDINALITY AS parents(id, n) ON items.id = parents.id ORDER BY parents.n;", id ) .fetch_all(pool) .await } pub async fn get_children(pool: &PgPool, id: Uuid) -> Result, sqlx::Error> { query_as!(Item, "SELECT * FROM items WHERE parent = $1", id) .fetch_all(pool) .await } pub async fn original_packaging_contents( pool: &PgPool, id: Uuid, ) -> Result, sqlx::Error> { query_as!( Item, "SELECT * FROM items WHERE original_packaging = $1", id ) .fetch_all(pool) .await }