mirror of
https://github.com/RGBCube/dix
synced 2025-07-28 04:07:46 +00:00
remove bloated dependency sqlx, switch to libsqlite3-sys
sqlx is way to large for this utility and also async, which we currently do not use
This commit is contained in:
parent
55c969cfdf
commit
eee8c920a7
6 changed files with 203 additions and 2169 deletions
1998
Cargo.lock
generated
1998
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -10,5 +10,4 @@ yansi = "1.0.1"
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.12"
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
env_logger = "0.11.3"
|
env_logger = "0.11.3"
|
||||||
sqlx = { version = "0.8.5", features = ["runtime-async-std", "sqlite"] }
|
rusqlite = { version = "0.35.0", features = ["bundled"] }
|
||||||
async-std = "1.13.1"
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[env]
|
|
||||||
DATABASE_URL="sqlite:///nix/var/nix/db/db.sqlite"
|
|
134
src/error.rs
Normal file
134
src/error.rs
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Application errors with thiserror
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum AppError {
|
||||||
|
#[error("Command failed: {command} {args:?} - {message}")]
|
||||||
|
CommandFailed {
|
||||||
|
command: String,
|
||||||
|
args: Vec<String>,
|
||||||
|
message: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("Failed to decode command output from {context}: {source}")]
|
||||||
|
CommandOutputError {
|
||||||
|
source: std::str::Utf8Error,
|
||||||
|
context: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("Failed to parse data in {context}: {message}")]
|
||||||
|
ParseError {
|
||||||
|
message: String,
|
||||||
|
context: String,
|
||||||
|
#[source]
|
||||||
|
source: Option<Box<dyn std::error::Error + Send + Sync>>,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("Regex error in {context}: {source}")]
|
||||||
|
RegexError {
|
||||||
|
source: regex::Error,
|
||||||
|
context: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("IO error in {context}: {source}")]
|
||||||
|
IoError {
|
||||||
|
source: std::io::Error,
|
||||||
|
context: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("Database error: {source}")]
|
||||||
|
DatabaseError { source: rusqlite::Error },
|
||||||
|
|
||||||
|
#[error("Nix store error: {message} for path: {store_path}")]
|
||||||
|
NixStoreError { message: String, store_path: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement From traits to support the ? operator
|
||||||
|
impl From<std::io::Error> for AppError {
|
||||||
|
fn from(source: std::io::Error) -> Self {
|
||||||
|
Self::IoError {
|
||||||
|
source,
|
||||||
|
context: "unknown context".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::str::Utf8Error> for AppError {
|
||||||
|
fn from(source: std::str::Utf8Error) -> Self {
|
||||||
|
Self::CommandOutputError {
|
||||||
|
source,
|
||||||
|
context: "command output".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<rusqlite::Error> for AppError {
|
||||||
|
fn from(source: rusqlite::Error) -> Self {
|
||||||
|
Self::DatabaseError { source }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<regex::Error> for AppError {
|
||||||
|
fn from(source: regex::Error) -> Self {
|
||||||
|
Self::RegexError {
|
||||||
|
source,
|
||||||
|
context: "regex operation".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppError {
|
||||||
|
/// Create a command failure error with context
|
||||||
|
pub fn command_failed<S: Into<String>>(command: S, args: &[&str], message: S) -> Self {
|
||||||
|
Self::CommandFailed {
|
||||||
|
command: command.into(),
|
||||||
|
args: args.iter().map(|&s| s.to_string()).collect(),
|
||||||
|
message: message.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a parse error with context
|
||||||
|
pub fn parse_error<S: Into<String>, C: Into<String>>(
|
||||||
|
message: S,
|
||||||
|
context: C,
|
||||||
|
source: Option<Box<dyn std::error::Error + Send + Sync>>,
|
||||||
|
) -> Self {
|
||||||
|
Self::ParseError {
|
||||||
|
message: message.into(),
|
||||||
|
context: context.into(),
|
||||||
|
source,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an IO error with context
|
||||||
|
pub fn io_error<C: Into<String>>(source: std::io::Error, context: C) -> Self {
|
||||||
|
Self::IoError {
|
||||||
|
source,
|
||||||
|
context: context.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a regex error with context
|
||||||
|
pub fn regex_error<C: Into<String>>(source: regex::Error, context: C) -> Self {
|
||||||
|
Self::RegexError {
|
||||||
|
source,
|
||||||
|
context: context.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a command output error with context
|
||||||
|
pub fn command_output_error<C: Into<String>>(source: std::str::Utf8Error, context: C) -> Self {
|
||||||
|
Self::CommandOutputError {
|
||||||
|
source,
|
||||||
|
context: context.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a Nix store error
|
||||||
|
pub fn nix_store_error<M: Into<String>, P: Into<String>>(message: M, store_path: P) -> Self {
|
||||||
|
Self::NixStoreError {
|
||||||
|
message: message.into(),
|
||||||
|
store_path: store_path.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
128
src/main.rs
128
src/main.rs
|
@ -1,7 +1,6 @@
|
||||||
mod print;
|
mod print;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use core::str;
|
use core::str;
|
||||||
use env_logger;
|
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -11,133 +10,10 @@ use std::{
|
||||||
sync::OnceLock,
|
sync::OnceLock,
|
||||||
thread,
|
thread,
|
||||||
};
|
};
|
||||||
use thiserror::Error;
|
|
||||||
use yansi::Paint;
|
use yansi::Paint;
|
||||||
|
mod error;
|
||||||
mod store;
|
mod store;
|
||||||
|
use error::AppError;
|
||||||
/// Application errors with thiserror
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
enum AppError {
|
|
||||||
#[error("Command failed: {command} {args:?} - {message}")]
|
|
||||||
CommandFailed {
|
|
||||||
command: String,
|
|
||||||
args: Vec<String>,
|
|
||||||
message: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("Failed to decode command output from {context}: {source}")]
|
|
||||||
CommandOutputError {
|
|
||||||
source: std::str::Utf8Error,
|
|
||||||
context: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("Failed to parse data in {context}: {message}")]
|
|
||||||
ParseError {
|
|
||||||
message: String,
|
|
||||||
context: String,
|
|
||||||
#[source]
|
|
||||||
source: Option<Box<dyn std::error::Error + Send + Sync>>,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("Regex error in {context}: {source}")]
|
|
||||||
RegexError {
|
|
||||||
source: regex::Error,
|
|
||||||
context: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("IO error in {context}: {source}")]
|
|
||||||
IoError {
|
|
||||||
source: std::io::Error,
|
|
||||||
context: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("Nix store error: {message} for path: {store_path}")]
|
|
||||||
NixStoreError { message: String, store_path: String },
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement From traits to support the ? operator
|
|
||||||
impl From<std::io::Error> for AppError {
|
|
||||||
fn from(source: std::io::Error) -> Self {
|
|
||||||
Self::IoError {
|
|
||||||
source,
|
|
||||||
context: "unknown context".into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::str::Utf8Error> for AppError {
|
|
||||||
fn from(source: std::str::Utf8Error) -> Self {
|
|
||||||
Self::CommandOutputError {
|
|
||||||
source,
|
|
||||||
context: "command output".into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<regex::Error> for AppError {
|
|
||||||
fn from(source: regex::Error) -> Self {
|
|
||||||
Self::RegexError {
|
|
||||||
source,
|
|
||||||
context: "regex operation".into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AppError {
|
|
||||||
/// Create a command failure error with context
|
|
||||||
pub fn command_failed<S: Into<String>>(command: S, args: &[&str], message: S) -> Self {
|
|
||||||
Self::CommandFailed {
|
|
||||||
command: command.into(),
|
|
||||||
args: args.iter().map(|&s| s.to_string()).collect(),
|
|
||||||
message: message.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a parse error with context
|
|
||||||
pub fn parse_error<S: Into<String>, C: Into<String>>(
|
|
||||||
message: S,
|
|
||||||
context: C,
|
|
||||||
source: Option<Box<dyn std::error::Error + Send + Sync>>,
|
|
||||||
) -> Self {
|
|
||||||
Self::ParseError {
|
|
||||||
message: message.into(),
|
|
||||||
context: context.into(),
|
|
||||||
source,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create an IO error with context
|
|
||||||
pub fn io_error<C: Into<String>>(source: std::io::Error, context: C) -> Self {
|
|
||||||
Self::IoError {
|
|
||||||
source,
|
|
||||||
context: context.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a regex error with context
|
|
||||||
pub fn regex_error<C: Into<String>>(source: regex::Error, context: C) -> Self {
|
|
||||||
Self::RegexError {
|
|
||||||
source,
|
|
||||||
context: context.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a command output error with context
|
|
||||||
pub fn command_output_error<C: Into<String>>(source: std::str::Utf8Error, context: C) -> Self {
|
|
||||||
Self::CommandOutputError {
|
|
||||||
source,
|
|
||||||
context: context.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a Nix store error
|
|
||||||
pub fn nix_store_error<M: Into<String>, P: Into<String>>(message: M, store_path: P) -> Self {
|
|
||||||
Self::NixStoreError {
|
|
||||||
message: message.into(),
|
|
||||||
store_path: store_path.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use type alias for Result with our custom error type
|
// Use type alias for Result with our custom error type
|
||||||
type Result<T> = std::result::Result<T, AppError>;
|
type Result<T> = std::result::Result<T, AppError>;
|
||||||
|
|
107
src/store.rs
107
src/store.rs
|
@ -1,29 +1,10 @@
|
||||||
use async_std::task;
|
use crate::error::AppError;
|
||||||
use sqlx::{Connection, SqliteConnection};
|
use rusqlite::Connection;
|
||||||
use std::future;
|
|
||||||
|
|
||||||
// executes a query on the nix db directly
|
// Use type alias for Result with our custom error type
|
||||||
// to gather all derivations that the derivation given by the path
|
type Result<T> = std::result::Result<T, AppError>;
|
||||||
// depends on
|
|
||||||
//
|
|
||||||
// in the future, we might wan't to switch to async
|
|
||||||
pub fn get_packages(path: &std::path::Path) -> Result<Vec<String>, String> {
|
|
||||||
let p = path
|
|
||||||
.canonicalize()
|
|
||||||
.ok()
|
|
||||||
.map(|p| p.to_string_lossy().to_string())
|
|
||||||
.ok_or("Could not convert resolve path")?;
|
|
||||||
|
|
||||||
task::block_on(async {
|
const QUERY_PKGS: &str = "
|
||||||
struct Col {
|
|
||||||
path: String,
|
|
||||||
}
|
|
||||||
let mut conn = SqliteConnection::connect("sqlite:///nix/var/nix/db/db.sqlite")
|
|
||||||
.await
|
|
||||||
.map_err(|_| "Could not establish DB connection")?;
|
|
||||||
let query_res = sqlx::query_as!(
|
|
||||||
Col,
|
|
||||||
"
|
|
||||||
WITH RECURSIVE
|
WITH RECURSIVE
|
||||||
graph(p) AS (
|
graph(p) AS (
|
||||||
SELECT id
|
SELECT id
|
||||||
|
@ -35,41 +16,9 @@ WITH RECURSIVE
|
||||||
)
|
)
|
||||||
SELECT path from graph
|
SELECT path from graph
|
||||||
JOIN ValidPaths ON id = p;
|
JOIN ValidPaths ON id = p;
|
||||||
",
|
";
|
||||||
p
|
|
||||||
)
|
|
||||||
.fetch_all(&mut conn)
|
|
||||||
.await
|
|
||||||
.map_err(|_| "Could not execute package query")?
|
|
||||||
.into_iter()
|
|
||||||
.map(|c| c.path)
|
|
||||||
.collect();
|
|
||||||
Ok::<_, String>(query_res)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// executes a query on the nix db directly
|
const QUERY_CLOSURE_SIZE: &str = "
|
||||||
// to get the total closure size of the derivation
|
|
||||||
// by summing up the nar size of all derivations
|
|
||||||
// depending on the derivation
|
|
||||||
//
|
|
||||||
// in the future, we might wan't to switch to async
|
|
||||||
pub fn get_closure_size(path: &std::path::Path) -> Result<i64, String> {
|
|
||||||
let p = path
|
|
||||||
.canonicalize()
|
|
||||||
.ok()
|
|
||||||
.map(|p| p.to_string_lossy().to_string())
|
|
||||||
.ok_or("Could not convert resolve path")?;
|
|
||||||
let size = task::block_on(async {
|
|
||||||
struct Res {
|
|
||||||
sum: Option<i64>,
|
|
||||||
}
|
|
||||||
let mut conn = SqliteConnection::connect("sqlite:///nix/var/nix/db/db.sqlite")
|
|
||||||
.await
|
|
||||||
.map_err(|_| "Could not establish DB connection")?;
|
|
||||||
let query_res = sqlx::query_as!(
|
|
||||||
Res,
|
|
||||||
"
|
|
||||||
WITH RECURSIVE
|
WITH RECURSIVE
|
||||||
graph(p) AS (
|
graph(p) AS (
|
||||||
SELECT id
|
SELECT id
|
||||||
|
@ -81,18 +30,34 @@ WITH RECURSIVE
|
||||||
)
|
)
|
||||||
SELECT SUM(narSize) as sum from graph
|
SELECT SUM(narSize) as sum from graph
|
||||||
JOIN ValidPaths ON p = id;
|
JOIN ValidPaths ON p = id;
|
||||||
",
|
";
|
||||||
p
|
|
||||||
)
|
/// executes a query on the nix db directly
|
||||||
.fetch_one(&mut conn)
|
/// to gather all derivations that the derivation given by the path
|
||||||
.await
|
/// depends on
|
||||||
.map_err(|_| "Could not execute package query")?
|
///
|
||||||
.sum
|
/// in the future, we might wan't to switch to async
|
||||||
.ok_or("Could not get closure size sum")?;
|
pub fn get_packages(path: &std::path::Path) -> Result<Vec<String>> {
|
||||||
Ok::<_, String>(query_res)
|
let p: String = path.canonicalize()?.to_string_lossy().into_owned();
|
||||||
});
|
let conn = Connection::open("/nix/var/nix/db/db.sqlite")?;
|
||||||
size
|
|
||||||
|
let mut stmt = conn.prepare(QUERY_PKGS)?;
|
||||||
|
let queried_pkgs: std::result::Result<Vec<String>, _> =
|
||||||
|
stmt.query_map([p], |row| row.get(0))?.collect();
|
||||||
|
Ok(queried_pkgs?)
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
/// executes a query on the nix db directly
|
||||||
//
|
/// to get the total closure size of the derivation
|
||||||
|
/// by summing up the nar size of all derivations
|
||||||
|
/// depending on the derivation
|
||||||
|
///
|
||||||
|
/// in the future, we might wan't to switch to async
|
||||||
|
pub fn get_closure_size(path: &std::path::Path) -> Result<i64> {
|
||||||
|
let p: String = path.canonicalize()?.to_string_lossy().into_owned();
|
||||||
|
let conn = Connection::open("/nix/var/nix/db/db.sqlite")?;
|
||||||
|
|
||||||
|
let mut stmt = conn.prepare(QUERY_CLOSURE_SIZE)?;
|
||||||
|
let queried_sum = stmt.query_row([p], |row| row.get(0))?;
|
||||||
|
Ok(queried_sum)
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue