mirror of
https://codeberg.org/unspeaker/perch.git
synced 2025-12-06 09:36:42 +01:00
unify setters and deleters
This commit is contained in:
parent
25ca263cd7
commit
d1889481b1
5 changed files with 86 additions and 163 deletions
12
src/keys.rs
12
src/keys.rs
|
|
@ -47,9 +47,9 @@ impl Handle<TuiIn> for Taggart {
|
|||
press!(Right) => { self.column_select( 1); },
|
||||
press!(Char('[')) => { self.column_resize(-1); },
|
||||
press!(Char(']')) => { self.column_resize( 1); },
|
||||
press!(Char('{')) => { self.column_collapse(true, 1); },
|
||||
press!(Char('}')) => { self.column_collapse(false, -1); },
|
||||
press!(Delete) => { self.clear() },
|
||||
press!(Char('{')) => { self.column_collapse(true); self.column_select( 1); },
|
||||
press!(Char('}')) => { self.column_collapse(false); self.column_select(-1); },
|
||||
press!(Delete) => { self.edit_clear() },
|
||||
press!(Enter) => { self.edit_begin() },
|
||||
press!(Char(' ')) => { self.open_in_player()?; },
|
||||
_ => {}
|
||||
|
|
@ -61,9 +61,6 @@ impl Handle<TuiIn> for Taggart {
|
|||
}
|
||||
|
||||
impl Taggart {
|
||||
fn clear (&self) {
|
||||
todo!("clear")
|
||||
}
|
||||
fn open_in_player (&self) -> Usually<()> {
|
||||
open(self.entries[self.cursor].path.as_ref())?;
|
||||
Ok(())
|
||||
|
|
@ -78,9 +75,8 @@ impl Taggart {
|
|||
let column = &mut self.columns.0[self.column];
|
||||
column.width = ((column.width as isize) + amount).max(0) as usize;
|
||||
}
|
||||
fn column_collapse (&mut self, value: bool, next: isize) {
|
||||
fn column_collapse (&mut self, value: bool) {
|
||||
let column = &mut self.columns.0[self.column];
|
||||
column.collapsed = value;
|
||||
self.column = ((self.column as isize) + next).max(0) as usize;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,12 +35,17 @@ impl Taggart {
|
|||
&& let Some(setter) = &column.setter
|
||||
&& self.entries.get_mut(self.cursor).is_some()
|
||||
{
|
||||
setter(self, self.cursor, value.as_ref());
|
||||
setter(self, self.cursor, Some(value.as_ref()));
|
||||
self.mode = None;
|
||||
}
|
||||
}
|
||||
pub fn edit_clear (&mut self) {
|
||||
todo!("edit_clear")
|
||||
if self.entries.get_mut(self.cursor).is_some()
|
||||
&& let Some(column) = self.columns.0.get(self.column)
|
||||
&& let Some(setter) = &column.setter
|
||||
{
|
||||
setter(self, self.cursor, None);
|
||||
}
|
||||
}
|
||||
pub fn edit_insert (&mut self, c: char) {
|
||||
if let Some(Mode::Edit { value, index }) = &self.mode {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ pub struct Taggart {
|
|||
/// Table columns to display
|
||||
pub columns: Columns<
|
||||
fn(&Entry)->Option<Arc<str>>,
|
||||
fn(&mut Self, usize, &str),
|
||||
fn(&mut Self, usize, Option<&str>),
|
||||
fn(&Entry)->Option<Style>,
|
||||
>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ pub struct Column<G, S, U> {
|
|||
}
|
||||
|
||||
type Getter<T> = fn(&T)->Option<Arc<str>>;
|
||||
type Setter<T> = fn(&mut T, usize, &str);
|
||||
type Setter<T> = fn(&mut T, usize, Option<&str>);
|
||||
type Styler<T> = fn(&T)->Option<Style>;
|
||||
|
||||
impl<G, S, U> Column<Getter<G>, Setter<S>, Styler<U>> {
|
||||
|
|
@ -63,21 +63,16 @@ pub struct Columns<G, S, U>(pub Vec<Column<G, S, U>>);
|
|||
|
||||
macro_rules! setter {
|
||||
($set:ident) => {{
|
||||
fn $set (state: &mut Taggart, index: usize, value: &str) {
|
||||
fn $set (state: &mut Taggart, index: usize, value: Option<&str>) {
|
||||
if let Some(entries) = entries_under(&mut state.entries, index) {
|
||||
for (_path, entry) in entries.into_iter() {
|
||||
if entry.write().unwrap().$set(&value) {
|
||||
if entry.write().unwrap().$set(value) {
|
||||
state.count_changes();
|
||||
//state.tasks.retain(|t|{(t.path != p) || (t.item.key() != item.key())});
|
||||
//state.tasks.push(Task { path: p, item, });
|
||||
};
|
||||
}
|
||||
} else if let Some(entry) = state.entries.get_mut(index) {
|
||||
//let p = entry.path.clone();
|
||||
if entry.$set(&value) {
|
||||
if entry.$set(value) {
|
||||
state.count_changes();
|
||||
//state.tasks.retain(|t|{(t.path != p) || (t.item.key() != item.key())});
|
||||
//state.tasks.push(Task { path: p, item, });
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -87,7 +82,7 @@ macro_rules! setter {
|
|||
|
||||
impl Default for Columns<
|
||||
fn(&Entry)->Option<Arc<str>>,
|
||||
fn(&mut Taggart, usize, &str),
|
||||
fn(&mut Taggart, usize, Option<&str>),
|
||||
fn(&Entry)->Option<Style>,
|
||||
> {
|
||||
fn default () -> Self {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use crate::*;
|
||||
use std::fs::File;
|
||||
use std::borrow::Cow;
|
||||
use std::io::{BufReader, Read};
|
||||
use std::cmp::{Eq, PartialEq, Ord, PartialOrd, Ordering};
|
||||
use byte_unit::{Byte, Unit::MB};
|
||||
|
|
@ -65,14 +66,11 @@ impl Entry {
|
|||
Some(format!("{indent}{icon} {name}").into())
|
||||
}
|
||||
fn icon (&self) -> &'static str {
|
||||
if self.is_directory() {
|
||||
ICON_DIRECTORY
|
||||
} else if self.is_image() {
|
||||
ICON_IMAGE
|
||||
} else if self.is_music() {
|
||||
ICON_MUSIC
|
||||
} else {
|
||||
ICON_UNKNOWN
|
||||
match &*self.info.read().unwrap() {
|
||||
Metadata::Directory { .. } => ICON_DIRECTORY,
|
||||
Metadata::Image { .. } => ICON_IMAGE,
|
||||
Metadata::Music { .. } => ICON_MUSIC,
|
||||
_ => ICON_UNKNOWN
|
||||
}
|
||||
}
|
||||
pub fn is_modified (&self) -> bool {
|
||||
|
|
@ -201,162 +199,81 @@ macro_rules! generated_field {
|
|||
}
|
||||
}
|
||||
macro_rules! music_tag_field {
|
||||
(string: $get:ident $set:ident $del:ident $key:expr) => {
|
||||
($get:ident $set:ident $del:ident $key:expr; write = $write:expr; parse = $parse:expr;) => {
|
||||
impl Entry {
|
||||
pub fn $get (&self) -> Option<Arc<str>> {
|
||||
self.info.read().unwrap().$get()
|
||||
}
|
||||
pub fn $set (&mut self, value: &impl AsRef<str>) -> bool {
|
||||
pub fn $set (&mut self, value: Option<&str>) -> bool {
|
||||
self.info.write().unwrap().$set(value)
|
||||
}
|
||||
}
|
||||
impl Metadata {
|
||||
/// Get the metadata value if present.
|
||||
pub fn $get (&self) -> Option<Arc<str>> {
|
||||
let write = $write;
|
||||
if let Metadata::Music { original_tag, modified_tag, .. } = self {
|
||||
return modified_tag.as_ref()
|
||||
.map(|tag|tag.read().unwrap().$get().map(|t|t.into()))
|
||||
return modified_tag
|
||||
.as_ref()
|
||||
.map(|tag|tag.read().unwrap().$get().map(|x|write(x)))
|
||||
.flatten()
|
||||
.or_else(||original_tag.as_ref()
|
||||
.map(|tag|tag.$get().map(|t|t.into()))
|
||||
.or_else(||original_tag
|
||||
.as_ref()
|
||||
.map(|tag|tag.$get().map(|x|write(x)))
|
||||
.flatten())
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn $set (&mut self, value: &impl AsRef<str>) -> bool {
|
||||
let value = value.as_ref().trim();
|
||||
if let &mut Metadata::Music {
|
||||
ref original_tag,
|
||||
ref mut modified_tag,
|
||||
..
|
||||
} = self {
|
||||
match (value.len(), &original_tag, &modified_tag) {
|
||||
(0, _, Some(new_tag)) => {
|
||||
if new_tag.read().unwrap().item_count() <= 1 {
|
||||
// removing last entry removes modified_tag
|
||||
*modified_tag = None;
|
||||
} else {
|
||||
/// Set or delete the metadata value.
|
||||
/// Return whether the changes should be recounted.
|
||||
pub fn $set (&mut self, raw_value: Option<&str>) -> bool {
|
||||
let parse = $parse;
|
||||
if let &mut Metadata::Music { ref mut modified_tag, .. } = self {
|
||||
//let write = $write;
|
||||
if let Some(raw_value) = raw_value {
|
||||
// Set value
|
||||
if let Ok(parsed_value) = parse(raw_value) {
|
||||
match &modified_tag {
|
||||
Some(new_tag) => {
|
||||
// add entry to modified_tag
|
||||
new_tag.write().unwrap().$set(parsed_value);
|
||||
true
|
||||
},
|
||||
None => {
|
||||
// first entry creates modified_tag
|
||||
let mut new_tag = Tag::new(TagType::Id3v2); // FIXME other types
|
||||
new_tag.$set(parsed_value);
|
||||
*modified_tag = Some(Arc::new(RwLock::new(new_tag)));
|
||||
true
|
||||
},
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
// Delete value
|
||||
match &modified_tag {
|
||||
Some(new_tag) => {
|
||||
// remove entry from modified_tag
|
||||
new_tag.write().unwrap().$del();
|
||||
if new_tag.read().unwrap().item_count() <= 1 {
|
||||
// removing last entry removes modified_tag
|
||||
*modified_tag = None;
|
||||
}
|
||||
true
|
||||
},
|
||||
None => {
|
||||
false
|
||||
}
|
||||
return true;
|
||||
},
|
||||
(_, _, Some(new_tag)) => {
|
||||
// add entry to modified_tag
|
||||
new_tag.write().unwrap().$set(value.into());
|
||||
return true;
|
||||
},
|
||||
(0, _, None) => {
|
||||
// leave modified_tag empty
|
||||
return false;
|
||||
},
|
||||
(_, _, None) => {
|
||||
// first entry creates modified_tag
|
||||
let mut new_tag = Tag::new(TagType::Id3v2); // FIXME other types
|
||||
new_tag.$set(value.into());
|
||||
*modified_tag = Some(Arc::new(RwLock::new(new_tag)));
|
||||
return true;
|
||||
},
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
(number: $get:ident $set:ident $del:ident $key:expr) => {
|
||||
impl Entry {
|
||||
pub fn $get (&self) -> Option<Arc<str>> {
|
||||
self.info.read().unwrap().$get()
|
||||
}
|
||||
pub fn $del (&mut self) -> bool {
|
||||
self.info.write().unwrap().$del()
|
||||
}
|
||||
pub fn $set (&mut self, value: &impl AsRef<str>) -> bool {
|
||||
self.info.write().unwrap().$set(value)
|
||||
}
|
||||
}
|
||||
impl Metadata {
|
||||
pub fn $get (&self) -> Option<Arc<str>> {
|
||||
if let Metadata::Music { original_tag, modified_tag, .. } = self {
|
||||
return modified_tag.as_ref()
|
||||
.map(|tag|tag.read().unwrap().$get().map(|t|format!("{t}").into()))
|
||||
.flatten()
|
||||
.or_else(||original_tag.as_ref()
|
||||
.map(|tag|tag.$get().map(|t|format!("{t}").into()))
|
||||
.flatten())
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn $del (&mut self) -> bool {
|
||||
if let &mut Metadata::Music { ref original_tag, ref mut modified_tag, .. } = self {
|
||||
let old_value = original_tag.as_ref()
|
||||
.map(|m|m.$get().clone()).flatten();
|
||||
let new_value = modified_tag.as_ref()
|
||||
.map(|m|m.read().unwrap().$get().clone()).flatten();
|
||||
match (old_value, new_value) {
|
||||
(Some(old), Some(new)) => {
|
||||
let mut new_tag = modified_tag.as_ref().unwrap().write().unwrap();
|
||||
new_tag.$set(old);
|
||||
true
|
||||
},
|
||||
(None, Some(new)) => {
|
||||
let mut new_tag = modified_tag.as_ref().unwrap().write().unwrap();
|
||||
new_tag.$del();
|
||||
if new_tag.item_count() == 0 {
|
||||
std::mem::drop(new_tag);
|
||||
*modified_tag = None;
|
||||
}
|
||||
true
|
||||
},
|
||||
(_, None) => {
|
||||
false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
pub fn $set (&mut self, value: &impl AsRef<str>) -> bool {
|
||||
let value = value.as_ref().trim();
|
||||
if let Ok(numeric_value) = value.parse::<u32>() && let &mut Metadata::Music {
|
||||
ref original_tag,
|
||||
ref mut modified_tag,
|
||||
..
|
||||
} = self {
|
||||
match (value.len(), &original_tag, &modified_tag) {
|
||||
(0, _, Some(new_tag)) => {
|
||||
if new_tag.read().unwrap().item_count() <= 1 {
|
||||
// removing last entry removes modified_tag
|
||||
*modified_tag = None;
|
||||
} else {
|
||||
// remove entry from modified_tag
|
||||
new_tag.write().unwrap().$del();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
(_, _, Some(new_tag)) => {
|
||||
// add entry to modified_tag
|
||||
new_tag.write().unwrap().$set(numeric_value);
|
||||
return true;
|
||||
},
|
||||
(0, _, None) => {
|
||||
// leave modified_tag empty
|
||||
return false;
|
||||
},
|
||||
(_, _, None) => {
|
||||
// first entry creates modified_tag
|
||||
let mut new_tag = Tag::new(TagType::Id3v2); // FIXME other types
|
||||
new_tag.$set(numeric_value);
|
||||
*modified_tag = Some(Arc::new(RwLock::new(new_tag)));
|
||||
return true;
|
||||
},
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
generated_field!(hash = |self|match self {
|
||||
Metadata::Image { hash, .. } => Some(hash.clone()),
|
||||
Metadata::Music { hash, .. } => Some(hash.clone()),
|
||||
|
|
@ -369,8 +286,18 @@ generated_field!(size = |self|match self {
|
|||
Metadata::Unknown { size, .. } => Some(size.clone()),
|
||||
_ => None
|
||||
});
|
||||
music_tag_field!(string: artist set_artist remove_artist ItemKey::TrackArtist);
|
||||
music_tag_field!(number: year set_year remove_year ItemKey::Year);
|
||||
music_tag_field!(string: album set_album remove_album ItemKey::AlbumTitle);
|
||||
music_tag_field!(number: track set_track remove_track ItemKey::TrackNumber);
|
||||
music_tag_field!(string: title set_title remove_title ItemKey::TrackTitle);
|
||||
music_tag_field!(artist set_artist remove_artist ItemKey::TrackArtist;
|
||||
write = |value: Cow<str>|value.into();
|
||||
parse = |value: &str|Ok::<String, ()>(value.trim().into()););
|
||||
music_tag_field!(year set_year remove_year ItemKey::Year;
|
||||
write = |value: u32|format!("{value}").into();
|
||||
parse = |value: &str|value.trim().parse::<u32>(););
|
||||
music_tag_field!(album set_album remove_album ItemKey::AlbumTitle;
|
||||
write = |value: Cow<str>|value.into();
|
||||
parse = |value: &str|Ok::<String, ()>(value.trim().into()););
|
||||
music_tag_field!(track set_track remove_track ItemKey::TrackNumber;
|
||||
write = |value: u32|format!("{value}").into();
|
||||
parse = |value: &str|value.trim().parse::<u32>(););
|
||||
music_tag_field!(title set_title remove_title ItemKey::TrackTitle;
|
||||
write = |value: Cow<str>|value.into();
|
||||
parse = |value: &str|Ok::<String, ()>(value.trim().into()););
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue