Skip to content

Commit

Permalink
refactor(fuzzy): add error type using thiserror crate
Browse files Browse the repository at this point in the history
IMHO extracting error messages makes the code more readable
This also decouples everything but lib.rs and the lua
type wrappers from mlua
  • Loading branch information
stefanboca committed Jan 8, 2025
1 parent 7742416 commit 4c9e524
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 94 deletions.
21 changes: 21 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ frizbee = { git = "https://github.com/saghen/frizbee" }
serde = { version = "1.0.216", features = ["derive"] }
heed = "0.21.0"
mlua = { version = "0.10.2", features = ["module", "luajit"] }
thiserror = "2.0.10"
44 changes: 44 additions & 0 deletions lua/blink/cmp/fuzzy/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum Error {
#[error("Failed to acquire lock for frecency")]
AcquireFrecencyLock,

#[error("Failed to acquire lock for items by provider")]
AcquireItemLock,

#[error("Attempted to use frencecy before initialization")]
UseFrecencyBeforeInit,

#[error(
"Attempted to fuzzy match for provider {provider_id} before setting the provider's items"
)]
FuzzyBeforeSetItems { provider_id: String },

#[error("Failed to create frecency database directory: {0}")]
CreateDir(#[source] std::io::Error),
#[error("Failed to open frecency database env: {0}")]
EnvOpen(#[source] heed::Error),
#[error("Failed to create frecency database: {0}")]
DbCreate(#[source] heed::Error),
#[error("Failed to clear stale readers for frecency database: {0}")]
DbClearStaleReaders(#[source] heed::Error),

#[error("Failed to start read transaction for frecency database: {0}")]
DbStartReadTxn(#[source] heed::Error),
#[error("Failed to start write transaction for frecency database: {0}")]
DbStartWriteTxn(#[source] heed::Error),

#[error("Failed to read from frecency database: {0}")]
DbRead(#[source] heed::Error),
#[error("Failed to write to frecency database: {0}")]
DbWrite(#[source] heed::Error),
#[error("Failed to commit write transaction to frecency database: {0}")]
DbCommit(#[source] heed::Error),
}

impl From<Error> for mlua::Error {
fn from(value: Error) -> Self {
mlua::Error::RuntimeError(value.to_string())
}
}
76 changes: 17 additions & 59 deletions lua/blink/cmp/fuzzy/frecency.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::error::Error;
use crate::lsp_item::LspItem;
use heed::{types::*, EnvFlags};
use heed::{Database, Env, EnvOpenOptions};
use mlua::Result as LuaResult;
use serde::{Deserialize, Serialize};
use std::fs;
use std::time::{SystemTime, UNIX_EPOCH};
Expand Down Expand Up @@ -31,42 +31,23 @@ pub struct FrecencyTracker {
}

impl FrecencyTracker {
pub fn new(db_path: &str, use_unsafe_no_lock: bool) -> LuaResult<Self> {
fs::create_dir_all(db_path).map_err(|err| {
mlua::Error::RuntimeError(
"Failed to create frecency database directory: ".to_string() + &err.to_string(),
)
})?;
pub fn new(db_path: &str, use_unsafe_no_lock: bool) -> Result<Self, Error> {
fs::create_dir_all(db_path).map_err(Error::CreateDir)?;
let env = unsafe {
let mut opts = EnvOpenOptions::new();
if use_unsafe_no_lock {
opts.flags(EnvFlags::NO_LOCK | EnvFlags::NO_SYNC | EnvFlags::NO_META_SYNC);
}
opts.open(db_path).map_err(|err| {
mlua::Error::RuntimeError(
"Failed to open frecency database: ".to_string() + &err.to_string(),
)
})?
opts.open(db_path).map_err(Error::EnvOpen)?
};
env.clear_stale_readers().map_err(|err| {
mlua::Error::RuntimeError(
"Failed to clear stale readers for frecency database: ".to_string()
+ &err.to_string(),
)
})?;
env.clear_stale_readers()
.map_err(Error::DbClearStaleReaders)?;

// we will open the default unnamed database
let mut wtxn = env.write_txn().map_err(|err| {
mlua::Error::RuntimeError(
"Failed to open write transaction for frecency database: ".to_string()
+ &err.to_string(),
)
})?;
let db = env.create_database(&mut wtxn, None).map_err(|err| {
mlua::Error::RuntimeError(
"Failed to create frecency database: ".to_string() + &err.to_string(),
)
})?;
let mut wtxn = env.write_txn().map_err(Error::DbStartWriteTxn)?;
let db = env
.create_database(&mut wtxn, None)
.map_err(Error::DbCreate)?;

let access_thresholds = [
(1., 1000 * 60 * 2), // 2 minutes
Expand All @@ -83,20 +64,11 @@ impl FrecencyTracker {
})
}

fn get_accesses(&self, item: &LspItem) -> LuaResult<Option<Vec<u64>>> {
let rtxn = self.env.read_txn().map_err(|err| {
mlua::Error::RuntimeError(
"Failed to start read transaction for frecency database: ".to_string()
+ &err.to_string(),
)
})?;
fn get_accesses(&self, item: &LspItem) -> Result<Option<Vec<u64>>, Error> {
let rtxn = self.env.read_txn().map_err(Error::DbStartReadTxn)?;
self.db
.get(&rtxn, &CompletionItemKey::from(item))
.map_err(|err| {
mlua::Error::RuntimeError(
"Failed to read from frecency database: ".to_string() + &err.to_string(),
)
})
.map_err(Error::DbRead)
}

fn get_now(&self) -> u64 {
Expand All @@ -106,31 +78,17 @@ impl FrecencyTracker {
.as_secs()
}

pub fn access(&mut self, item: &LspItem) -> LuaResult<()> {
let mut wtxn = self.env.write_txn().map_err(|err| {
mlua::Error::RuntimeError(
"Failed to start write transaction for frecency database: ".to_string()
+ &err.to_string(),
)
})?;
pub fn access(&self, item: &LspItem) -> Result<(), Error> {
let mut wtxn = self.env.write_txn().map_err(Error::DbStartWriteTxn)?;

let mut accesses = self.get_accesses(item)?.unwrap_or_default();
accesses.push(self.get_now());

self.db
.put(&mut wtxn, &CompletionItemKey::from(item), &accesses)
.map_err(|err| {
mlua::Error::RuntimeError(
"Failed to write to frecency database: ".to_string() + &err.to_string(),
)
})?;
.map_err(Error::DbWrite)?;

wtxn.commit().map_err(|err| {
mlua::Error::RuntimeError(
"Failed to commit write transaction for frecency database: ".to_string()
+ &err.to_string(),
)
})?;
wtxn.commit().map_err(Error::DbCommit)?;

Ok(())
}
Expand Down
55 changes: 20 additions & 35 deletions lua/blink/cmp/fuzzy/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::error::Error;
use crate::frecency::FrecencyTracker;
use crate::fuzzy::FuzzyOptions;
use crate::lsp_item::LspItem;
Expand All @@ -6,6 +7,7 @@ use regex::Regex;
use std::collections::{HashMap, HashSet};
use std::sync::{LazyLock, RwLock};

mod error;
mod frecency;
mod fuzzy;
mod keyword;
Expand All @@ -17,9 +19,7 @@ static HAYSTACKS_BY_PROVIDER: LazyLock<RwLock<HashMap<String, Vec<LspItem>>>> =
LazyLock::new(|| RwLock::new(HashMap::new()));

pub fn init_db(_: &Lua, (db_path, use_unsafe_no_lock): (String, bool)) -> LuaResult<bool> {
let mut frecency = FRECENCY.write().map_err(|_| {
mlua::Error::RuntimeError("Failed to acquire lock for frecency".to_string())
})?;
let mut frecency = FRECENCY.write().map_err(|_| Error::AcquireFrecencyLock)?;
if frecency.is_some() {
return Ok(false);
}
Expand All @@ -28,26 +28,18 @@ pub fn init_db(_: &Lua, (db_path, use_unsafe_no_lock): (String, bool)) -> LuaRes
}

pub fn destroy_db(_: &Lua, _: ()) -> LuaResult<bool> {
let frecency = FRECENCY.write().map_err(|_| {
mlua::Error::RuntimeError("Failed to acquire lock for frecency".to_string())
})?;
let frecency = FRECENCY.write().map_err(|_| Error::AcquireFrecencyLock)?;
drop(frecency);

let mut frecency = FRECENCY.write().map_err(|_| {
mlua::Error::RuntimeError("Failed to acquire lock for frecency".to_string())
})?;
let mut frecency = FRECENCY.write().map_err(|_| Error::AcquireFrecencyLock)?;
*frecency = None;

Ok(true)
}

pub fn access(_: &Lua, item: LspItem) -> LuaResult<bool> {
let mut frecency_handle = FRECENCY.write().map_err(|_| {
mlua::Error::RuntimeError("Failed to acquire lock for frecency".to_string())
})?;
let frecency = frecency_handle.as_mut().ok_or_else(|| {
mlua::Error::RuntimeError("Attempted to use frencecy before initialization".to_string())
})?;
let mut frecency = FRECENCY.write().map_err(|_| Error::AcquireFrecencyLock)?;
let frecency = frecency.as_mut().ok_or(Error::UseFrecencyBeforeInit)?;
frecency.access(&item)?;
Ok(true)
}
Expand All @@ -56,33 +48,26 @@ pub fn set_provider_items(
_: &Lua,
(provider_id, items): (String, Vec<LspItem>),
) -> LuaResult<bool> {
let mut items_by_provider = HAYSTACKS_BY_PROVIDER.write().map_err(|_| {
mlua::Error::RuntimeError("Failed to acquire lock for items by provider".to_string())
})?;
items_by_provider.insert(provider_id, items);
HAYSTACKS_BY_PROVIDER
.write()
.map_err(|_| Error::AcquireItemLock)?
.insert(provider_id, items);
Ok(true)
}

pub fn fuzzy(
_lua: &Lua,
(line, cursor_col, provider_id, opts): (String, usize, String, FuzzyOptions),
) -> LuaResult<(Vec<i32>, Vec<u32>)> {
let frecency_handle = FRECENCY.read().map_err(|_| {
mlua::Error::RuntimeError("Failed to acquire lock for frecency".to_string())
})?;
let frecency = frecency_handle.as_ref().ok_or_else(|| {
mlua::Error::RuntimeError("Attempted to use frencecy before initialization".to_string())
})?;

let haystacks_by_provider = HAYSTACKS_BY_PROVIDER.read().map_err(|_| {
mlua::Error::RuntimeError("Failed to acquire lock for items by provider".to_string())
})?;
let haystack = haystacks_by_provider.get(&provider_id).ok_or_else(|| {
mlua::Error::RuntimeError(format!(
"Attempted to fuzzy match for provider {} before setting the provider's items",
provider_id
))
})?;
let frecency = FRECENCY.read().map_err(|_| Error::AcquireFrecencyLock)?;
let frecency = frecency.as_ref().ok_or(Error::UseFrecencyBeforeInit)?;

let haystacks_by_provider = HAYSTACKS_BY_PROVIDER
.read()
.map_err(|_| Error::AcquireItemLock)?;
let haystack = haystacks_by_provider
.get(&provider_id)
.ok_or(Error::FuzzyBeforeSetItems { provider_id })?;

Ok(fuzzy::fuzzy(&line, cursor_col, haystack, frecency, opts))
}
Expand Down

0 comments on commit 4c9e524

Please sign in to comment.