Skip to content

Commit

Permalink
feat(oma-ubuntu-cmd-not-found): init for ubuntu command-not-found query
Browse files Browse the repository at this point in the history
  • Loading branch information
eatradish committed Sep 14, 2024
1 parent 27cb4c6 commit 325a22d
Show file tree
Hide file tree
Showing 6 changed files with 227 additions and 8 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ oma-fetch = { path = "./oma-fetch", default-features = false }
oma-topics = { path = "./oma-topics", optional = true, default-features = false }
oma-history = { path = "./oma-history" }
oma-repo-verify = { path = "./oma-repo-verify" }
oma-ubuntu-cmd-not-found = { path = "./oma-ubuntu-cmd-not-found", optional = true }

# i18n
i18n-embed = { version = "0.15.0", features = ["fluent-system", "desktop-requester"]}
Expand All @@ -70,13 +71,14 @@ sequoia-openssl-backend = ["oma-refresh/sequoia-openssl-backend"]
sequoia-nettle-backend = ["oma-refresh/sequoia-nettle-backend"]
egg = ["dep:colored", "dep:image"]
tokio-console = ["dep:console-subscriber"]
ubuntu = ["dep:oma-ubuntu-cmd-not-found"]
rustls = ["reqwest/rustls-tls", "oma-fetch/rustls", "oma-refresh/rustls", "oma-topics/rustls"]
openssl = ["reqwest/native-tls", "oma-fetch/native-tls", "oma-refresh/native-tls", "oma-topics/native-tls"]
generic = ["sequoia-nettle-backend", "rustls"]
default = ["aosc", "generic"]

[workspace]
members = ["oma-contents", "oma-console", "oma-topics", "oma-fetch", "oma-refresh", "oma-utils", "oma-pm", "oma-history", "oma-pm-operation-type", "oma-repo-verify"]
members = ["oma-contents", "oma-console", "oma-topics", "oma-fetch", "oma-refresh", "oma-utils", "oma-pm", "oma-history", "oma-pm-operation-type", "oma-repo-verify", "oma-ubuntu-cmd-not-found"]

[package.metadata.deb]
copyright = "2024, AOSC Dev <[email protected]>"
Expand Down Expand Up @@ -104,3 +106,4 @@ default-features = false
lto = "thin"
opt-level = 3
codegen-units = 1
debug = 2
10 changes: 10 additions & 0 deletions oma-ubuntu-cmd-not-found/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "oma-ubuntu-cmd-not-found"
version = "0.1.0"
edition = "2021"
description = "Ubuntu command-not-found database handling library"

[dependencies]
rusqlite = { version = "0.32", features = ["bundled"] }
tracing = "0.1"
thiserror = "1.0"
107 changes: 107 additions & 0 deletions oma-ubuntu-cmd-not-found/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use rusqlite::Connection;
pub use rusqlite::Error;
use std::path::Path;

pub struct UbuntuCmdNotFound {
db: Connection,
}

impl UbuntuCmdNotFound {
const DEFAULT_DB_PATH: &str = "/var/lib/command-not-found/commands.db";
pub fn new(db: impl AsRef<Path>) -> Result<Self, rusqlite::Error> {
let db = Connection::open(db)?;

Ok(Self { db })
}

pub fn default_new() -> Result<Self, rusqlite::Error> {
Self::new(Self::DEFAULT_DB_PATH)
}

pub fn query_where_command_like(&self, query: &str) -> Result<Vec<(String, String)>, Error> {
let mut stmt = self
.db
.prepare("SELECT pkgID, command FROM commands WHERE command LIKE ?1")?;
let query_str = format!("{}%", query);
let res_iter = stmt.query_map([query_str], |row| {
let pkg_id: i64 = row.get(0)?;
let cmd: String = row.get(1)?;

Ok((pkg_id, cmd))
})?;
let mut res = vec![];
for i in res_iter {
let (pkg_id, cmd) = i?;
let pkgs = self.get_pkg_from_from_pkg_id(pkg_id)?;

for pkg in pkgs {
res.push((pkg, cmd.clone()));
}
}
Ok(res)
}

pub fn query_where_command_count(&self, query: &str) -> Result<i64, Error> {
let mut stmt = self
.db
.prepare("SELECT COUNT(command) AS count FROM commands WHERE command = ?1")?;
let mut res_iter = stmt.query_map([query], |row| row.get(0))?;

if let Some(Ok(n)) = res_iter.next() {
return Ok(n);
}

Ok(0)
}

pub fn query_where_command_like_count(&self, query: &str) -> Result<i64, Error> {
let mut stmt = self
.db
.prepare("SELECT COUNT(command) AS count FROM commands WHERE command LIKE ?")?;
let query_str = format!("{}%", query);
let mut res_iter = stmt.query_map([query_str], |row| row.get(0))?;

if let Some(Ok(n)) = res_iter.next() {
return Ok(n);
}

Ok(0)
}

pub fn query_where_command(&self, query: &str) -> Result<Vec<(String, String)>, Error> {
let mut stmt = self
.db
.prepare("SELECT pkgID, command FROM commands WHERE command = ?1")?;
let res_iter = stmt.query_map([query], |row| {
let pkg_id: i64 = row.get(0)?;
let cmd: String = row.get(1)?;

Ok((pkg_id, cmd))
})?;
let mut res = vec![];
for i in res_iter {
let (pkg_id, cmd) = i?;
let pkgs = self.get_pkg_from_from_pkg_id(pkg_id)?;

for pkg in pkgs {
res.push((pkg, cmd.clone()));
}
}
Ok(res)
}

fn get_pkg_from_from_pkg_id(&self, pkg_id: i64) -> Result<Vec<String>, rusqlite::Error> {
let mut res = vec![];
let mut stmt = self
.db
.prepare("SELECT name FROM packages where pkgID = ?1")?;
let pkgs = stmt.query_map([pkg_id], |row| row.get(0))?;

for pkg in pkgs {
let pkg = pkg?;
res.push(pkg);
}

Ok(res)
}
}
10 changes: 10 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,16 @@ impl From<anyhow::Error> for OutputError {
}
}

#[cfg(feature = "ubuntu")]
impl From<oma_ubuntu_cmd_not_found::Error> for OutputError {
fn from(value: oma_ubuntu_cmd_not_found::Error) -> Self {
Self {
description: value.to_string(),
source: Some(Box::new(value)),
}
}
}

pub fn oma_apt_error_to_output(err: OmaAptError) -> OutputError {
debug!("{:?}", err);
match err {
Expand Down
93 changes: 86 additions & 7 deletions src/subcommand/command_not_found.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
use std::error::Error;
use std::io::stdout;

use ahash::AHashMap;
use oma_console::due_to;
use oma_console::print::Action;
use oma_contents::searcher::{pure_search, ripgrep_search, Mode};
use oma_contents::OmaContentsError;
use oma_pm::apt::{AptConfig, OmaApt, OmaAptArgs};
use oma_pm::format_description;
use tracing::error;
Expand All @@ -14,12 +10,19 @@ use crate::error::OutputError;
use crate::table::PagerPrinter;
use crate::{color_formatter, fl};

const FILTER_JARO_NUM: u8 = 204;
const APT_LIST_PATH: &str = "/var/lib/apt/lists";

#[cfg(not(feature = "ubuntu"))]
type IndexSet<T> = indexmap::IndexSet<T, ahash::RandomState>;

#[cfg(not(feature = "ubuntu"))]
pub fn execute(query: &str) -> Result<i32, OutputError> {
use oma_console::due_to;
use oma_contents::searcher::{pure_search, ripgrep_search, Mode};
use oma_contents::OmaContentsError;
use std::error::Error;

const FILTER_JARO_NUM: u8 = 204;
const APT_LIST_PATH: &str = "/var/lib/apt/lists";

let mut res = IndexSet::with_hasher(ahash::RandomState::new());

let cb = |line| {
Expand Down Expand Up @@ -136,6 +139,82 @@ pub fn execute(query: &str) -> Result<i32, OutputError> {
Ok(127)
}

#[cfg(feature = "ubuntu")]
pub fn execute(query: &str) -> Result<i32, OutputError> {
use oma_ubuntu_cmd_not_found::UbuntuCmdNotFound;

let cnf = UbuntuCmdNotFound::default_new()?;
let count = cnf.query_where_command_count(query)?;

let query_res = if count != 0 {
cnf.query_where_command(query)?
} else if cnf.query_where_command_like_count(query)? == 0 {
error!("{}", fl!("command-not-found", kw = query));
return Ok(127);
} else {
cnf.query_where_command_like(query)?
};

let mut too_many = false;
let mut map: AHashMap<String, String> = AHashMap::new();
let apt_config = AptConfig::new();
let oma_apt_args = OmaAptArgs::builder().build();
let apt = OmaApt::new(vec![], oma_apt_args, false, apt_config)?;
let mut res = vec![];

for (i, (pkg, cmd)) in query_res.iter().enumerate() {
if i == 10 {
too_many = true;
break;
}

let desc = if let Some(desc) = map.get(pkg) {
desc.to_string()
} else if let Some(pkg) = apt.cache.get(&pkg) {
let desc = pkg
.candidate()
.and_then(|x| {
x.description()
.map(|x| format_description(&x).0.to_string())
})
.unwrap_or_else(|| "no description.".to_string());

map.insert(pkg.name().to_string(), desc.to_string());

desc
} else {
continue;
};

let entry = (
color_formatter()
.color_str(pkg, Action::Emphasis)
.bold()
.to_string(),
color_formatter()
.color_str(cmd, Action::Secondary)
.to_string(),
desc,
);

res.push(entry);
}

println!("{}\n", fl!("command-not-found-with-result", kw = query));
let mut printer = PagerPrinter::new(stdout());
printer
.print_table(res, vec!["Name", "Path", "Description"])
.ok();

if too_many {
println!("\n{}", fl!("cnf-too-many-query"));
println!("{}", fl!("cnf-too-many-query-2", query = query));
}

Ok(127)
}

#[cfg(not(feature = "ubuntu"))]
fn jaro_nums(input: IndexSet<(String, String)>, query: &str) -> Vec<(String, String, u8)> {
let mut output = vec![];

Expand Down

0 comments on commit 325a22d

Please sign in to comment.