use chrono::{DateTime, Utc};
use serde_yaml;
use sha2::{Digest, Sha256};
use std::collections::HashSet;
use std::io::{self, prelude::*};
use crate::error::PatchIdError;
use crate::Error;
mod change;
pub use self::change::{Change, Changes};
struct HashingWriter<W: Write> {
writer: W,
hasher: Sha256,
}
impl<W: Write> HashingWriter<W> {
fn new(writer: W) -> HashingWriter<W> {
HashingWriter {
writer,
hasher: Default::default(),
}
}
}
impl<W: Write> Write for HashingWriter<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.hasher.input(buf);
self.writer.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.writer.flush()
}
}
struct HashingReader<R: Read> {
reader: R,
hasher: Sha256,
}
impl<R: Read> HashingReader<R> {
fn new(reader: R) -> HashingReader<R> {
HashingReader {
reader,
hasher: Default::default(),
}
}
}
impl<R: Read> Read for HashingReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let size = self.reader.read(buf)?;
self.hasher.input(&buf[..size]);
Ok(size)
}
}
mod patch_id_base64 {
pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
serializer.serialize_str(&base64::encode_config(bytes, base64::URL_SAFE))
} else {
serializer.serialize_bytes(bytes)
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error>
where
D: serde::Deserializer<'de>,
{
if deserializer.is_human_readable() {
let s = <String as serde::Deserialize>::deserialize(deserializer)?;
let mut ret = [0; 32];
let vec =
base64::decode_config(&s, base64::URL_SAFE).map_err(serde::de::Error::custom)?;
ret.copy_from_slice(&vec[..]);
Ok(ret)
} else {
<[u8; 32] as serde::Deserialize>::deserialize(deserializer)
}
}
}
#[derive(Copy, Clone, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
#[serde(transparent)]
pub struct PatchId {
#[serde(with = "patch_id_base64")]
pub(crate) data: [u8; 32],
}
impl std::fmt::Debug for PatchId {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fmt.debug_tuple("PatchId").field(&self.to_base64()).finish()
}
}
impl PatchId {
pub fn cur() -> PatchId {
PatchId { data: [0; 32] }
}
pub fn is_cur(&self) -> bool {
self.data == [0; 32]
}
pub fn to_base64(&self) -> String {
let mut ret = vec![0; 45];
ret[0] = b'P';
base64::encode_config_slice(&self.data[..], base64::URL_SAFE, &mut ret[1..]);
String::from_utf8(ret).unwrap()
}
pub fn from_base64<S: ?Sized + AsRef<[u8]>>(name: &S) -> Result<PatchId, Error> {
let data = base64::decode_config(&name.as_ref()[1..], base64::URL_SAFE)
.map_err(PatchIdError::from)?;
let mut ret = PatchId::cur();
if data.len() != ret.data.len() {
Err(PatchIdError::InvalidLength(data.len()).into())
} else {
ret.data.copy_from_slice(&data);
Ok(ret)
}
}
fn from_sha256(hasher: Sha256) -> PatchId {
let mut ret = PatchId::cur();
ret.data.copy_from_slice(&hasher.result()[..]);
ret
}
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct UnidentifiedPatch {
changes: Changes,
header: PatchHeader,
deps: Vec<PatchId>,
}
impl UnidentifiedPatch {
pub fn new(author: String, description: String, changes: Changes) -> UnidentifiedPatch {
let mut deps = HashSet::new();
for c in &changes.changes {
match *c {
Change::DeleteNode { ref id } => {
if !id.patch.is_cur() {
deps.insert(id.patch);
}
}
Change::NewEdge { ref src, ref dest } => {
if !src.patch.is_cur() {
deps.insert(src.patch);
}
if !dest.patch.is_cur() {
deps.insert(dest.patch);
}
}
_ => {}
}
}
UnidentifiedPatch {
header: PatchHeader {
author,
description,
#[cfg(not(target_arch = "wasm32"))]
timestamp: Utc::now(),
},
changes,
deps: deps.into_iter().collect(),
}
}
fn set_id(self, id: PatchId) -> Patch {
let mut ret = Patch {
id,
header: self.header,
changes: self.changes,
deps: self.deps,
};
ret.changes.set_patch_id(&ret.id);
ret
}
pub fn write_out<W: Write>(self, writer: W) -> Result<Patch, serde_yaml::Error> {
let mut w = HashingWriter::new(writer);
serde_yaml::to_writer(&mut w, &self)?;
let patch_id = PatchId::from_sha256(w.hasher);
Ok(self.set_id(patch_id))
}
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq)]
pub struct Patch {
id: PatchId,
header: PatchHeader,
changes: Changes,
deps: Vec<PatchId>,
}
impl Patch {
pub fn from_reader<R: Read>(input: R) -> Result<Patch, Error> {
let mut reader = HashingReader::new(input);
let up: UnidentifiedPatch = serde_yaml::from_reader(&mut reader)?;
let id = PatchId::from_sha256(reader.hasher);
Ok(up.set_id(id))
}
pub fn id(&self) -> &PatchId {
&self.id
}
pub fn header(&self) -> &PatchHeader {
&self.header
}
pub fn changes(&self) -> &Changes {
&self.changes
}
pub fn deps(&self) -> &[PatchId] {
&self.deps
}
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct PatchHeader {
pub author: String,
pub description: String,
#[cfg(not(target_arch = "wasm32"))]
pub timestamp: DateTime<Utc>,
}