misc: initial code

This commit is contained in:
hdbg
2026-02-27 10:27:24 +01:00
commit 91036f4188
32 changed files with 36435 additions and 0 deletions

View File

@@ -0,0 +1,119 @@
use std::fmt::{Display, Formatter};
use std::path::PathBuf;
use diesel::{Connection as _, SqliteConnection, connection::SimpleConnection as _};
use diesel_async::{
AsyncConnection, SimpleAsyncConnection,
pooled_connection::{AsyncDieselConnectionManager, ManagerConfig},
sync_connection_wrapper::SyncConnectionWrapper,
};
use diesel_migrations::{EmbeddedMigrations, MigrationHarness, embed_migrations};
pub type DatabaseConnection = SyncConnectionWrapper<SqliteConnection>;
pub type DatabasePool = diesel_async::pooled_connection::bb8::Pool<DatabaseConnection>;
pub type DatabaseResult<T> = Result<T, DatabaseError>;
const DB_FILE: &str = "codetaker.sqlite";
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
#[derive(Debug)]
pub enum DatabaseError {
Connection { message: String },
Setup { message: String },
Migration { message: String },
Pool { message: String },
}
impl Display for DatabaseError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Connection { message } => write!(f, "database connection error: {message}"),
Self::Setup { message } => write!(f, "database setup error: {message}"),
Self::Migration { message } => write!(f, "database migration error: {message}"),
Self::Pool { message } => write!(f, "database pool error: {message}"),
}
}
}
impl std::error::Error for DatabaseError {}
fn database_path() -> DatabaseResult<PathBuf> {
let cwd = std::env::current_dir().map_err(|err| DatabaseError::Setup {
message: format!("failed to determine current directory for database path: {err}"),
})?;
Ok(cwd.join(DB_FILE))
}
fn db_config(conn: &mut SqliteConnection) -> Result<(), diesel::result::Error> {
conn.batch_execute("PRAGMA synchronous = NORMAL;")?;
conn.batch_execute("PRAGMA wal_autocheckpoint = 1000;")?;
conn.batch_execute("PRAGMA wal_checkpoint(TRUNCATE);")?;
conn.batch_execute("PRAGMA foreign_keys = ON;")?;
conn.batch_execute("PRAGMA auto_vacuum = FULL;")?;
conn.batch_execute("PRAGMA secure_delete = ON;")?;
Ok(())
}
fn initialize_database(url: &str) -> DatabaseResult<()> {
let mut conn = SqliteConnection::establish(url).map_err(|err| DatabaseError::Connection {
message: format!("failed to establish sqlite connection '{url}': {err}"),
})?;
db_config(&mut conn).map_err(|err| DatabaseError::Setup {
message: format!("failed to configure sqlite pragmas for '{url}': {err}"),
})?;
conn.run_pending_migrations(MIGRATIONS)
.map_err(|err| DatabaseError::Migration {
message: format!("failed to run migrations for '{url}': {err}"),
})?;
Ok(())
}
pub async fn create_pool(url: Option<&str>) -> DatabaseResult<DatabasePool> {
let database_url = match url {
Some(value) => value.to_owned(),
None => database_path()?.to_string_lossy().into_owned(),
};
initialize_database(&database_url)?;
let mut config = ManagerConfig::default();
config.custom_setup = Box::new(|url| {
Box::pin(async move {
let mut conn = DatabaseConnection::establish(url).await?;
conn.batch_execute("PRAGMA busy_timeout = 9000;")
.await
.map_err(diesel::ConnectionError::CouldntSetupConfiguration)?;
conn.batch_execute("PRAGMA journal_mode = WAL;")
.await
.map_err(diesel::ConnectionError::CouldntSetupConfiguration)?;
conn.batch_execute("PRAGMA foreign_keys = ON;")
.await
.map_err(diesel::ConnectionError::CouldntSetupConfiguration)?;
Ok(conn)
})
});
DatabasePool::builder()
.build(AsyncDieselConnectionManager::new_with_config(
database_url,
config,
))
.await
.map_err(|err| DatabaseError::Pool {
message: format!("failed to create database pool: {err}"),
})
}
pub async fn create_test_pool() -> DatabaseResult<DatabasePool> {
let tempfile_name = format!(
"codetaker_test_{}_{}.sqlite",
std::process::id(),
chrono::Utc::now().timestamp_nanos_opt().unwrap_or_default(),
);
let file = std::env::temp_dir().join(tempfile_name);
let url = format!("{}?mode=rwc", file.to_string_lossy());
create_pool(Some(&url)).await
}

View File

@@ -0,0 +1,9 @@
mod db;
pub use db::{
DatabaseConnection, DatabaseError, DatabasePool, DatabaseResult, MIGRATIONS, create_pool,
create_test_pool,
};
pub mod models;
pub mod schema;

View File

@@ -0,0 +1,108 @@
use chrono::NaiveDateTime;
use diesel::{Associations, Identifiable, Insertable, Queryable, Selectable};
use crate::schema::{
project_memory_entries, project_memory_summaries, projects, review_thread_messages,
review_threads,
};
#[derive(Debug, Clone, Queryable, Selectable, Identifiable)]
#[diesel(table_name = projects)]
pub struct ProjectRow {
pub id: i32,
pub forge: String,
pub owner: String,
pub repo: String,
}
#[derive(Debug, Insertable)]
#[diesel(table_name = projects)]
pub struct NewProjectRow<'a> {
pub forge: &'a str,
pub owner: &'a str,
pub repo: &'a str,
}
#[derive(Debug, Clone, Queryable, Selectable, Identifiable, Associations)]
#[diesel(table_name = project_memory_entries)]
#[diesel(belongs_to(ProjectRow, foreign_key = project_id))]
pub struct ProjectMemoryEntryRow {
pub id: i32,
pub project_id: i32,
pub key: String,
pub value_json: String,
pub source: String,
pub updated_at: NaiveDateTime,
}
#[derive(Debug, Insertable)]
#[diesel(table_name = project_memory_entries)]
pub struct NewProjectMemoryEntryRow<'a> {
pub project_id: i32,
pub key: &'a str,
pub value_json: &'a str,
pub source: &'a str,
pub updated_at: NaiveDateTime,
}
#[derive(Debug, Clone, Queryable, Selectable, Identifiable, Associations)]
#[diesel(table_name = project_memory_summaries)]
#[diesel(belongs_to(ProjectRow, foreign_key = project_id))]
pub struct ProjectMemorySummaryRow {
pub id: i32,
pub project_id: i32,
pub summary_type: String,
pub content: String,
pub updated_at: NaiveDateTime,
}
#[derive(Debug, Insertable)]
#[diesel(table_name = project_memory_summaries)]
pub struct NewProjectMemorySummaryRow<'a> {
pub project_id: i32,
pub summary_type: &'a str,
pub content: &'a str,
pub updated_at: NaiveDateTime,
}
#[derive(Debug, Clone, Queryable, Selectable, Identifiable, Associations)]
#[diesel(table_name = review_threads)]
#[diesel(belongs_to(ProjectRow, foreign_key = project_id))]
pub struct ReviewThreadRow {
pub id: i32,
pub project_id: i32,
pub file: String,
pub line: i32,
pub initial_comment: String,
pub created_at: NaiveDateTime,
}
#[derive(Debug, Insertable)]
#[diesel(table_name = review_threads)]
pub struct NewReviewThreadRow<'a> {
pub project_id: i32,
pub file: &'a str,
pub line: i32,
pub initial_comment: &'a str,
pub created_at: NaiveDateTime,
}
#[derive(Debug, Clone, Queryable, Selectable, Identifiable, Associations)]
#[diesel(table_name = review_thread_messages)]
#[diesel(belongs_to(ReviewThreadRow, foreign_key = thread_id))]
pub struct ReviewThreadMessageRow {
pub id: i32,
pub thread_id: i32,
pub author: String,
pub body: String,
pub created_at: NaiveDateTime,
}
#[derive(Debug, Insertable)]
#[diesel(table_name = review_thread_messages)]
pub struct NewReviewThreadMessageRow<'a> {
pub thread_id: i32,
pub author: &'a str,
pub body: &'a str,
pub created_at: NaiveDateTime,
}

View File

@@ -0,0 +1,65 @@
// @generated automatically by Diesel CLI-like configuration. Kept explicit in-repo for agent tests.
diesel::table! {
projects (id) {
id -> Integer,
forge -> Text,
owner -> Text,
repo -> Text,
}
}
diesel::table! {
project_memory_entries (id) {
id -> Integer,
project_id -> Integer,
key -> Text,
value_json -> Text,
source -> Text,
updated_at -> Timestamp,
}
}
diesel::table! {
project_memory_summaries (id) {
id -> Integer,
project_id -> Integer,
summary_type -> Text,
content -> Text,
updated_at -> Timestamp,
}
}
diesel::table! {
review_thread_messages (id) {
id -> Integer,
thread_id -> Integer,
author -> Text,
body -> Text,
created_at -> Timestamp,
}
}
diesel::table! {
review_threads (id) {
id -> Integer,
project_id -> Integer,
file -> Text,
line -> Integer,
initial_comment -> Text,
created_at -> Timestamp,
}
}
diesel::joinable!(project_memory_entries -> projects (project_id));
diesel::joinable!(project_memory_summaries -> projects (project_id));
diesel::joinable!(review_thread_messages -> review_threads (thread_id));
diesel::joinable!(review_threads -> projects (project_id));
diesel::allow_tables_to_appear_in_same_query!(
projects,
project_memory_entries,
project_memory_summaries,
review_threads,
review_thread_messages,
);