wip: 3-way merge logic

This commit is contained in:
🪞👃🪞 2025-04-08 02:36:28 +03:00
parent d1889481b1
commit 44315dcc72

View file

@ -1,6 +1,7 @@
use crate::*; use crate::*;
use std::fs::File; use std::fs::File;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashSet;
use std::io::{BufReader, Read}; use std::io::{BufReader, Read};
use std::cmp::{Eq, PartialEq, Ord, PartialOrd, Ordering}; use std::cmp::{Eq, PartialEq, Ord, PartialOrd, Ordering};
use byte_unit::{Byte, Unit::MB}; use byte_unit::{Byte, Unit::MB};
@ -75,7 +76,17 @@ impl Entry {
} }
pub fn is_modified (&self) -> bool { pub fn is_modified (&self) -> bool {
match &*self.info.read().unwrap() { match &*self.info.read().unwrap() {
Metadata::Music { modified_tag, .. } => modified_tag.is_some(), Metadata::Music { original_tag, modified_tag, .. } => match (
original_tag,
modified_tag,
) {
(Some(original), Some(modified)) => {
let original_items: HashSet<_> = original.items().collect();
original_items != modified.read().unwrap().items().collect()
},
(None, None) => false,
_ => true,
},
_ => false, _ => false,
} }
} }
@ -91,24 +102,26 @@ impl PartialOrd for Entry {
pub enum Metadata { pub enum Metadata {
Directory { Directory {
hash_file: Option<()>, hash_file: Option<()>,
catalog_file: Option<()>, catalog_file: Option<()>,
artist_file: Option<()>, artist_file: Option<()>,
release_file: Option<()>, release_file: Option<()>,
}, },
Music { Music {
invalid: bool, invalid: bool,
hash: Arc<str>, hash: Arc<str>,
size: Arc<str>, size: Arc<str>,
/// The tag that was read from the file
original_tag: Option<Arc<Tag>>, original_tag: Option<Arc<Tag>>,
/// The tag that will be written to the file
modified_tag: Option<Arc<RwLock<Tag>>>, modified_tag: Option<Arc<RwLock<Tag>>>,
}, },
Image { Image {
invalid: bool, invalid: bool,
hash: Arc<str>, hash: Arc<str>,
size: Arc<str>, size: Arc<str>,
title: Option<String>, title: Option<String>,
author: Option<String>, author: Option<String>,
}, },
Unknown { Unknown {
hash: Arc<str>, hash: Arc<str>,
@ -136,7 +149,7 @@ impl Metadata {
size: format!("{:#>8.2}", size).into(), size: format!("{:#>8.2}", size).into(),
invalid: false, invalid: false,
original_tag: tag.map(|t|t.clone().into()), original_tag: tag.map(|t|t.clone().into()),
modified_tag: None, modified_tag: tag.map(|t|Arc::new(t.clone().into())),
}) })
} else { } else {
Self::new_fallback(path) Self::new_fallback(path)
@ -226,49 +239,88 @@ macro_rules! music_tag_field {
} }
/// Set or delete the metadata value. /// Set or delete the metadata value.
/// Return whether the changes should be recounted. /// Return whether the changes should be recounted.
pub fn $set (&mut self, raw_value: Option<&str>) -> bool { pub fn $set (&mut self, value: Option<&str>) -> bool {
let parse = $parse; let parse = $parse;
if let &mut Metadata::Music { ref mut modified_tag, .. } = self { match self {
//let write = $write;
if let Some(raw_value) = raw_value { &mut Metadata::Music {
// Set value original_tag: Some(ref original), modified_tag: Some(ref modified), ..
if let Ok(parsed_value) = parse(raw_value) { } => if let Some(value) = value {
match &modified_tag { // set new value
Some(new_tag) => { let value = parse(value).unwrap();
// add entry to modified_tag let changed = original.$get() != Some(value);
new_tag.write().unwrap().$set(parsed_value); modified.write().unwrap().$set(value);
true changed
}, } else {
None => { let modified = modified.write().unwrap();
// first entry creates modified_tag if let Some(value) = modified.$get() {
let mut new_tag = Tag::new(TagType::Id3v2); // FIXME other types // delete modified value
new_tag.$set(parsed_value); modified.$del();
*modified_tag = Some(Arc::new(RwLock::new(new_tag))); true
true } else if let Some(value) = original.$get() {
}, // set to original value
} modified.$set(value);
true
} else {
false
}
},
&mut Metadata::Music {
original_tag: Some(ref original), ref mut modified_tag, ..
} => if let Some(input_value) = value {
let mut new_tag = Tag::new(TagType::Id3v2); // FIXME other types
new_tag.$set(input_value);
*modified_tag = Some(Arc::new(RwLock::new(new_tag)));
true
} else if let Some(original_value) = original.$get() {
// set to original value
let mut new_tag = Tag::new(TagType::Id3v2); // FIXME other types
new_tag.$set(original_value);
*modified_tag = Some(Arc::new(RwLock::new(new_tag)));
true
} else {
false
},
&mut Metadata::Music {
original_tag: None, modified_tag: Some(ref modified), ..
} => if let Some(value) = value {
// set new value
let value = parse(value).unwrap();
let modified = modified.write().unwrap();
if modified.$get() != Some(value) {
// set modified value
modified.$set(value);
true
} else { } else {
false false
} }
} else { } else {
// Delete value let modified = modified.write().unwrap();
match &modified_tag { if modified.$get().is_some() {
Some(new_tag) => { // remove modified value
// remove entry from modified_tag modified.$del();
new_tag.write().unwrap().$del(); true
if new_tag.read().unwrap().item_count() <= 1 { } else {
// removing last entry removes modified_tag false
*modified_tag = None;
}
true
},
None => {
false
}
} }
},
&mut Metadata::Music {
original_tag: None, ref mut modified_tag, ..
} => if let Some(value) = value {
let mut new_tag = Tag::new(TagType::Id3v2); // FIXME other types
new_tag.$set(value);
*modified_tag = Some(Arc::new(RwLock::new(new_tag)));
true
} else {
false
},
_ => {
false
} }
} else {
false
} }
} }
} }
@ -288,7 +340,7 @@ generated_field!(size = |self|match self {
}); });
music_tag_field!(artist set_artist remove_artist ItemKey::TrackArtist; music_tag_field!(artist set_artist remove_artist ItemKey::TrackArtist;
write = |value: Cow<str>|value.into(); write = |value: Cow<str>|value.into();
parse = |value: &str|Ok::<String, ()>(value.trim().into());); parse = |value: &str|Ok::<String, ()>(value.trim()););
music_tag_field!(year set_year remove_year ItemKey::Year; music_tag_field!(year set_year remove_year ItemKey::Year;
write = |value: u32|format!("{value}").into(); write = |value: u32|format!("{value}").into();
parse = |value: &str|value.trim().parse::<u32>();); parse = |value: &str|value.trim().parse::<u32>(););