remove old stacks
This commit is contained in:
152
src/models/error.rs
Normal file
152
src/models/error.rs
Normal file
@@ -0,0 +1,152 @@
|
||||
use std::fmt;
|
||||
|
||||
use sqlx::error::DatabaseError as _;
|
||||
|
||||
pub enum DatabaseError {
|
||||
/// Errors we can receive **from** the database that are caused by connection
|
||||
/// problems or schema problems (e.g. we get a return value that does not fit our enum,
|
||||
/// or a wrongly formatted date)
|
||||
Sql {
|
||||
description: String,
|
||||
},
|
||||
Uuid {
|
||||
description: String,
|
||||
},
|
||||
Enum {
|
||||
description: String,
|
||||
},
|
||||
TimeParse {
|
||||
description: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl fmt::Display for DatabaseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Sql { description } => {
|
||||
write!(f, "SQL error: {description}")
|
||||
}
|
||||
Self::Uuid { description } => {
|
||||
write!(f, "UUID error: {description}")
|
||||
}
|
||||
Self::Enum { description } => {
|
||||
write!(f, "Enum error: {description}")
|
||||
}
|
||||
Self::TimeParse { description } => {
|
||||
write!(f, "Date parse error: {description}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum QueryError {
|
||||
/// Errors that are caused by wrong input data, e.g. ids that cannot be found, or
|
||||
/// inserts that violate unique constraints
|
||||
Duplicate {
|
||||
description: String,
|
||||
},
|
||||
NotFound {
|
||||
description: String,
|
||||
},
|
||||
ReferenceNotFound {
|
||||
description: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl fmt::Display for QueryError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Duplicate { description } => {
|
||||
write!(f, "Duplicate data entry: {description}")
|
||||
}
|
||||
Self::NotFound { description } => {
|
||||
write!(f, "not found: {description}")
|
||||
}
|
||||
Self::ReferenceNotFound { description } => {
|
||||
write!(f, "SQL foreign key reference was not found: {description}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Error {
|
||||
Database(DatabaseError),
|
||||
Query(QueryError),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Database(error) => write!(f, "{error}"),
|
||||
Self::Query(error) => write!(f, "{error}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// defer to Display
|
||||
write!(f, "SQL error: {self}")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<uuid::Error> for Error {
|
||||
fn from(value: uuid::Error) -> Self {
|
||||
Error::Database(DatabaseError::Uuid {
|
||||
description: value.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<time::error::Format> for Error {
|
||||
fn from(value: time::error::Format) -> Self {
|
||||
Error::Database(DatabaseError::TimeParse {
|
||||
description: value.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sqlx::Error> for Error {
|
||||
fn from(value: sqlx::Error) -> Self {
|
||||
match value {
|
||||
sqlx::Error::RowNotFound => Error::Query(QueryError::NotFound {
|
||||
description: value.to_string(),
|
||||
}),
|
||||
sqlx::Error::Database(ref error) => {
|
||||
let sqlite_error = error.downcast_ref::<sqlx::sqlite::SqliteError>();
|
||||
if let Some(code) = sqlite_error.code() {
|
||||
match &*code {
|
||||
// SQLITE_CONSTRAINT_FOREIGNKEY
|
||||
"787" => Error::Query(QueryError::ReferenceNotFound {
|
||||
description: "foreign key reference not found".to_string(),
|
||||
}),
|
||||
// SQLITE_CONSTRAINT_UNIQUE
|
||||
"2067" => Error::Query(QueryError::Duplicate {
|
||||
description: "item with unique constraint already exists".to_string(),
|
||||
}),
|
||||
_ => Error::Database(DatabaseError::Sql {
|
||||
description: format!("got error with unknown code: {sqlite_error}"),
|
||||
}),
|
||||
}
|
||||
} else {
|
||||
Error::Database(DatabaseError::Sql {
|
||||
description: format!("got error without code: {sqlite_error}"),
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => Error::Database(DatabaseError::Sql {
|
||||
description: format!("got unknown error: {value}"),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<time::error::Parse> for Error {
|
||||
fn from(value: time::error::Parse) -> Self {
|
||||
Error::Database(DatabaseError::TimeParse {
|
||||
description: value.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
489
src/models/inventory.rs
Normal file
489
src/models/inventory.rs
Normal file
@@ -0,0 +1,489 @@
|
||||
use super::Error;
|
||||
use crate::{sqlite, Context};
|
||||
|
||||
use futures::{TryFutureExt, TryStreamExt};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub struct Inventory {
|
||||
pub categories: Vec<Category>,
|
||||
}
|
||||
|
||||
impl Inventory {
|
||||
#[tracing::instrument]
|
||||
pub async fn load(ctx: &Context, pool: &sqlite::Pool) -> Result<Self, Error> {
|
||||
let user_id = ctx.user.id.to_string();
|
||||
|
||||
let mut categories = crate::query_all!(
|
||||
&sqlite::QueryClassification {
|
||||
query_type: sqlite::QueryType::Select,
|
||||
component: sqlite::Component::Inventory,
|
||||
},
|
||||
pool,
|
||||
DbCategoryRow,
|
||||
Category,
|
||||
"SELECT
|
||||
id,
|
||||
name,
|
||||
description
|
||||
FROM inventory_items_categories
|
||||
WHERE user_id = ?",
|
||||
user_id
|
||||
)
|
||||
.await?;
|
||||
|
||||
for category in &mut categories {
|
||||
category.populate_items(ctx, pool).await?;
|
||||
}
|
||||
|
||||
Ok(Self { categories })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Category {
|
||||
pub id: Uuid,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub items: Option<Vec<Item>>,
|
||||
}
|
||||
|
||||
pub struct DbCategoryRow {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
impl TryFrom<DbCategoryRow> for Category {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(row: DbCategoryRow) -> Result<Self, Self::Error> {
|
||||
Ok(Category {
|
||||
id: Uuid::try_parse(&row.id)?,
|
||||
name: row.name,
|
||||
description: row.description,
|
||||
items: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Category {
|
||||
#[tracing::instrument]
|
||||
pub async fn _find(
|
||||
ctx: &Context,
|
||||
pool: &sqlite::Pool,
|
||||
id: Uuid,
|
||||
) -> Result<Option<Category>, Error> {
|
||||
let id_param = id.to_string();
|
||||
let user_id = ctx.user.id.to_string();
|
||||
crate::query_one!(
|
||||
&sqlite::QueryClassification {
|
||||
query_type: sqlite::QueryType::Select,
|
||||
component: sqlite::Component::Inventory,
|
||||
},
|
||||
pool,
|
||||
DbCategoryRow,
|
||||
Category,
|
||||
"SELECT
|
||||
id,
|
||||
name,
|
||||
description
|
||||
FROM inventory_items_categories AS category
|
||||
WHERE
|
||||
category.id = ?
|
||||
AND category.user_id = ?",
|
||||
id_param,
|
||||
user_id,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn save(ctx: &Context, pool: &sqlite::Pool, name: &str) -> Result<Uuid, Error> {
|
||||
let id = Uuid::new_v4();
|
||||
let id_param = id.to_string();
|
||||
let user_id = ctx.user.id.to_string();
|
||||
crate::execute!(
|
||||
&sqlite::QueryClassification {
|
||||
query_type: sqlite::QueryType::Insert,
|
||||
component: sqlite::Component::Inventory,
|
||||
},
|
||||
pool,
|
||||
"INSERT INTO inventory_items_categories
|
||||
(id, name, user_id)
|
||||
VALUES
|
||||
(?, ?, ?)",
|
||||
id_param,
|
||||
name,
|
||||
user_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub fn items(&self) -> &Vec<Item> {
|
||||
self.items
|
||||
.as_ref()
|
||||
.expect("you need to call populate_items()")
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub fn total_weight(&self) -> i64 {
|
||||
self.items().iter().map(|item| item.weight).sum()
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn populate_items(
|
||||
&mut self,
|
||||
ctx: &Context,
|
||||
pool: &sqlite::Pool,
|
||||
) -> Result<(), Error> {
|
||||
let id = self.id.to_string();
|
||||
let user_id = ctx.user.id.to_string();
|
||||
let items = crate::query_all!(
|
||||
&sqlite::QueryClassification {
|
||||
query_type: sqlite::QueryType::Select,
|
||||
component: sqlite::Component::Inventory,
|
||||
},
|
||||
pool,
|
||||
DbInventoryItemsRow,
|
||||
Item,
|
||||
"SELECT
|
||||
id,
|
||||
name,
|
||||
weight,
|
||||
description,
|
||||
category_id
|
||||
FROM inventory_items
|
||||
WHERE
|
||||
category_id = ?
|
||||
AND user_id = ?",
|
||||
id,
|
||||
user_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.items = Some(items);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Product {
|
||||
pub id: Uuid,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub comment: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InventoryItem {
|
||||
pub id: Uuid,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub weight: i64,
|
||||
pub category: Category,
|
||||
pub product: Option<Product>,
|
||||
}
|
||||
|
||||
struct DbInventoryItemRow {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub weight: i64,
|
||||
pub category_id: String,
|
||||
pub category_name: String,
|
||||
pub category_description: Option<String>,
|
||||
pub product_id: Option<String>,
|
||||
pub product_name: Option<String>,
|
||||
pub product_description: Option<String>,
|
||||
pub product_comment: Option<String>,
|
||||
}
|
||||
|
||||
impl TryFrom<DbInventoryItemRow> for InventoryItem {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(row: DbInventoryItemRow) -> Result<Self, Self::Error> {
|
||||
Ok(InventoryItem {
|
||||
id: Uuid::try_parse(&row.id)?,
|
||||
name: row.name,
|
||||
description: row.description,
|
||||
weight: row.weight,
|
||||
category: Category {
|
||||
id: Uuid::try_parse(&row.category_id)?,
|
||||
name: row.category_name,
|
||||
description: row.category_description,
|
||||
items: None,
|
||||
},
|
||||
product: row
|
||||
.product_id
|
||||
.map(|id| -> Result<Product, Error> {
|
||||
Ok(Product {
|
||||
id: Uuid::try_parse(&id)?,
|
||||
name: row.product_name.unwrap(),
|
||||
description: row.product_description,
|
||||
comment: row.product_comment,
|
||||
})
|
||||
})
|
||||
.transpose()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl InventoryItem {
|
||||
#[tracing::instrument]
|
||||
pub async fn find(ctx: &Context, pool: &sqlite::Pool, id: Uuid) -> Result<Option<Self>, Error> {
|
||||
let id_param = id.to_string();
|
||||
let user_id = ctx.user.id.to_string();
|
||||
|
||||
crate::query_one!(
|
||||
&sqlite::QueryClassification {
|
||||
query_type: sqlite::QueryType::Select,
|
||||
component: sqlite::Component::Inventory,
|
||||
},
|
||||
pool,
|
||||
DbInventoryItemRow,
|
||||
Self,
|
||||
"SELECT
|
||||
item.id AS id,
|
||||
item.name AS name,
|
||||
item.description AS description,
|
||||
weight,
|
||||
category.id AS category_id,
|
||||
category.name AS category_name,
|
||||
category.description AS category_description,
|
||||
product.id AS product_id,
|
||||
product.name AS product_name,
|
||||
product.description AS product_description,
|
||||
product.comment AS product_comment
|
||||
FROM inventory_items AS item
|
||||
INNER JOIN inventory_items_categories as category
|
||||
ON item.category_id = category.id
|
||||
LEFT JOIN inventory_products AS product
|
||||
ON item.product_id = product.id
|
||||
WHERE
|
||||
item.id = ?
|
||||
AND item.user_id = ?",
|
||||
id_param,
|
||||
user_id,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn name_exists(
|
||||
ctx: &Context,
|
||||
pool: &sqlite::Pool,
|
||||
name: &str,
|
||||
) -> Result<bool, Error> {
|
||||
let user_id = ctx.user.id.to_string();
|
||||
crate::query_exists!(
|
||||
&sqlite::QueryClassification {
|
||||
query_type: sqlite::QueryType::Select,
|
||||
component: sqlite::Component::Inventory,
|
||||
},
|
||||
pool,
|
||||
"SELECT id
|
||||
FROM inventory_items
|
||||
WHERE
|
||||
name = ?
|
||||
AND user_id = ?",
|
||||
name,
|
||||
user_id
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn delete(ctx: &Context, pool: &sqlite::Pool, id: Uuid) -> Result<bool, Error> {
|
||||
let id_param = id.to_string();
|
||||
let user_id = ctx.user.id.to_string();
|
||||
let results = crate::execute!(
|
||||
&sqlite::QueryClassification {
|
||||
query_type: sqlite::QueryType::Delete,
|
||||
component: sqlite::Component::Inventory,
|
||||
},
|
||||
pool,
|
||||
"DELETE FROM inventory_items
|
||||
WHERE
|
||||
id = ?
|
||||
AND user_id = ?",
|
||||
id_param,
|
||||
user_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(results.rows_affected() != 0)
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn update(
|
||||
ctx: &Context,
|
||||
pool: &sqlite::Pool,
|
||||
id: Uuid,
|
||||
name: &str,
|
||||
weight: u32,
|
||||
) -> Result<Uuid, Error> {
|
||||
let user_id = ctx.user.id.to_string();
|
||||
let weight = i64::try_from(weight).unwrap();
|
||||
|
||||
let id_param = id.to_string();
|
||||
crate::execute_returning_uuid!(
|
||||
&sqlite::QueryClassification {
|
||||
query_type: sqlite::QueryType::Update,
|
||||
component: sqlite::Component::Inventory,
|
||||
},
|
||||
pool,
|
||||
"UPDATE inventory_items AS item
|
||||
SET
|
||||
name = ?,
|
||||
weight = ?
|
||||
WHERE
|
||||
item.id = ?
|
||||
AND item.user_id = ?
|
||||
RETURNING inventory_items.category_id AS id
|
||||
",
|
||||
name,
|
||||
weight,
|
||||
id_param,
|
||||
user_id,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn save(
|
||||
ctx: &Context,
|
||||
pool: &sqlite::Pool,
|
||||
name: &str,
|
||||
category_id: Uuid,
|
||||
weight: u32,
|
||||
) -> Result<Uuid, Error> {
|
||||
let id = Uuid::new_v4();
|
||||
let id_param = id.to_string();
|
||||
let user_id = ctx.user.id.to_string();
|
||||
let category_id_param = category_id.to_string();
|
||||
|
||||
crate::execute!(
|
||||
&sqlite::QueryClassification {
|
||||
query_type: sqlite::QueryType::Insert,
|
||||
component: sqlite::Component::Inventory,
|
||||
},
|
||||
pool,
|
||||
"INSERT INTO inventory_items
|
||||
(id, name, description, weight, category_id, user_id)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?)",
|
||||
id_param,
|
||||
name,
|
||||
"",
|
||||
weight,
|
||||
category_id_param,
|
||||
user_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn get_category_max_weight(
|
||||
ctx: &Context,
|
||||
pool: &sqlite::Pool,
|
||||
category_id: Uuid,
|
||||
) -> Result<i64, Error> {
|
||||
let user_id = ctx.user.id.to_string();
|
||||
let category_id_param = category_id.to_string();
|
||||
let weight = crate::execute_returning!(
|
||||
&sqlite::QueryClassification {
|
||||
query_type: sqlite::QueryType::Select,
|
||||
component: sqlite::Component::Inventory,
|
||||
},
|
||||
pool,
|
||||
"
|
||||
SELECT COALESCE(MAX(i_item.weight), 0) as weight
|
||||
FROM inventory_items_categories as category
|
||||
INNER JOIN inventory_items as i_item
|
||||
ON i_item.category_id = category.id
|
||||
WHERE
|
||||
category_id = ?
|
||||
AND category.user_id = ?
|
||||
",
|
||||
i64,
|
||||
|row| i64::from(row.weight),
|
||||
category_id_param,
|
||||
user_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(weight)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Item {
|
||||
pub id: Uuid,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub weight: i64,
|
||||
pub category_id: Uuid,
|
||||
}
|
||||
|
||||
pub struct DbInventoryItemsRow {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub weight: i64,
|
||||
pub description: Option<String>,
|
||||
pub category_id: String,
|
||||
}
|
||||
|
||||
impl TryFrom<DbInventoryItemsRow> for Item {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(row: DbInventoryItemsRow) -> Result<Self, Self::Error> {
|
||||
Ok(Item {
|
||||
id: Uuid::try_parse(&row.id)?,
|
||||
name: row.name,
|
||||
description: row.description, // TODO
|
||||
weight: row.weight,
|
||||
category_id: Uuid::try_parse(&row.category_id)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Item {
|
||||
#[tracing::instrument]
|
||||
pub async fn _get_category_total_picked_weight(
|
||||
ctx: &Context,
|
||||
pool: &sqlite::Pool,
|
||||
category_id: Uuid,
|
||||
) -> Result<i64, Error> {
|
||||
let user_id = ctx.user.id.to_string();
|
||||
let category_id_param = category_id.to_string();
|
||||
crate::execute_returning!(
|
||||
&sqlite::QueryClassification {
|
||||
query_type: sqlite::QueryType::Select,
|
||||
component: sqlite::Component::Inventory,
|
||||
},
|
||||
pool,
|
||||
"
|
||||
SELECT COALESCE(SUM(i_item.weight), 0) as weight
|
||||
FROM inventory_items_categories as category
|
||||
INNER JOIN inventory_items as i_item
|
||||
ON i_item.category_id = category.id
|
||||
INNER JOIN trips_items as t_item
|
||||
ON i_item.id = t_item.item_id
|
||||
WHERE
|
||||
category_id = ?
|
||||
AND category.user_id = ?
|
||||
AND t_item.pick = 1
|
||||
",
|
||||
i64,
|
||||
|row| i64::from(row.weight),
|
||||
category_id_param,
|
||||
user_id,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
13
src/models/mod.rs
Normal file
13
src/models/mod.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
pub mod inventory;
|
||||
pub mod trips;
|
||||
pub mod user;
|
||||
|
||||
mod error;
|
||||
pub use error::{DatabaseError, Error, QueryError};
|
||||
|
||||
mod consts {
|
||||
use time::{format_description::FormatItem, macros::format_description};
|
||||
|
||||
pub(super) const DATE_FORMAT: &[FormatItem<'static>] =
|
||||
format_description!("[year]-[month]-[day]");
|
||||
}
|
||||
1489
src/models/trips.rs
Normal file
1489
src/models/trips.rs
Normal file
File diff suppressed because it is too large
Load Diff
81
src/models/user.rs
Normal file
81
src/models/user.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
use super::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::sqlite;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct User {
|
||||
pub id: Uuid,
|
||||
pub username: String,
|
||||
pub fullname: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NewUser<'a> {
|
||||
pub username: &'a str,
|
||||
pub fullname: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DbUserRow {
|
||||
id: String,
|
||||
username: String,
|
||||
fullname: String,
|
||||
}
|
||||
|
||||
impl TryFrom<DbUserRow> for User {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(row: DbUserRow) -> Result<Self, Self::Error> {
|
||||
Ok(User {
|
||||
id: Uuid::try_parse(&row.id)?,
|
||||
username: row.username,
|
||||
fullname: row.fullname,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl User {
|
||||
#[tracing::instrument]
|
||||
pub async fn find_by_name(
|
||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
||||
name: &str,
|
||||
) -> Result<Option<Self>, Error> {
|
||||
crate::query_one!(
|
||||
&sqlite::QueryClassification {
|
||||
query_type: sqlite::QueryType::Select,
|
||||
component: sqlite::Component::User,
|
||||
},
|
||||
pool,
|
||||
DbUserRow,
|
||||
Self,
|
||||
"SELECT id,username,fullname FROM users WHERE username = ?",
|
||||
name
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn create(pool: &sqlite::Pool, user: NewUser<'_>) -> Result<Uuid, Error> {
|
||||
let id = Uuid::new_v4();
|
||||
let id_param = id.to_string();
|
||||
|
||||
crate::execute!(
|
||||
&sqlite::QueryClassification {
|
||||
query_type: sqlite::QueryType::Insert,
|
||||
component: sqlite::Component::User,
|
||||
},
|
||||
pool,
|
||||
"INSERT INTO users
|
||||
(id, username, fullname)
|
||||
VALUES
|
||||
(?, ?, ?)",
|
||||
id_param,
|
||||
user.username,
|
||||
user.fullname
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
Reference in New Issue
Block a user