unify setters and deleters

This commit is contained in:
🪞👃🪞 2025-04-07 15:18:40 +03:00
parent 25ca263cd7
commit d1889481b1
5 changed files with 86 additions and 163 deletions

View file

@ -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;
}
}

View file

@ -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 {

View file

@ -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>,
>,
}

View file

@ -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 {

View file

@ -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()););