feat(db): expose package search and lookup helpers
This commit is contained in:
parent
3c392fa759
commit
666edac19a
3 changed files with 100 additions and 0 deletions
|
|
@ -1,9 +1,11 @@
|
||||||
pub mod models;
|
pub mod models;
|
||||||
pub mod schema;
|
pub mod schema;
|
||||||
|
|
||||||
|
use std::cmp;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use diesel::OptionalExtension;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use diesel::r2d2::{self, ConnectionManager};
|
use diesel::r2d2::{self, ConnectionManager};
|
||||||
use diesel::sqlite::SqliteConnection;
|
use diesel::sqlite::SqliteConnection;
|
||||||
|
|
@ -105,3 +107,98 @@ pub fn load_packages_via_pool(pool: &Pool) -> Result<Vec<Package>> {
|
||||||
let mut conn = pool.get().context("acquiring database connection")?;
|
let mut conn = pool.get().context("acquiring database connection")?;
|
||||||
load_packages(&mut conn)
|
load_packages(&mut conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load package definitions instead of raw Diesel models for convenience.
|
||||||
|
pub fn load_package_definitions(conn: &mut SqliteConnection) -> Result<Vec<PackageDefinition>> {
|
||||||
|
load_packages(conn)?
|
||||||
|
.into_iter()
|
||||||
|
.map(|record| record.into_definition())
|
||||||
|
.collect::<Result<Vec<_>>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pool-backed helper mirroring [`load_package_definitions`].
|
||||||
|
pub fn load_package_definitions_via_pool(pool: &Pool) -> Result<Vec<PackageDefinition>> {
|
||||||
|
let mut conn = pool.get().context("acquiring database connection")?;
|
||||||
|
load_package_definitions(&mut conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Locate a package by name and optional version, returning the newest matching entry when
|
||||||
|
/// the version is not supplied.
|
||||||
|
pub fn find_package(
|
||||||
|
conn: &mut SqliteConnection,
|
||||||
|
name: &str,
|
||||||
|
version: Option<&str>,
|
||||||
|
) -> Result<Option<Package>> {
|
||||||
|
let mut query = packages_dsl::packages
|
||||||
|
.filter(packages_dsl::name.eq(name))
|
||||||
|
.into_boxed();
|
||||||
|
|
||||||
|
if let Some(version) = version {
|
||||||
|
query = query.filter(packages_dsl::version.eq(version));
|
||||||
|
}
|
||||||
|
|
||||||
|
query
|
||||||
|
.order(packages_dsl::version.desc())
|
||||||
|
.first::<Package>(conn)
|
||||||
|
.optional()
|
||||||
|
.context("querying package by name")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience wrapper returning the package as a [`PackageDefinition`].
|
||||||
|
pub fn find_package_definition(
|
||||||
|
conn: &mut SqliteConnection,
|
||||||
|
name: &str,
|
||||||
|
version: Option<&str>,
|
||||||
|
) -> Result<Option<PackageDefinition>> {
|
||||||
|
Ok(find_package(conn, name, version)?
|
||||||
|
.map(|pkg| pkg.into_definition())
|
||||||
|
.transpose()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pool-backed variant of [`find_package_definition`].
|
||||||
|
pub fn find_package_definition_via_pool(
|
||||||
|
pool: &Pool,
|
||||||
|
name: &str,
|
||||||
|
version: Option<&str>,
|
||||||
|
) -> Result<Option<PackageDefinition>> {
|
||||||
|
let mut conn = pool.get().context("acquiring database connection")?;
|
||||||
|
find_package_definition(&mut conn, name, version)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Locate packages using a basic substring match on the name, ordered deterministically and
|
||||||
|
/// optionally limited for responsiveness.
|
||||||
|
pub fn search_packages(
|
||||||
|
conn: &mut SqliteConnection,
|
||||||
|
term: &str,
|
||||||
|
limit: Option<i64>,
|
||||||
|
) -> Result<Vec<Package>> {
|
||||||
|
let trimmed = term.trim();
|
||||||
|
if trimmed.is_empty() {
|
||||||
|
return Ok(Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
let normalized: String = trimmed.chars().take(128).collect();
|
||||||
|
let sanitized = normalized.replace('%', "\\%").replace('_', "\\_");
|
||||||
|
let pattern = format!("%{}%", sanitized);
|
||||||
|
let mut query = packages_dsl::packages
|
||||||
|
.filter(packages_dsl::name.like(&pattern))
|
||||||
|
.order((packages_dsl::name, packages_dsl::version))
|
||||||
|
.into_boxed();
|
||||||
|
|
||||||
|
let effective_limit = limit.map(|value| cmp::max(1, value)).unwrap_or(50);
|
||||||
|
query = query.limit(cmp::min(effective_limit, 200));
|
||||||
|
|
||||||
|
query
|
||||||
|
.load::<Package>(conn)
|
||||||
|
.context("searching packages by name")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pool-backed variant of [`search_packages`].
|
||||||
|
pub fn search_packages_via_pool(
|
||||||
|
pool: &Pool,
|
||||||
|
term: &str,
|
||||||
|
limit: Option<i64>,
|
||||||
|
) -> Result<Vec<Package>> {
|
||||||
|
let mut conn = pool.get().context("acquiring database connection")?;
|
||||||
|
search_packages(&mut conn, term, limit)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
pub mod ai;
|
pub mod ai;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
|
#[cfg(feature = "graphql")]
|
||||||
|
pub mod graphql;
|
||||||
pub mod html;
|
pub mod html;
|
||||||
pub mod ingest;
|
pub mod ingest;
|
||||||
pub mod md5_utils;
|
pub mod md5_utils;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
pub mod animations;
|
||||||
pub mod disk_manager;
|
pub mod disk_manager;
|
||||||
pub mod downloader;
|
pub mod downloader;
|
||||||
pub mod main_menu;
|
pub mod main_menu;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue