use crate::song::Song;
use itertools::Itertools;
use skim::{
prelude::{SkimItemReader, SkimOptionsBuilder},
Skim,
};
use std::io::Cursor;
fn get_choices(prompt: String, items: Vec<&String>) -> Vec<String> {
let options = SkimOptionsBuilder::default()
.height(String::from("100%"))
.multi(true)
.prompt(prompt)
.build()
.unwrap();
let items = SkimItemReader::default().of_bufread(Cursor::new(items.iter().join("\n")));
let result = Skim::run_with(&options, Some(items)).expect("No skim result.");
match result.final_event {
skim::prelude::Event::EvActAccept(_) => result
.selected_items
.iter()
.map(|item| item.output().to_string())
.collect(),
_ => Vec::new(),
}
}
fn filter_artists(songs: &Vec<Song>) -> Vec<&str> {
let artists: Vec<&String> = songs
.iter()
.map(|song| &song.artists)
.flatten()
.unique()
.collect();
let artists = get_choices(String::from("artist(s): "), artists);
songs
.iter()
.filter(|song| song.artists.iter().any(|artist| artists.contains(artist)))
.filter_map(|song| song.path.to_str())
.collect()
}
fn filter_single<F>(songs: &Vec<Song>, extractor: F, prompt: String) -> Vec<&str>
where
F: Fn(&Song) -> &String,
{
let attrs: Vec<&String> = songs.iter().map(|song| extractor(song)).unique().collect();
let attrs = get_choices(prompt, attrs);
songs
.iter()
.filter(|song| attrs.contains(extractor(song)))
.filter_map(|song| song.path.to_str())
.collect()
}
pub fn filter<'a>(songs: &'a Vec<Song>, attr: &'a str) -> Option<Vec<&'a str>> {
match attr {
"title" => Some(filter_single(
&songs,
|song| &song.title,
String::from("title: "),
)),
"album" => Some(filter_single(
&songs,
|song| &song.album,
String::from("album: "),
)),
"artist" => Some(filter_artists(&songs)),
"genre" => Some(filter_single(
&songs,
|song| &song.genre,
String::from("genre: "),
)),
"year" => Some(filter_single(
&songs,
|song| &song.year,
String::from("year: "),
)),
_ => None,
}
}