Implements a structured output option for pijul change
. Most of the Serialize
implementations are on newtyped structures which include extra context and state and leaves the base/derived instances of Serialize
untouched. Struct/variant/field names are implemented using snakecase convention across the board
HCJ7BOUVOEFS6X7OTHI6JZR2ASFIGEPQCTL5K3JTSBI7Y7M7W3KQC
JG3MWHENOL4DM7DZU7CZA5UJQZDRRDEJTNZU7A74C4H3N6YNTJDAC
RZ2FSIQ2ZOTTORLX432W7GWOTHQQ544ARFYI7NPNXSCWIK2RO4LAC
3QGE6HRDSMTV2SNEDC2AJUIR6GIHRU73KXQMZ3BGUYT4H4TAMETQC
SXEYMYF7P4RZMZ46WPL4IZUTSQ2ATBWYZX7QNVMS3SGOYXYOHAGQC
Q4SVMHAEZQQCBFGPJMWS5H4VXB2HFQREZ3AWGOHFWHLFARUQVPBAC
5BB266P6HPUGYEVR7QNNOA62EFPYPUYJ3UMLE5J3LLYMSUWXANIQC
ZRUPLBBTT4S6S7A3LOAHG4ONYEGPA5CFO4L2XBCNFKK45MWX3BDAC
IIV3EL2XYI2X7HZWKXEXQFAE3R3KC2Q7SGOT3Q332HSENMYVF32QC
6YMDOZIB5LVYLFIDGN2WNT5JTHEAMS4TFPVDEZ3OWXWOKJOC5QDAC
I24UEJQLCH2SOXA4UHIYWTRDCHSOPU7AFTRUOTX7HZIAV4AZKYEQC
VO5OQW4W2656DIYYRNZ3PO7TQ4JOKQ3GVWE5ALUTYVMX3WMXJOYQC
A3RM526Y7LUXNYW4TL56YKQ5GVOK2R5D7JJVTSQ6TT5MEXIR6YAAC
FXEDPLRI7PXLDXV634ZA6D5Q3ZWG3ESTKJTMRPJ4MAHI7PKU3M6AC
XR7MNOMU5PMOOEY2EPPUABZ7NOP432RDCWUET23ONPXTT3JQIFIAC
VMOYG7MKEWTUEEY2EOL256RWCVPGRD63IFOSKXHBGJ6VSRITLMOAC
CCFJ7VO3I73FE3MZRS5RSDRYNZVW7AXC345P4BXS7JIL2TU3LQJQC
let colors = super::diff::is_colored();
change.write(
&changes,
Some(hash),
true,
super::diff::Colored {
w: termcolor::StandardStream::stdout(termcolor::ColorChoice::Auto),
colors,
},
)?;
match self.output_format {
Some(s) if s.eq_ignore_ascii_case("json") => {
let pretty = libpijul::change::PrettyLocalChange {
local_change: &change,
change_store: &changes,
hash: Some(hash),
write_header: true,
};
serde_json::to_writer_pretty(&mut std::io::stdout(), &pretty)?;
}
Some(s) => {
anyhow::bail!("unsupported output format {}", s)
}
None => {
let colors = super::diff::is_colored();
change.write(
&changes,
Some(hash),
true,
super::diff::Colored {
w: termcolor::StandardStream::stdout(termcolor::ColorChoice::Auto),
colors,
},
)?;
}
}
}
pub(crate) struct PrettyPosition<'a, P> {
pub(crate) position: &'a Position<P>,
pub(crate) hashes: &'a HashMap<Hash, i32>,
}
impl<'a> serde::Serialize for PrettyPosition<'a, Option<Hash>> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
use serde::ser::SerializeStruct;
let mut st = serializer.serialize_struct("position", 2)?;
let change = if let Some(Hash::None) = self.position.change {
1
} else if let Some(ref c) = self.position.change {
*self.hashes.get(c).unwrap()
} else {
0
};
st.serialize_field("change", &change)?;
st.serialize_field("pos", &self.position.pos.0)?;
st.end()
}
impl<'a> std::fmt::Display for PrettyPosition<'a, Option<Hash>> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let change = if let Some(Hash::None) = self.position.change {
1
} else if let Some(ref c) = self.position.change {
*self.hashes.get(c).unwrap()
} else {
0
};
write!(f, "{}.{}", change, self.position.pos.0)
}
}
}
}
#[repr(transparent)]
pub(crate) struct PrettyEdgeFlags(pub(crate) EdgeFlags);
use serde::ser::Serializer;
use serde::Serialize;
impl std::fmt::Display for PrettyEdgeFlags {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if self.0.contains(EdgeFlags::BLOCK) {
write!(f, "B")?;
}
if self.0.contains(EdgeFlags::FOLDER) {
write!(f, "F")?;
}
if self.0.contains(EdgeFlags::DELETED) {
write!(f, "D")?;
}
assert!(!self.0.contains(EdgeFlags::PARENT));
assert!(!self.0.contains(EdgeFlags::PSEUDO));
Ok(())
}
}
impl Serialize for PrettyEdgeFlags {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(&format!("{}", self))
}
/// Newtype for `Atom` which includes context/state needed for printing.
/// Implements `Serialize` such that the output is fit for external consumers.
pub(crate) struct PrettyAtom<'a, C: crate::changestore::ChangeStore> {
atom: &'a Atom<Option<Hash>>,
change_store: &'a C,
change_contents: &'a [u8],
hashes: &'a HashMap<Hash, i32>,
encoding: &'a Option<Encoding>,
}
impl<'a, C: crate::changestore::ChangeStore> serde::Serialize for PrettyAtom<'a, C> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
use serde::ser::SerializeStructVariant;
match &self.atom {
Atom::NewVertex(ref n) => {
let mut st = serializer.serialize_struct_variant("atom", 0, "new_vertex", 6)?;
st.serialize_field(
"inode",
&PrettyPosition {
position: &n.inode,
hashes: self.hashes,
},
)?;
st.serialize_field(
"up",
&n.up_context
.iter()
.map(|position| PrettyPosition {
position,
hashes: self.hashes,
})
.collect::<Vec<_>>(),
)?;
st.serialize_field("start", &n.start.0)?;
st.serialize_field("end", &n.end.0)?;
st.serialize_field(
"down",
&n.down_context
.iter()
.map(|position| PrettyPosition {
position,
hashes: self.hashes,
})
.collect::<Vec<_>>(),
)?;
st.serialize_field(
"contents",
&PrettyContents {
change_store: self.change_store,
atom: self.atom,
change_contents: self.change_contents,
encoding: self.encoding,
},
)?;
st.end()
}
Atom::EdgeMap(ref n) => {
let mut st = serializer.serialize_struct_variant("atom", 1, "edge_map", 3)?;
st.serialize_field(
"inode",
&PrettyPosition {
position: &n.inode,
hashes: self.hashes,
},
)?;
st.serialize_field(
"edges",
&n.edges
.iter()
.map(|new_edge| PrettyNewEdge {
new_edge,
hashes: self.hashes,
})
.collect::<Vec<_>>(),
)?;
st.serialize_field(
"contents",
&PrettyContents {
change_store: self.change_store,
atom: self.atom,
change_contents: self.change_contents,
encoding: self.encoding,
},
)?;
st.end()
}
}
}
}
/// Newtype for `Atom` which includes context/state needed for printing.
/// Implements `Serialize` such that the output is fit for external consumers.
///
/// This struct is used in very particular spots when rendering `Hunk` items.
pub(crate) struct PrettyAtomNames<'a, C: crate::changestore::ChangeStore> {
atom: &'a Atom<Option<Hash>>,
change_store: &'a C,
}
impl<'a, C: crate::changestore::ChangeStore> serde::Serialize for PrettyAtomNames<'a, C> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
use serde::ser::SerializeSeq;
let mut st = serializer.serialize_seq(None)?;
if let Atom::EdgeMap(ref e) = self.atom {
let mut buf = Vec::new();
for d in e.edges.iter() {
buf.clear();
self.change_store
.get_contents_ext(d.to, &mut buf)
.map_err(|c| {
<S::Error as serde::ser::Error>::custom(TextSerError::<C::Error>::C(c))
})?;
if !buf.is_empty() {
let crate::changestore::FileMetadata { basename: name, .. } =
crate::changestore::FileMetadata::read(&buf);
st.serialize_element(name)?;
}
}
}
st.end()
}
}
pub fn print_contents_aux(contents: &[u8], encoding: &Option<Encoding>) -> String {
if let Some(encoding) = encoding {
let dec = encoding.decode(&contents);
let dec = if dec.ends_with("\n") {
&dec[..dec.len() - 1]
} else {
&dec
};
dec.to_string()
} else {
format!("b{}", data_encoding::BASE64.encode(contents))
}
}
/// Newtype for `Atom` which includes context/state needed for printing,
/// meant specifically for rendering the change contents.
/// Implements `Serialize` such that the output is fit for external consumers.
pub(crate) struct PrettyContents<'a, C: crate::changestore::ChangeStore> {
atom: &'a Atom<Option<Hash>>,
change_store: &'a C,
change_contents: &'a [u8],
encoding: &'a Option<Encoding>,
impl<'a, C: crate::changestore::ChangeStore> serde::Serialize for PrettyContents<'a, C> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
use serde::ser::SerializeStruct;
match self.atom {
Atom::NewVertex(ref n) => {
let mut st = serializer.serialize_struct("addition", 1)?;
let c = &self.change_contents[n.start.us()..n.end.us()];
let s = print_contents_aux(c, &self.encoding);
st.serialize_field("addition", &s)?;
st.end()
}
Atom::EdgeMap(ref n) if n.edges.is_empty() => Err(
<S::Error as serde::ser::Error>::custom(TextSerError::<C::Error>::InvalidChange),
),
Atom::EdgeMap(ref n) if n.edges[0].flag.contains(EdgeFlags::DELETED) => {
let mut st = serializer.serialize_struct("deletion", 1)?;
let mut buf = Vec::new();
let mut current = None;
let mut deletions = Vec::new();
for e in n.edges.iter() {
if Some(e.to) == current {
continue;
}
buf.clear();
self.change_store
.get_contents_ext(e.to, &mut buf)
.map_err(|c| {
<S::Error as serde::ser::Error>::custom(TextSerError::<C::Error>::C(c))
})?;
deletions.push(print_contents_aux(&buf[..], &self.encoding));
current = Some(e.to)
}
st.serialize_field("deletions", &deletions)?;
st.end()
}
_ => serializer.serialize_none(),
}
}
}
}
/// Newtype for `NewEdge` which includes context/state needed for printing.
/// Implements `Serialize` such that the output is fit for external consumers.
pub(crate) struct PrettyNewEdge<'a> {
pub(crate) new_edge: &'a NewEdge<Option<Hash>>,
pub(crate) hashes: &'a HashMap<Hash, i32>,
impl<'a> serde::Serialize for PrettyNewEdge<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
use serde::ser::SerializeStruct;
let h = if let Some(h) = self
.hashes
.get(self.new_edge.introduced_by.as_ref().unwrap())
{
h
} else {
panic!(
"introduced_by = {:?}, not found",
self.new_edge.introduced_by
);
};
let mut st = serializer.serialize_struct("new_edge", 5)?;
st.serialize_field("previous", &PrettyEdgeFlags(self.new_edge.previous))?;
st.serialize_field("flag", &PrettyEdgeFlags(self.new_edge.flag))?;
st.serialize_field(
"from",
&(PrettyPosition {
position: &self.new_edge.from,
hashes: self.hashes,
}),
)?;
st.serialize_field(
"to",
&(PrettyPosition {
position: &self.new_edge.to.start_pos(),
hashes: self.hashes,
}),
)?;
st.serialize_field("end", &(self.new_edge.to.end.0, h))?;
st.end()
}
}
/// Newtype for `LocalChange` which includes context/state needed for printing.
/// Implements `Serialize` such that the output is fit for external consumers.
pub struct PrettyLocalChange<'a, C: crate::changestore::ChangeStore> {
pub local_change: &'a LocalChange<Hunk<Option<Hash>, Local>, Author>,
pub change_store: &'a C,
pub hash: Option<Hash>,
pub write_header: bool,
}
impl<'a, C: ChangeStore> Serialize for PrettyLocalChange<'a, C> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut st = serializer.serialize_struct("local_change", 5)?;
if let Some(h) = self.hash {
let mut hasher = Hasher::default();
hasher.update(&self.local_change.contents);
let hash = hasher.finish();
if hash != self.local_change.contents_hash {
return Err(<S::Error as serde::ser::Error>::custom(TextSerError::<
C::Error,
>::MissingContents {
h,
}));
}
}
let header = if self.write_header {
Some(&self.local_change.header)
} else {
None
};
let mut hashes = HashMap::default();
let mut i = 2;
let mut dependencies = Vec::new();
if !self.local_change.dependencies.is_empty() {
for dep in self.local_change.dependencies.iter() {
hashes.insert(*dep, i);
dependencies.push(dep.to_base32());
i += 1;
}
}
let mut transitive_dependencies = Vec::new();
self.local_change
.write_all_deps(|change| {
if let Entry::Vacant(e) = hashes.entry(change) {
e.insert(i);
transitive_dependencies.push(change.to_base32());
i += 1;
}
Ok(())
})
.map_err(|e| <S::Error as serde::ser::Error>::custom(e))?;
let mut extra_known_changes = Vec::new();
if !self.local_change.extra_known.is_empty() {
for dep in self.local_change.extra_known.iter() {
extra_known_changes.push(dep.to_base32());
i += 1;
}
}
let phunks = PrettyHunkIter {
hunks: self.local_change.changes.as_slice(),
change_store: self.change_store,
hashes: &hashes,
change_contents: &self.local_change.contents,
};
st.serialize_field("header", &header)?;
st.serialize_field("dependencies", &dependencies)?;
st.serialize_field("transitive_dependencies", &transitive_dependencies)?;
st.serialize_field("extra_known_changes", &extra_known_changes)?;
st.serialize_field("hunks", &phunks)?;
st.end()
}
}
/// Iterator implementing serialize for a sequence of hunks. Since we're
/// working with serde's `to_writer` method(s), we want to be able to stream
/// the output of this iterator, which necessitates the creation of a hand-rolled
/// iterator.
pub struct PrettyHunkIter<'a, C: crate::changestore::ChangeStore> {
pub hunks: &'a [Hunk<Option<Hash>, Local>],
pub change_store: &'a C,
change_contents: &'a [u8],
hashes: &'a HashMap<Hash, i32>,
}
impl<'a, C: ChangeStore> Serialize for PrettyHunkIter<'a, C> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
use serde::ser::SerializeSeq;
let mut seq = serializer.serialize_seq(Some(self.hunks.len()))?;
for hunk in self.hunks {
let phunk = PrettyHunk {
hunk,
change_contents: self.change_contents,
change_store: self.change_store,
hashes: self.hashes,
};
seq.serialize_element(&phunk)?;
}
seq.end()
}
}
/// Newtype around `Hunk` with extra context needed for printing.
/// Implements `Serialize` such that the output is fit for external consumers.
pub struct PrettyHunk<'a, C: ChangeStore> {
hunk: &'a Hunk<Option<Hash>, Local>,
change_contents: &'a [u8],
change_store: &'a C,
hashes: &'a HashMap<Hash, i32>,
}
impl<'a, C: ChangeStore> Serialize for PrettyHunk<'a, C> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
use self::text_changes::*;
match &self.hunk {
Hunk::FileMove { del, add, path } => match add {
Atom::NewVertex(ref inner_add) => {
let FileMetadata {
basename: name,
metadata: perms,
..
} = FileMetadata::read(
&self.change_contents[inner_add.start.0.into()..inner_add.end.0.into()],
);
let metadata = if perms.0 & 0o1000 == 0o1000 {
vec!["d", "x"]
} else if perms.0 & 0o100 == 0o100 {
vec!["x"]
} else {
vec![]
};
let mut st = serializer.serialize_struct_variant("hunk", 0, "file_move", 5)?;
st.serialize_field("path", path)?;
st.serialize_field("name", name)?;
st.serialize_field("metadata", &metadata)?;
st.serialize_field(
"del",
&PrettyAtom {
atom: del,
change_store: self.change_store,
change_contents: self.change_contents,
hashes: self.hashes,
encoding: &None,
},
)?;
st.serialize_field(
"add",
&PrettyAtom {
atom: add,
change_store: self.change_store,
change_contents: self.change_contents,
encoding: &None,
hashes: self.hashes,
},
)?;
st.end()
}
Atom::EdgeMap(_) => {
let mut st = serializer.serialize_struct_variant("hunk", 1, "file_move", 3)?;
st.serialize_field("path", path)?;
st.serialize_field(
"add",
&PrettyAtom {
atom: add,
change_store: self.change_store,
change_contents: self.change_contents,
encoding: &None,
hashes: self.hashes,
},
)?;
st.serialize_field(
"del",
&PrettyAtom {
atom: del,
change_store: self.change_store,
change_contents: self.change_contents,
encoding: &None,
hashes: self.hashes,
},
)?;
st.end()
}
},
Hunk::FileDel {
del,
contents,
path,
encoding,
} => {
let mut st = serializer.serialize_struct_variant("hunk", 2, "file_del", 4)?;
st.serialize_field("path", &path)?;
st.serialize_field("encoding", encoding_label(encoding))?;
st.serialize_field(
"del",
&PrettyAtom {
atom: del,
hashes: self.hashes,
change_store: self.change_store,
change_contents: self.change_contents,
encoding,
},
)?;
st.serialize_field(
"contents",
&contents.as_ref().map(|contents_atom| PrettyAtom {
atom: contents_atom,
change_store: self.change_store,
change_contents: self.change_contents,
hashes: self.hashes,
encoding,
}),
)?;
st.end()
}
Hunk::FileUndel {
undel,
contents,
path,
encoding,
} => {
let mut st = serializer.serialize_struct_variant("hunk", 3, "file_undel", 4)?;
st.serialize_field("path", &path)?;
st.serialize_field("encoding", encoding_label(encoding))?;
st.serialize_field(
"undel",
&PrettyAtom {
atom: undel,
change_store: self.change_store,
change_contents: self.change_contents,
encoding,
hashes: self.hashes,
},
)?;
st.serialize_field(
"contents",
&contents.as_ref().map(|contents_atom| PrettyAtom {
atom: contents_atom,
hashes: self.hashes,
change_store: self.change_store,
change_contents: self.change_contents,
encoding,
}),
)?;
st.end()
}
Hunk::FileAdd {
add_name,
contents,
path,
encoding,
..
} => {
let mut st = serializer.serialize_struct_variant("hunk", 4, "file_add", 8)?;
if let Atom::NewVertex(ref n) = add_name {
debug!("add_name {:?}", n);
let FileMetadata {
basename: name,
metadata: perms,
..
} = FileMetadata::read(&self.change_contents[n.start.0.into()..n.end.0.into()]);
let parent = if let Some(p) = crate::path::parent(&path) {
if p.is_empty() {
"/"
} else {
p
}
} else {
"/"
};
let permissions = if perms.0 & 0o1000 == 0o1000 {
vec!["d", "x"]
} else if perms.0 & 0o100 == 0o100 {
vec!["x"]
} else {
vec![]
};
st.serialize_field("name", name)?;
st.serialize_field("parent", parent)?;
st.serialize_field("permissions", &permissions)?;
st.serialize_field("encoding", encoding_label(encoding))?;
st.serialize_field("start", &n.start.0)?;
st.serialize_field("end", &n.end.0)?;
st.serialize_field(
"up_context",
&n.up_context
.iter()
.map(|position| PrettyPosition {
position,
hashes: self.hashes,
})
.collect::<Vec<_>>(),
)?;
assert!(n.down_context.is_empty());
} else {
st.serialize_field("name", &None::<&str>)?;
st.serialize_field("parent", &None::<&str>)?;
st.serialize_field("permissions", &None::<Vec<&str>>)?;
st.serialize_field("encoding", &None::<Encoding>)?;
st.serialize_field("start", &None::<L64>)?;
st.serialize_field("end", &None::<L64>)?;
st.serialize_field("up_context", &None::<PrettyPosition<'_, _>>)?;
}
st.serialize_field(
"change",
&contents.as_ref().map(|contents_atom| PrettyAtom {
change_store: self.change_store,
atom: contents_atom,
change_contents: self.change_contents,
hashes: self.hashes,
encoding,
}),
)?;
st.end()
}
Hunk::Edit {
change,
local,
encoding,
} => {
debug!("edit");
let mut st = serializer.serialize_struct_variant("hunk", 5, "edit", 4)?;
st.serialize_field("path", &local.path)?;
st.serialize_field("line", &local.line)?;
st.serialize_field("encoding", encoding_label(encoding))?;
st.serialize_field(
"change",
&PrettyAtom {
change_store: self.change_store,
atom: change,
change_contents: self.change_contents,
encoding,
hashes: self.hashes,
},
)?;
st.end()
}
Hunk::Replacement {
change,
replacement,
local,
encoding,
} => {
debug!("replacement");
let mut st = serializer.serialize_struct_variant("hunk", 6, "replacement", 5)?;
st.serialize_field("path", &local.path)?;
st.serialize_field("line", &local.line)?;
st.serialize_field("encoding", encoding_label(encoding))?;
st.serialize_field(
"change",
&PrettyAtom {
atom: change,
change_store: self.change_store,
change_contents: self.change_contents,
hashes: self.hashes,
encoding,
},
)?;
st.serialize_field(
"replacement",
&PrettyAtom {
atom: replacement,
hashes: self.hashes,
change_store: self.change_store,
change_contents: self.change_contents,
encoding,
},
)?;
st.end()
}
Hunk::SolveNameConflict { name, path } => {
let mut st =
serializer.serialize_struct_variant("hunk", 7, "solve_name_conflict", 3)?;
st.serialize_field("path", &format!("{}", path))?;
st.serialize_field(
"name",
&PrettyAtom {
atom: name,
hashes: self.hashes,
change_store: self.change_store,
change_contents: self.change_contents,
encoding: &None,
},
)?;
st.serialize_field(
"name_names",
&PrettyAtomNames {
change_store: self.change_store,
atom: name,
},
)?;
st.end()
}
Hunk::UnsolveNameConflict { name, path } => {
let mut st =
serializer.serialize_struct_variant("hunk", 8, "unsolve_name_conflict", 3)?;
st.serialize_field("path", &path)?;
st.serialize_field(
"name",
&PrettyAtom {
atom: name,
hashes: self.hashes,
change_store: self.change_store,
change_contents: self.change_contents,
encoding: &None,
},
)?;
st.serialize_field(
"name_names",
&PrettyAtomNames {
change_store: self.change_store,
atom: name,
},
)?;
st.end()
}
Hunk::SolveOrderConflict { change, local } => {
let mut st =
serializer.serialize_struct_variant("hunk", 9, "solve_order_conflict", 3)?;
st.serialize_field("path", &local.path)?;
st.serialize_field("line", &local.line)?;
st.serialize_field(
"change",
&PrettyAtom {
atom: change,
hashes: self.hashes,
change_store: self.change_store,
change_contents: self.change_contents,
encoding: &None,
},
)?;
st.end()
}
Hunk::UnsolveOrderConflict { change, local } => {
let mut st =
serializer.serialize_struct_variant("hunk", 10, "unsolve_order_conflict", 3)?;
st.serialize_field("path", &local.path)?;
st.serialize_field("line", &local.line)?;
st.serialize_field(
"change",
&PrettyAtom {
atom: change,
hashes: self.hashes,
change_store: self.change_store,
change_contents: self.change_contents,
encoding: &None,
},
)?;
st.end()
}
Hunk::ResurrectZombies {
change,
local,
encoding,
} => {
let mut st =
serializer.serialize_struct_variant("hunk", 11, "resurrect_zombies", 4)?;
st.serialize_field("path", &local.path)?;
st.serialize_field("line", &local.line)?;
st.serialize_field("encoding", encoding_label(encoding))?;
st.serialize_field(
"change",
&PrettyAtom {
atom: change,
hashes: self.hashes,
change_store: self.change_store,
change_contents: self.change_contents,
encoding: &None,
},
)?;
st.end()
}
}
}
}
Some(zstd_seekable::Seekable::init(OffFile {
f: r,
start: offsets.contents_off,
}, None)?)
Some(zstd_seekable::Seekable::init(
OffFile {
f: r,
start: offsets.contents_off,
},
None,
)?)