use super::{Atom, Change, Hashed, Local};
#[cfg(feature = "zstd")]
use super::{ChangeError, LocalChange, Offsets};
#[cfg(feature = "zstd")]
use crate::pristine::Hasher;
use crate::Hash;
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub enum Hunk<Hash, Local> {
FileMove {
del: Atom<Hash>,
add: Atom<Hash>,
path: String,
},
FileDel {
del: Atom<Hash>,
contents: Option<Atom<Hash>>,
path: String,
},
FileUndel {
undel: Atom<Hash>,
contents: Option<Atom<Hash>>,
path: String,
},
FileAdd {
add_name: Atom<Hash>,
add_inode: Atom<Hash>,
contents: Option<Atom<Hash>>,
path: String,
},
SolveNameConflict {
name: Atom<Hash>,
path: String,
},
UnsolveNameConflict {
name: Atom<Hash>,
path: String,
},
Edit {
change: Atom<Hash>,
local: Local,
},
Replacement {
change: Atom<Hash>,
replacement: Atom<Hash>,
local: Local,
},
SolveOrderConflict {
change: Atom<Hash>,
local: Local,
},
UnsolveOrderConflict {
change: Atom<Hash>,
local: Local,
},
ResurrectZombies {
change: Atom<Hash>,
local: Local,
},
}
impl<H, L> From<Hunk<H, L>> for super::Hunk<H, L> {
fn from(h: Hunk<H, L>) -> Self {
let encoding = Some(crate::text_encoding::Encoding(encoding_rs::UTF_8));
match h {
Hunk::FileMove { del, add, path } => super::Hunk::FileMove { del, add, path },
Hunk::FileDel {
del,
contents,
path,
} => super::Hunk::FileDel {
del,
contents,
path,
encoding,
},
Hunk::FileUndel {
undel,
contents,
path,
} => super::Hunk::FileUndel {
undel,
contents,
path,
encoding,
},
Hunk::FileAdd {
add_name,
add_inode,
contents,
path,
} => super::Hunk::FileAdd {
add_name,
add_inode,
contents,
path,
encoding,
},
Hunk::SolveNameConflict { name, path } => super::Hunk::SolveNameConflict { name, path },
Hunk::UnsolveNameConflict { name, path } => {
super::Hunk::UnsolveNameConflict { name, path }
}
Hunk::Edit { change, local } => super::Hunk::Edit {
change,
local,
encoding,
},
Hunk::Replacement {
change,
replacement,
local,
} => super::Hunk::Replacement {
change,
replacement,
local,
encoding,
},
Hunk::SolveOrderConflict { change, local } => {
super::Hunk::SolveOrderConflict { change, local }
}
Hunk::UnsolveOrderConflict { change, local } => {
super::Hunk::UnsolveOrderConflict { change, local }
}
Hunk::ResurrectZombies { change, local } => super::Hunk::ResurrectZombies {
change,
local,
encoding,
},
}
}
}
impl Change {
#[cfg(feature = "zstd")]
pub(super) fn deserialize_noenc(
offsets: Offsets,
mut r: std::fs::File,
hash: Option<&Hash>,
) -> Result<Self, ChangeError> {
use std::io::Read;
let mut buf = vec![0u8; (offsets.unhashed_off - Self::OFFSETS_SIZE) as usize];
r.read_exact(&mut buf)?;
let hashed: Hashed<Hunk<Option<Hash>, Local>, Author> = {
let mut s = zstd_seekable::Seekable::init_buf(&buf[..])?;
let mut out = vec![0u8; offsets.hashed_len as usize];
s.decompress(&mut out[..], 0)?;
let mut hasher = Hasher::default();
hasher.update(&out);
let computed_hash = hasher.finish();
if let Some(hash) = hash {
if &computed_hash != hash {
return Err(super::ChangeError::ChangeHashMismatch {
claimed: *hash,
computed: computed_hash,
});
}
}
bincode::deserialize_from(&out[..])?
};
buf.clear();
buf.resize((offsets.contents_off - offsets.unhashed_off) as usize, 0);
let unhashed = if buf.is_empty() {
None
} else {
r.read_exact(&mut buf)?;
let mut s = zstd_seekable::Seekable::init_buf(&buf[..])?;
let mut out = vec![0u8; offsets.unhashed_len as usize];
s.decompress(&mut out[..], 0)?;
serde_json::from_slice(&out).ok()
};
trace!("unhashed = {:?}", unhashed);
buf.clear();
buf.resize((offsets.total - offsets.contents_off) as usize, 0);
let contents = if r.read_exact(&mut buf).is_ok() {
let mut s = zstd_seekable::Seekable::init_buf(&buf[..])?;
let mut contents = vec![0u8; offsets.contents_len as usize];
s.decompress(&mut contents[..], 0)?;
contents
} else {
Vec::new()
};
trace!("contents = {:?}", contents);
Ok(LocalChange {
offsets,
hashed: hashed.into(),
unhashed,
contents,
})
}
}
impl From<Hashed<Hunk<Option<Hash>, Local>, Author>>
for Hashed<super::Hunk<Option<Hash>, Local>, super::Author>
{
fn from(hashed: Hashed<Hunk<Option<Hash>, Local>, Author>) -> Self {
Hashed {
contents_hash: hashed.contents_hash,
dependencies: hashed.dependencies,
extra_known: hashed.extra_known,
header: hashed.header.into(),
metadata: hashed.metadata,
version: hashed.version,
changes: hashed.changes.into_iter().map(|x| x.into()).collect(),
}
}
}
use super::ChangeHeader_;
impl From<ChangeHeader_<Author>> for ChangeHeader_<super::Author> {
fn from(c: ChangeHeader_<Author>) -> Self {
ChangeHeader_ {
message: c.message,
description: c.description,
timestamp: c.timestamp,
authors: c.authors.into_iter().map(|x| x.into()).collect(),
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
pub struct Author {
pub name: String,
#[serde(default)]
pub full_name: Option<String>,
#[serde(default)]
pub email: Option<String>,
}
impl From<String> for Author {
fn from(name: String) -> Author {
Author {
name,
..Author::default()
}
}
}
impl From<Author> for super::Author {
fn from(c: Author) -> Self {
let mut b = std::collections::BTreeMap::new();
b.insert("name".to_string(), c.name);
if let Some(n) = c.full_name {
b.insert("full_name".to_string(), n);
}
if let Some(n) = c.email {
b.insert("email".to_string(), n);
}
super::Author(b)
}
}