use chrono::NaiveDateTime;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum DecayKind {
HyperbolicDays,
ExpHalfLife,
ZoxideLogBuckets,
}
impl DecayKind {
#[must_use]
pub fn decay(self, age_days: f64, half_life_days: f64) -> f64 {
let age = age_days.max(0.0);
match self {
DecayKind::HyperbolicDays => 1.0 / (1.0 + age),
DecayKind::ExpHalfLife => {
let hl = if half_life_days <= 0.0 { 1.0 } else { half_life_days };
2.0_f64.powf(-age / hl)
}
DecayKind::ZoxideLogBuckets => {
if age < 1.0 / 24.0 {
4.0
} else if age < 1.0 {
2.0
} else if age < 7.0 {
0.5
} else {
0.25
}
}
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
#[serde(tag = "kind")]
pub enum RankPhase {
LoadEntries,
ComputeAge,
ApplyDecay,
Combine,
FloorIndexed,
SortDesc,
TopK { n: usize },
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct FrecencyRankingSpec {
pub name: String,
pub decay: DecayKind,
pub half_life_days: f64,
pub freq_weight: f64,
pub recency_weight: f64,
pub indexed_epsilon: f64,
pub phases: Vec<RankPhase>,
}
impl FrecencyRankingSpec {
#[must_use]
pub fn canonical_phases() -> Vec<RankPhase> {
vec![
RankPhase::LoadEntries,
RankPhase::ComputeAge,
RankPhase::ApplyDecay,
RankPhase::Combine,
RankPhase::FloorIndexed,
RankPhase::SortDesc,
RankPhase::TopK { n: 50 },
]
}
#[must_use]
pub fn skimtab_parity() -> Self {
Self {
name: "skimtab-parity".to_owned(),
decay: DecayKind::HyperbolicDays,
half_life_days: 0.0,
freq_weight: 0.0,
recency_weight: 1.0,
indexed_epsilon: 0.001,
phases: Self::canonical_phases(),
}
}
#[must_use]
pub fn zoxide_parity() -> Self {
Self {
name: "zoxide-parity".to_owned(),
decay: DecayKind::ExpHalfLife,
half_life_days: 30.0,
freq_weight: 1.0,
recency_weight: 1.0,
indexed_epsilon: 0.001,
phases: Self::canonical_phases(),
}
}
#[must_use]
pub fn by_name(name: &str) -> Option<Self> {
match name {
"skimtab-parity" => Some(Self::skimtab_parity()),
"zoxide-parity" => Some(Self::zoxide_parity()),
_ => None,
}
}
#[must_use]
pub fn all() -> Vec<Self> {
vec![Self::skimtab_parity(), Self::zoxide_parity()]
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DirEntry {
pub path: PathBuf,
pub visits: Vec<NaiveDateTime>,
pub discovered_only: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct RankedDir {
pub path: PathBuf,
pub score: f64,
}