use std::sync::Arc;
use crate::util::sha1::{
hex_decode_20, hex_encode_20, sha1_digest, sha1_fingerprint, sha1_hex_of_digest_hexes,
};
use crate::xmllinq::NodeId;
use super::{ComparisonUnitGroupType, CorrelationStatus, WmlComparerRevisionType};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct AtomHash([u8; 20]);
impl AtomHash {
pub fn of_bytes(bytes: &[u8]) -> Self {
Self(sha1_digest(bytes))
}
pub fn from_hex(s: &str) -> Self {
match hex_decode_20(s) {
Some(d) => Self(d),
None => Self::of_bytes(s.as_bytes()),
}
}
pub fn to_hex(&self) -> [u8; 40] {
hex_encode_20(&self.0)
}
pub fn to_hex_string(&self) -> String {
String::from_utf8(self.to_hex().to_vec()).expect("hex digits are ASCII")
}
pub fn as_bytes(&self) -> &[u8; 20] {
&self.0
}
}
impl std::fmt::Debug for AtomHash {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "AtomHash({})", self.to_hex_string())
}
}
impl From<&str> for AtomHash {
fn from(s: &str) -> Self {
Self::of_bytes(s.as_bytes())
}
}
impl From<String> for AtomHash {
fn from(s: String) -> Self {
Self::of_bytes(s.as_bytes())
}
}
impl From<&String> for AtomHash {
fn from(s: &String) -> Self {
Self::of_bytes(s.as_bytes())
}
}
#[derive(Clone, Debug, Default)]
pub struct FormatChangeInfo {
pub old_run_properties: Option<NodeId>,
pub new_run_properties: Option<NodeId>,
pub old_para_properties: Option<NodeId>,
pub changed_properties: Vec<String>,
}
#[derive(Clone, Debug)]
pub struct AtomBlock {
pub atoms: Vec<usize>,
pub start_index: usize,
}
#[derive(Clone, Debug)]
pub struct ComparisonUnitAtom {
pub correlation_status: CorrelationStatus,
pub sha1_hash: AtomHash,
pub content_element: NodeId,
pub ancestor_elements: Arc<[NodeId]>,
pub correlated_sha1_hash: Option<String>,
pub content_element_before: Option<NodeId>,
pub comparison_unit_atom_before: Option<std::sync::Arc<ComparisonUnitAtom>>,
pub ancestor_unids: Option<std::sync::Arc<[String]>>,
pub rev_track_element: Option<NodeId>,
pub move_group_id: Option<u32>,
pub move_name: Option<String>,
pub format_change: Option<FormatChangeInfo>,
}
impl ComparisonUnitAtom {
pub fn new(
content_element: NodeId,
ancestor_elements: impl Into<Arc<[NodeId]>>,
sha1_hash: impl Into<AtomHash>,
) -> Self {
ComparisonUnitAtom {
correlation_status: CorrelationStatus::Nil,
sha1_hash: sha1_hash.into(),
content_element,
ancestor_elements: ancestor_elements.into(),
correlated_sha1_hash: None,
content_element_before: None,
comparison_unit_atom_before: None,
ancestor_unids: None,
rev_track_element: None,
move_group_id: None,
move_name: None,
format_change: None,
}
}
}
#[derive(Clone, Debug)]
pub struct ComparisonUnitWord {
pub correlation_status: CorrelationStatus,
pub contents: Vec<ComparisonUnitAtom>,
pub sha1_hash: String,
pub sha1_key: u64,
pub sha1_key128: u128,
}
impl ComparisonUnitWord {
pub fn new(contents: Vec<ComparisonUnitAtom>) -> Self {
let sha1_hash = sha1_hex_of_digest_hexes(contents.iter().map(|a| a.sha1_hash.as_bytes()));
ComparisonUnitWord {
correlation_status: CorrelationStatus::Nil,
sha1_key: sha1_fingerprint(&sha1_hash),
sha1_key128: crate::util::sha1::sha1_fingerprint128(&sha1_hash),
sha1_hash,
contents,
}
}
}
#[derive(Clone, Debug)]
pub struct ComparisonUnitGroup {
pub correlation_status: CorrelationStatus,
pub group_type: ComparisonUnitGroupType,
pub contents: Vec<ComparisonUnit>,
pub level: usize,
pub sha1_hash: String,
pub sha1_key: u64,
pub sha1_key128: u128,
pub correlated_sha1_hash: Option<String>,
pub structure_sha1_hash: Option<String>,
pub atom_count_memo: std::cell::Cell<usize>,
}
#[derive(Clone, Debug)]
pub enum ComparisonUnit {
Word(ComparisonUnitWord),
Group(ComparisonUnitGroup),
}
impl ComparisonUnit {
pub fn sha1(&self) -> &str {
match self {
ComparisonUnit::Word(w) => &w.sha1_hash,
ComparisonUnit::Group(g) => &g.sha1_hash,
}
}
pub fn sha1_key(&self) -> u64 {
match self {
ComparisonUnit::Word(w) => w.sha1_key,
ComparisonUnit::Group(g) => g.sha1_key,
}
}
pub fn sha1_key128(&self) -> u128 {
match self {
ComparisonUnit::Word(w) => w.sha1_key128,
ComparisonUnit::Group(g) => g.sha1_key128,
}
}
pub fn correlated_sha1(&self) -> Option<&str> {
match self {
ComparisonUnit::Word(_) => None,
ComparisonUnit::Group(g) => g.correlated_sha1_hash.as_deref(),
}
}
pub fn correlation_status(&self) -> CorrelationStatus {
match self {
ComparisonUnit::Word(w) => w.correlation_status,
ComparisonUnit::Group(g) => g.correlation_status,
}
}
pub fn set_correlation_status(&mut self, s: CorrelationStatus) {
match self {
ComparisonUnit::Word(w) => w.correlation_status = s,
ComparisonUnit::Group(g) => g.correlation_status = s,
}
}
pub fn descendant_atoms(&self) -> Vec<&ComparisonUnitAtom> {
let mut out = Vec::new();
self.collect_atoms(&mut out);
out
}
pub fn descendant_content_atoms_count(&self) -> usize {
match self {
ComparisonUnit::Word(w) => w.contents.len(),
ComparisonUnit::Group(g) => {
let memo = g.atom_count_memo.get();
if memo != usize::MAX {
return memo;
}
let n = g
.contents
.iter()
.map(ComparisonUnit::descendant_content_atoms_count)
.sum();
g.atom_count_memo.set(n);
n
}
}
}
fn collect_atoms<'a>(&'a self, out: &mut Vec<&'a ComparisonUnitAtom>) {
match self {
ComparisonUnit::Word(w) => out.extend(w.contents.iter()),
ComparisonUnit::Group(g) => {
for c in &g.contents {
c.collect_atoms(out);
}
}
}
}
pub fn try_for_each_atom<'a>(
&'a self,
f: &mut impl FnMut(&'a ComparisonUnitAtom) -> bool,
) -> bool {
match self {
ComparisonUnit::Word(w) => {
for a in &w.contents {
if !f(a) {
return false;
}
}
true
}
ComparisonUnit::Group(g) => {
for c in &g.contents {
if !c.try_for_each_atom(f) {
return false;
}
}
true
}
}
}
pub fn first_atom(&self) -> Option<&ComparisonUnitAtom> {
match self {
ComparisonUnit::Word(w) => w.contents.first(),
ComparisonUnit::Group(g) => g.contents.iter().find_map(ComparisonUnit::first_atom),
}
}
pub fn last_atom(&self) -> Option<&ComparisonUnitAtom> {
match self {
ComparisonUnit::Word(w) => w.contents.last(),
ComparisonUnit::Group(g) => g.contents.iter().rev().find_map(ComparisonUnit::last_atom),
}
}
}
#[derive(Clone, Debug)]
pub struct CorrelatedSequence {
pub correlation_status: CorrelationStatus,
pub com_units_1: Option<Vec<ComparisonUnit>>,
pub com_units_2: Option<Vec<ComparisonUnit>>,
}
impl CorrelatedSequence {
pub fn paired(
status: CorrelationStatus,
a1: Vec<ComparisonUnit>,
a2: Vec<ComparisonUnit>,
) -> Self {
CorrelatedSequence {
correlation_status: status,
com_units_1: Some(a1),
com_units_2: Some(a2),
}
}
pub fn deleted(a1: Vec<ComparisonUnit>) -> Self {
CorrelatedSequence {
correlation_status: CorrelationStatus::Deleted,
com_units_1: Some(a1),
com_units_2: None,
}
}
pub fn inserted(a2: Vec<ComparisonUnit>) -> Self {
CorrelatedSequence {
correlation_status: CorrelationStatus::Inserted,
com_units_1: None,
com_units_2: Some(a2),
}
}
}
#[derive(Clone, Debug)]
pub struct WmlComparerRevision {
pub revision_type: WmlComparerRevisionType,
pub text: Option<String>,
pub author: Option<String>,
pub date: Option<String>,
pub content_element: Option<NodeId>,
pub revision_element: Option<NodeId>,
pub part_name: String,
pub move_group_id: Option<i32>,
pub is_move_source: Option<bool>,
pub format_change: Option<FormatChangeInfo>,
}