refactor: clean up db stuff; start comments stuff

This commit is contained in:
Jef Roosens 2024-12-29 19:53:06 +01:00
parent f980115d45
commit fed9c01370
Signed by: Jef Roosens
GPG key ID: 21FD3D77D56BAF49
9 changed files with 174 additions and 118 deletions

19
src/db/comment.rs Normal file
View file

@ -0,0 +1,19 @@
use r2d2_sqlite::rusqlite::{self, Row};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct Comment {
id: i32,
plant_id: i32,
comment: String,
}
impl Comment {
pub fn from_row(row: &Row<'_>) -> Result<Self, rusqlite::Error> {
Ok(Self {
id: row.get(0)?,
plant_id: row.get(1)?,
comment: row.get(2)?,
})
}
}

47
src/db/mod.rs Normal file
View file

@ -0,0 +1,47 @@
mod comment;
mod plant;
use r2d2_sqlite::{rusqlite, SqliteConnectionManager};
use std::{error::Error, fmt};
pub use comment::Comment;
pub use plant::{NewPlant, Plant};
pub type DbPool = r2d2::Pool<SqliteConnectionManager>;
#[derive(Debug)]
pub enum DbError {
Pool(r2d2::Error),
Db(rusqlite::Error),
}
impl fmt::Display for DbError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Pool(_) => write!(f, "failed to acquire connection from pool"),
Self::Db(_) => write!(f, "error while accessing the database"),
}
}
}
impl Error for DbError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Pool(err) => Some(err),
Self::Db(err) => Some(err),
}
}
}
impl From<r2d2::Error> for DbError {
fn from(value: r2d2::Error) -> Self {
Self::Pool(value)
}
}
impl From<rusqlite::Error> for DbError {
fn from(value: rusqlite::Error) -> Self {
Self::Db(value)
}
}

73
src/db/plant.rs Normal file
View file

@ -0,0 +1,73 @@
use r2d2_sqlite::rusqlite::{self, Row};
use serde::{Deserialize, Serialize};
use super::{Comment, DbError, DbPool};
#[derive(Serialize)]
pub struct Plant {
id: i32,
name: String,
species: String,
description: String,
}
impl Plant {
pub fn from_row(row: &Row<'_>) -> Result<Self, rusqlite::Error> {
Ok(Self {
id: row.get(0)?,
name: row.get(1)?,
species: row.get(2)?,
description: row.get(3)?,
})
}
pub fn all(pool: &DbPool) -> Result<Vec<Self>, DbError> {
let conn = pool.get()?;
let mut stmt = conn.prepare("select * from plants")?;
let plants: Result<Vec<_>, _> = stmt.query_map((), Self::from_row)?.collect();
Ok(plants?)
}
pub fn with_id(pool: &DbPool, id: i32) -> Result<Option<Self>, DbError> {
let conn = pool.get()?;
let mut stmt = conn.prepare("select * from plants where id = $1")?;
match stmt.query_row((id,), |row| Plant::from_row(row)) {
Ok(plant) => Ok(Some(plant)),
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
Err(err) => Err(DbError::Db(err)),
}
}
pub fn comments(&self, pool: &DbPool) -> Result<Vec<Comment>, DbError> {
let conn = pool.get()?;
let mut stmt = conn.prepare("select * from plant_comments where plant_id = $1")?;
let comments: Result<Vec<_>, _> = stmt.query_map((self.id,), Comment::from_row)?.collect();
Ok(comments?)
}
}
#[derive(Deserialize)]
pub struct NewPlant {
name: String,
species: String,
description: String,
}
impl NewPlant {
pub fn insert(self, pool: &DbPool) -> Result<Plant, DbError> {
let conn = pool.get()?;
let mut stmt = conn.prepare(
"insert into plants (name, species, description) values ($1, $2, $3) returning *",
)?;
Ok(stmt.query_row(
(&self.name, &self.species, &self.description),
Plant::from_row,
)?)
}
}