use crate::pm::PmInfo;
#[derive(Debug, Clone)]
pub struct Bump {
pub latest: String,
pub cmd: &'static str,
}
pub trait Find {
type Output;
fn name(&self) -> &'static str;
fn search_paths(&self) -> &'static [&'static str] {
&[] }
fn find(&self) -> Vec<Self::Output>;
fn check_bump(&self, _pm_info: &PmInfo) -> Option<Bump> {
None
}
}
impl<T: Find + ?Sized> Find for &T {
type Output = T::Output;
fn name(&self) -> &'static str {
(**self).name()
}
fn search_paths(&self) -> &'static [&'static str] {
(**self).search_paths()
}
fn find(&self) -> Vec<Self::Output> {
(**self).find()
}
fn check_bump(&self, pm_info: &PmInfo) -> Option<Bump> {
(**self).check_bump(pm_info)
}
}
impl<T: Find + ?Sized> Find for Box<T> {
type Output = T::Output;
fn name(&self) -> &'static str {
(**self).name()
}
fn search_paths(&self) -> &'static [&'static str] {
(**self).search_paths()
}
fn find(&self) -> Vec<Self::Output> {
(**self).find()
}
fn check_bump(&self, pm_info: &PmInfo) -> Option<Bump> {
(**self).check_bump(pm_info)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pm::{InstallMethod, PmInfo};
struct MockFinder {
name: &'static str,
results: Vec<PmInfo>,
}
impl MockFinder {
fn new(name: &'static str, results: Vec<PmInfo>) -> Self {
Self { name, results }
}
}
impl Find for MockFinder {
type Output = PmInfo;
fn name(&self) -> &'static str {
self.name
}
fn search_paths(&self) -> &'static [&'static str] {
&["~/.volta/bin/test", "~/.asdf/shims/test"]
}
fn find(&self) -> Vec<Self::Output> {
self.results.clone()
}
}
fn test_pm_info() -> PmInfo {
PmInfo {
name: "test".to_string(),
version: "1.0.0".to_string(),
path: "/usr/bin/test".to_string(),
install_method: InstallMethod::Unknown,
latest_version: None,
}
}
#[test]
fn find_trait_implementation() {
let finder = MockFinder::new("npm", vec![test_pm_info()]);
let name = finder.name();
let results = finder.find();
assert_eq!(name, "npm");
assert_eq!(results.len(), 1);
}
#[test]
fn find_blanket_impl_reference_name() {
let finder = MockFinder::new("test", vec![]);
let finder_ref: &MockFinder = &finder;
let name = <&MockFinder as Find>::name(&finder_ref);
assert_eq!(name, "test");
}
#[test]
fn find_blanket_impl_reference_find() {
let finder = MockFinder::new("test", vec![test_pm_info()]);
let finder_ref: &MockFinder = &finder;
let results = <&MockFinder as Find>::find(&finder_ref);
assert_eq!(results.len(), 1);
}
#[test]
fn find_blanket_impl_box_name() {
let finder = MockFinder::new("test", vec![]);
let finder_box: Box<MockFinder> = Box::new(finder);
let name = <Box<MockFinder> as Find>::name(&finder_box);
assert_eq!(name, "test");
}
#[test]
fn find_blanket_impl_box_find() {
let finder = MockFinder::new("test", vec![test_pm_info()]);
let finder_box: Box<MockFinder> = Box::new(finder);
let results = <Box<MockFinder> as Find>::find(&finder_box);
assert_eq!(results.len(), 1);
}
#[test]
fn find_empty_results() {
let finder = MockFinder::new("empty", vec![]);
let results = finder.find();
assert!(results.is_empty());
}
#[test]
fn find_multiple_results() {
let pm1 = test_pm_info();
let pm2 = test_pm_info();
let finder = MockFinder::new("multi", vec![pm1, pm2]);
let results = finder.find();
assert_eq!(results.len(), 2);
}
#[test]
fn find_blanket_impl_reference_search_paths() {
let finder = MockFinder::new("npm", vec![]);
let finder_ref: &MockFinder = &finder;
let paths = <&MockFinder as Find>::search_paths(&finder_ref);
assert_eq!(paths, &["~/.volta/bin/test", "~/.asdf/shims/test"]);
}
#[test]
fn find_blanket_impl_box_search_paths() {
let finder = MockFinder::new("cargo", vec![]);
let finder_box: Box<MockFinder> = Box::new(finder);
let paths = <Box<MockFinder> as Find>::search_paths(&finder_box);
assert_eq!(paths, &["~/.volta/bin/test", "~/.asdf/shims/test"]);
}
#[test]
fn default_check_bump_is_none() {
let finder = MockFinder::new("test", vec![]);
let pm = test_pm_info();
let bump = <MockFinder as Find>::check_bump(&finder, &pm);
assert!(bump.is_none());
}
#[test]
fn blanket_impl_reference_check_bump_none() {
let finder = MockFinder::new("test", vec![]);
let finder_ref: &MockFinder = &finder;
let pm = test_pm_info();
let bump = <&MockFinder as Find>::check_bump(&finder_ref, &pm);
assert!(bump.is_none());
}
#[test]
fn blanket_impl_box_check_bump_none() {
let finder: Box<MockFinder> = Box::new(MockFinder::new("test", vec![]));
let pm = test_pm_info();
let bump = <Box<MockFinder> as Find>::check_bump(&finder, &pm);
assert!(bump.is_none());
}
}