warehouse/src/db.rs

150 lines
3.6 KiB
Rust

use std::collections::HashMap;
use once_cell::sync::OnceCell;
use sqlx::{
error::BoxDynError,
postgres::{types::Oid, PgHasArrayType, PgTypeInfo},
query_as,
types::Json,
Decode, Encode, Executor, FromRow, Postgres, Type,
};
use crate::index::DependencyKind;
#[derive(Clone, Copy, Default, Debug)]
#[repr(transparent)]
pub struct PgU32(pub u32);
impl PgU32 {
fn to_i32(self) -> i32 {
i32::from_ne_bytes(self.0.to_ne_bytes())
}
fn from_i32(v: i32) -> Self {
Self(u32::from_ne_bytes(v.to_ne_bytes()))
}
}
impl<'r> Decode<'r, Postgres> for PgU32 {
fn decode(
value: <Postgres as sqlx::database::HasValueRef<'r>>::ValueRef,
) -> Result<Self, sqlx::error::BoxDynError> {
i32::decode(value).map(Self::from_i32)
}
}
impl<'q> Encode<'q, Postgres> for PgU32 {
fn encode_by_ref(
&self,
buf: &mut <Postgres as sqlx::database::HasArguments<'q>>::ArgumentBuffer,
) -> sqlx::encode::IsNull {
self.to_i32().encode(buf)
}
fn produces(&self) -> Option<<Postgres as sqlx::Database>::TypeInfo> {
self.to_i32().produces()
}
fn size_hint(&self) -> usize {
self.to_i32().size_hint()
}
}
impl Type<Postgres> for PgU32 {
fn type_info() -> <Postgres as sqlx::Database>::TypeInfo {
i32::type_info()
}
}
impl PgHasArrayType for PgU32 {
fn array_type_info() -> PgTypeInfo {
i32::array_type_info()
}
}
#[derive(FromRow)]
pub struct DbUser {
pub id: PgU32,
pub login: String,
pub credential: String,
pub name: Option<String>,
}
#[derive(FromRow)]
pub struct DbCrate {
pub id: PgU32,
pub name: String,
pub publisher: PgU32,
pub owners: Vec<PgU32>,
}
#[derive(FromRow)]
pub struct DbVersion {
pub id: PgU32,
pub vers: String,
pub cksum: String,
pub yanked: bool,
pub links: Option<String>,
pub crate_id: PgU32,
pub features: Vec<DbVersionFeature>,
pub authors: Vec<String>,
pub description: Option<String>,
pub documentation: Option<String>,
pub homepage: Option<String>,
pub readme: Option<String>,
pub readme_file: Option<String>,
pub keywords: Vec<String>,
pub categories: Vec<String>,
pub license: Option<String>,
pub license_file: Option<String>,
pub repository: Option<String>,
pub badges: Json<HashMap<String, HashMap<String, String>>>,
}
#[derive(FromRow)]
pub struct DbDep {
pub id: PgU32,
pub name: String,
pub version_req: String,
pub optional: bool,
pub default_features: bool,
pub target: Option<String>,
pub kind: DependencyKind,
pub registry: Option<String>,
pub package: Option<String>,
pub features: Vec<String>,
pub version_id: PgU32,
}
#[derive(Type)]
#[sqlx(type_name = "version_feature")]
pub struct DbVersionFeature {
pub feature: String,
pub enables: Vec<String>,
}
static VERSION_FEATURE_ARRAY_OID: OnceCell<Oid> = OnceCell::new();
pub async fn init<'c, E: Executor<'c, Database = Postgres> + Copy>(
e: E,
) -> Result<(), BoxDynError> {
// explicitly ignore the result, since it currently throws an error if the type already exists
let _ = e.execute(include_str!("create.sql")).await;
let (oid,): (Oid,) = query_as("SELECT typarray FROM pg_type WHERE typname = 'version_feature'")
.fetch_one(e)
.await?;
VERSION_FEATURE_ARRAY_OID
.set(oid)
.expect("db::init called multiple times");
Ok(())
}
impl PgHasArrayType for DbVersionFeature {
fn array_type_info() -> PgTypeInfo {
PgTypeInfo::with_oid(*VERSION_FEATURE_ARRAY_OID.get().unwrap())
}
}