diff --git a/src/lib.rs b/src/lib.rs index f820eb9..4c8e1ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,24 @@ +use std::path::{ + Path, + PathBuf, +}; + +use derive_more::Deref; +use ref_cast::RefCast; + pub mod error; pub mod print; + pub mod store; + pub mod util; + +#[derive(Deref, Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct DerivationId(i64); + +#[derive(RefCast, Deref, Debug, PartialEq, Eq)] +#[repr(transparent)] +pub struct StorePath(Path); + +#[derive(Deref, Debug, Clone, PartialEq, Eq)] +pub struct StorePathBuf(PathBuf); diff --git a/src/store.rs b/src/store.rs index 04b8876..d710917 100644 --- a/src/store.rs +++ b/src/store.rs @@ -1,23 +1,21 @@ -use std::{ - path::{ - Path, - PathBuf, - }, - result, -}; +use std::result; use anyhow::{ Context as _, Result, }; use derive_more::Deref; -use ref_cast::RefCast; -use rusqlite::Connection; use rustc_hash::{ FxBuildHasher, FxHashMap, }; +use crate::{ + DerivationId, + StorePath, + StorePathBuf, +}; + macro_rules! path_to_str { ($path:ident) => { let $path = $path.canonicalize().with_context(|| { @@ -36,50 +34,43 @@ macro_rules! path_to_str { }; } -#[derive(Deref, Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct DerivationId(i64); - -#[expect(clippy::module_name_repetitions)] -#[derive(RefCast, Deref, Debug, PartialEq, Eq)] -#[repr(transparent)] -pub struct StorePath(Path); - -#[expect(clippy::module_name_repetitions)] -#[derive(Deref, Debug, Clone, PartialEq, Eq)] -pub struct StorePathBuf(PathBuf); +#[derive(Deref)] +pub struct Connection(rusqlite::Connection); /// Connects to the Nix database. pub fn connect() -> Result { const DATABASE_PATH: &str = "/nix/var/nix/db/db.sqlite"; - Connection::open(DATABASE_PATH).with_context(|| { + let inner = rusqlite::Connection::open(DATABASE_PATH).with_context(|| { format!("failed to connect to Nix database at {DATABASE_PATH}") - }) + })?; + + Ok(Connection(inner)) } -/// Gathers all derivations that the given store path depends on. -pub fn query_depdendents( - connection: &mut Connection, - path: &StorePath, -) -> Result> { - const QUERY: &str = " - WITH RECURSIVE - graph(p) AS ( - SELECT id - FROM ValidPaths - WHERE path = ? - UNION - SELECT reference FROM Refs - JOIN graph ON referrer = p - ) - SELECT id, path from graph - JOIN ValidPaths ON id = p; - "; +impl Connection { + /// Gathers all derivations that the given store path depends on. + pub fn query_depdendents( + &mut self, + path: &StorePath, + ) -> Result> { + const QUERY: &str = " + WITH RECURSIVE + graph(p) AS ( + SELECT id + FROM ValidPaths + WHERE path = ? + UNION + SELECT reference FROM Refs + JOIN graph ON referrer = p + ) + SELECT id, path from graph + JOIN ValidPaths ON id = p; + "; - path_to_str!(path); + path_to_str!(path); - let packages: result::Result, _> = - connection + let packages: result::Result, _> = self .prepare_cached(QUERY)? .query_map([path], |row| { Ok(( @@ -89,79 +80,77 @@ pub fn query_depdendents( })? .collect(); - Ok(packages?) -} - -/// Gets the total closure size of the given store path by summing up the nar -/// size of all depdendent derivations. -pub fn query_closure_size( - connection: &mut Connection, - path: &StorePath, -) -> Result { - const QUERY: &str = " - WITH RECURSIVE - graph(p) AS ( - SELECT id - FROM ValidPaths - WHERE path = ? - UNION - SELECT reference FROM Refs - JOIN graph ON referrer = p - ) - SELECT SUM(narSize) as sum from graph - JOIN ValidPaths ON p = id; - "; - - path_to_str!(path); - - let closure_size = connection - .prepare_cached(QUERY)? - .query_row([path], |row| row.get(0))?; - - Ok(closure_size) -} - -/// Gathers the complete dependency graph of of the store path as an adjacency -/// list. -/// -/// We might want to collect the paths in the graph directly as -/// well in the future, depending on how much we use them -/// in the operations on the graph. -pub fn query_dependency_graph( - connection: &mut Connection, - path: &StorePath, -) -> Result>> { - const QUERY: &str = " - WITH RECURSIVE - graph(p, c) AS ( - SELECT id as par, reference as chd - FROM ValidPaths - JOIN Refs ON referrer = id - WHERE path = ? - UNION - SELECT referrer as par, reference as chd FROM Refs - JOIN graph ON referrer = c - ) - SELECT p, c from graph; - "; - - path_to_str!(path); - - let mut adj = - FxHashMap::>::with_hasher(FxBuildHasher); - - let mut statement = connection.prepare_cached(QUERY)?; - - let edges = statement.query_map([path], |row| { - Ok((DerivationId(row.get(0)?), DerivationId(row.get(1)?))) - })?; - - for row in edges { - let (from, to) = row?; - - adj.entry(from).or_default().push(to); - adj.entry(to).or_default(); + Ok(packages?) } - Ok(adj) + /// Gets the total closure size of the given store path by summing up the nar + /// size of all depdendent derivations. + pub fn query_closure_size(&mut self, path: &StorePath) -> Result { + const QUERY: &str = " + WITH RECURSIVE + graph(p) AS ( + SELECT id + FROM ValidPaths + WHERE path = ? + UNION + SELECT reference FROM Refs + JOIN graph ON referrer = p + ) + SELECT SUM(narSize) as sum from graph + JOIN ValidPaths ON p = id; + "; + + path_to_str!(path); + + let closure_size = self + .prepare_cached(QUERY)? + .query_row([path], |row| row.get(0))?; + + Ok(closure_size) + } + + /// Gathers the complete dependency graph of of the store path as an adjacency + /// list. + /// + /// We might want to collect the paths in the graph directly as + /// well in the future, depending on how much we use them + /// in the operations on the graph. + pub fn query_dependency_graph( + &mut self, + path: &StorePath, + ) -> Result>> { + const QUERY: &str = " + WITH RECURSIVE + graph(p, c) AS ( + SELECT id as par, reference as chd + FROM ValidPaths + JOIN Refs ON referrer = id + WHERE path = ? + UNION + SELECT referrer as par, reference as chd FROM Refs + JOIN graph ON referrer = c + ) + SELECT p, c from graph; + "; + + path_to_str!(path); + + let mut adj = + FxHashMap::>::with_hasher(FxBuildHasher); + + let mut statement = self.prepare_cached(QUERY)?; + + let edges = statement.query_map([path], |row| { + Ok((DerivationId(row.get(0)?), DerivationId(row.get(1)?))) + })?; + + for row in edges { + let (from, to) = row?; + + adj.entry(from).or_default().push(to); + adj.entry(to).or_default(); + } + + Ok(adj) + } }