use std::io::Write;
use super::functions::kind_tags;
use super::functions::{
count_by_kind, has_valid_magic, peek_decl_count, peek_version, HEADER_SIZE, MAGIC, VERSION,
};
#[allow(dead_code)]
pub struct BlobValidator {
expected_checksum: u32,
}
#[allow(dead_code)]
impl BlobValidator {
pub fn new(expected_checksum: u32) -> Self {
BlobValidator { expected_checksum }
}
pub fn compute_checksum(data: &[u8]) -> u32 {
let mut hash: u32 = 2_166_136_261;
for &b in data {
hash ^= b as u32;
hash = hash.wrapping_mul(16_777_619);
}
hash
}
pub fn validate(&self, data: &[u8]) -> bool {
Self::compute_checksum(data) == self.expected_checksum
}
pub fn expected(&self) -> u32 {
self.expected_checksum
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct FileStats {
pub total_decls: u32,
pub axioms: u32,
pub definitions: u32,
pub theorems: u32,
pub opaques: u32,
pub inductives: u32,
pub other: u32,
pub total_bytes: usize,
}
#[allow(dead_code)]
impl FileStats {
pub fn from_decls(decls: &[SerialDecl], total_bytes: usize) -> Self {
let counts = count_by_kind(decls);
FileStats {
total_decls: decls.len() as u32,
axioms: counts[kind_tags::AXIOM as usize],
definitions: counts[kind_tags::DEFINITION as usize],
theorems: counts[kind_tags::THEOREM as usize],
opaques: counts[kind_tags::OPAQUE as usize],
inductives: counts[kind_tags::INDUCTIVE as usize],
other: counts[kind_tags::OTHER as usize],
total_bytes,
}
}
pub fn bytes_per_decl(&self) -> f64 {
if self.total_decls == 0 {
0.0
} else {
self.total_bytes as f64 / self.total_decls as f64
}
}
pub fn summary(&self) -> String {
format!(
"total={} axioms={} defs={} theorems={} inductives={} bytes={}",
self.total_decls,
self.axioms,
self.definitions,
self.theorems,
self.inductives,
self.total_bytes
)
}
}
pub struct OleanReader<'a> {
data: &'a [u8],
pos: usize,
}
impl<'a> OleanReader<'a> {
pub fn new(data: &'a [u8]) -> Self {
OleanReader { data, pos: 0 }
}
pub fn remaining(&self) -> usize {
self.data.len().saturating_sub(self.pos)
}
fn ensure(&self, n: usize) -> Result<(), OleanError> {
if self.remaining() < n {
Err(OleanError::UnexpectedEof)
} else {
Ok(())
}
}
pub fn read_header(&mut self) -> Result<OleanHeader, OleanError> {
self.ensure(HEADER_SIZE)?;
let magic = &self.data[self.pos..self.pos + 4];
if magic != MAGIC {
return Err(OleanError::InvalidMagic);
}
self.pos += 4;
let version = self.read_u32()?;
if version != VERSION {
return Err(OleanError::UnsupportedVersion(version));
}
let decl_count = self.read_u32()?;
let metadata_offset = self.read_u64()?;
Ok(OleanHeader {
version,
decl_count,
metadata_offset,
})
}
pub fn read_string(&mut self) -> Result<String, OleanError> {
let len = self.read_u32()? as usize;
self.ensure(len)?;
let bytes = self.data[self.pos..self.pos + len].to_vec();
self.pos += len;
Ok(String::from_utf8(bytes)?)
}
pub fn read_u8(&mut self) -> Result<u8, OleanError> {
self.ensure(1)?;
let v = self.data[self.pos];
self.pos += 1;
Ok(v)
}
pub fn read_u32(&mut self) -> Result<u32, OleanError> {
self.ensure(4)?;
let bytes: [u8; 4] = self.data[self.pos..self.pos + 4]
.try_into()
.expect("slice length must match array size");
self.pos += 4;
Ok(u32::from_le_bytes(bytes))
}
pub fn read_u64(&mut self) -> Result<u64, OleanError> {
self.ensure(8)?;
let bytes: [u8; 8] = self.data[self.pos..self.pos + 8]
.try_into()
.expect("slice length must match array size");
self.pos += 8;
Ok(u64::from_le_bytes(bytes))
}
pub fn read_i64(&mut self) -> Result<i64, OleanError> {
self.ensure(8)?;
let bytes: [u8; 8] = self.data[self.pos..self.pos + 8]
.try_into()
.expect("slice length must match array size");
self.pos += 8;
Ok(i64::from_le_bytes(bytes))
}
pub fn read_bool(&mut self) -> Result<bool, OleanError> {
Ok(self.read_u8()? != 0)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct SectionHeader {
pub tag: u8,
pub length: u32,
pub offset: u64,
}
#[allow(dead_code)]
impl SectionHeader {
pub fn new(tag: u8, length: u32, offset: u64) -> Self {
SectionHeader {
tag,
length,
offset,
}
}
pub const SIZE: usize = 13;
pub fn write(&self, w: &mut OleanWriter) {
w.write_u8(self.tag);
w.write_u32(self.length);
w.write_u64(self.offset);
}
pub fn read(r: &mut OleanReader<'_>) -> Result<Self, OleanError> {
let tag = r.read_u8()?;
let length = r.read_u32()?;
let offset = r.read_u64()?;
Ok(SectionHeader {
tag,
length,
offset,
})
}
}
#[allow(dead_code)]
pub struct ChecksummedWriter {
inner: OleanWriter,
running_hash: u32,
}
#[allow(dead_code)]
impl ChecksummedWriter {
pub fn new() -> Self {
ChecksummedWriter {
inner: OleanWriter::new(),
running_hash: 2_166_136_261,
}
}
pub fn write_byte(&mut self, b: u8) {
self.running_hash ^= b as u32;
self.running_hash = self.running_hash.wrapping_mul(16_777_619);
self.inner.write_u8(b);
}
pub fn write_bytes(&mut self, data: &[u8]) {
for &b in data {
self.write_byte(b);
}
}
pub fn write_u32(&mut self, v: u32) {
self.write_bytes(&v.to_le_bytes());
}
pub fn write_u64(&mut self, v: u64) {
self.write_bytes(&v.to_le_bytes());
}
pub fn write_string(&mut self, s: &str) {
self.write_u32(s.len() as u32);
self.write_bytes(s.as_bytes());
}
pub fn current_checksum(&self) -> u32 {
self.running_hash
}
pub fn finish_with_checksum(mut self) -> Vec<u8> {
let checksum = self.running_hash;
self.inner.write_u32(checksum);
self.inner.finish()
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[allow(dead_code)]
pub struct NameTable {
entries: Vec<(String, u32)>,
}
#[allow(dead_code)]
impl NameTable {
pub fn new() -> Self {
NameTable {
entries: Vec::new(),
}
}
pub fn intern(&mut self, name: &str) -> u32 {
if let Some(&(_, id)) = self.entries.iter().find(|(n, _)| n == name) {
return id;
}
let id = self.entries.len() as u32;
self.entries.push((name.to_string(), id));
id
}
pub fn lookup_id(&self, id: u32) -> Option<&str> {
self.entries
.iter()
.find(|(_, i)| *i == id)
.map(|(n, _)| n.as_str())
}
pub fn lookup_name(&self, name: &str) -> Option<u32> {
self.entries
.iter()
.find(|(n, _)| n == name)
.map(|(_, id)| *id)
}
pub fn write(&self, w: &mut OleanWriter) {
w.write_u32(self.entries.len() as u32);
for (name, id) in &self.entries {
w.write_string(name);
w.write_u32(*id);
}
}
pub fn read(r: &mut OleanReader<'_>) -> Result<Self, OleanError> {
let count = r.read_u32()? as usize;
let mut t = NameTable::new();
for _ in 0..count {
let name = r.read_string()?;
let id = r.read_u32()?;
t.entries.push((name, id));
}
Ok(t)
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn names(&self) -> Vec<&str> {
self.entries.iter().map(|(n, _)| n.as_str()).collect()
}
}
#[allow(dead_code)]
pub struct DeclKindSet {
mask: u8,
}
#[allow(dead_code)]
impl DeclKindSet {
pub fn new() -> Self {
DeclKindSet { mask: 0 }
}
pub fn add(&mut self, tag: u8) {
if tag < 8 {
self.mask |= 1 << tag;
}
}
pub fn contains(&self, tag: u8) -> bool {
tag < 8 && (self.mask >> tag) & 1 != 0
}
pub fn mask(&self) -> u8 {
self.mask
}
pub fn count(&self) -> u32 {
self.mask.count_ones()
}
pub fn is_empty(&self) -> bool {
self.mask == 0
}
pub fn write(&self, w: &mut OleanWriter) {
w.write_u8(self.mask);
}
pub fn read(r: &mut OleanReader<'_>) -> Result<Self, OleanError> {
Ok(DeclKindSet { mask: r.read_u8()? })
}
}
#[derive(Debug)]
pub enum OleanError {
InvalidMagic,
UnsupportedVersion(u32),
UnexpectedEof,
InvalidUtf8(std::string::FromUtf8Error),
InvalidDeclKind(u8),
IoError(std::io::Error),
}
#[allow(dead_code)]
pub struct StringPool {
strings: Vec<String>,
}
#[allow(dead_code)]
impl StringPool {
pub fn new() -> Self {
StringPool {
strings: Vec::new(),
}
}
pub fn intern(&mut self, s: &str) -> u32 {
if let Some(idx) = self.strings.iter().position(|x| x == s) {
return idx as u32;
}
let idx = self.strings.len() as u32;
self.strings.push(s.to_string());
idx
}
pub fn get(&self, idx: u32) -> Option<&str> {
self.strings.get(idx as usize).map(|s| s.as_str())
}
pub fn len(&self) -> usize {
self.strings.len()
}
pub fn is_empty(&self) -> bool {
self.strings.is_empty()
}
pub fn write(&self, w: &mut OleanWriter) {
w.write_u32(self.strings.len() as u32);
for s in &self.strings {
w.write_string(s);
}
}
pub fn read(r: &mut OleanReader<'_>) -> Result<Self, OleanError> {
let count = r.read_u32()? as usize;
let mut pool = StringPool::new();
for _ in 0..count {
let s = r.read_string()?;
pool.strings.push(s);
}
Ok(pool)
}
pub fn all_strings(&self) -> &[String] {
&self.strings
}
}
#[allow(dead_code)]
pub struct DeclIndex {
names: Vec<(String, u32)>,
}
#[allow(dead_code)]
impl DeclIndex {
pub fn new() -> Self {
DeclIndex { names: Vec::new() }
}
pub fn add(&mut self, name: &str, offset: u32) {
self.names.push((name.to_string(), offset));
}
pub fn find_offset(&self, name: &str) -> Option<u32> {
self.names.iter().find(|(n, _)| n == name).map(|(_, o)| *o)
}
pub fn contains(&self, name: &str) -> bool {
self.names.iter().any(|(n, _)| n == name)
}
pub fn len(&self) -> usize {
self.names.len()
}
pub fn is_empty(&self) -> bool {
self.names.is_empty()
}
pub fn sorted_names(&self) -> Vec<&str> {
let mut v: Vec<&str> = self.names.iter().map(|(n, _)| n.as_str()).collect();
v.sort_unstable();
v
}
pub fn binary_search(&self, name: &str) -> Option<u32> {
let mut lo = 0usize;
let mut hi = self.names.len();
while lo < hi {
let mid = lo + (hi - lo) / 2;
match self.names[mid].0.as_str().cmp(name) {
std::cmp::Ordering::Equal => return Some(self.names[mid].1),
std::cmp::Ordering::Less => lo = mid + 1,
std::cmp::Ordering::Greater => hi = mid,
}
}
None
}
pub fn write(&self, w: &mut OleanWriter) {
w.write_u32(self.names.len() as u32);
for (name, offset) in &self.names {
w.write_string(name);
w.write_u32(*offset);
}
}
pub fn read(r: &mut OleanReader<'_>) -> Result<Self, OleanError> {
let count = r.read_u32()? as usize;
let mut idx = DeclIndex::new();
for _ in 0..count {
let name = r.read_string()?;
let offset = r.read_u32()?;
idx.names.push((name, offset));
}
Ok(idx)
}
}
#[allow(dead_code)]
pub struct MetadataWriter {
buf: OleanWriter,
entry_count: u32,
}
#[allow(dead_code)]
impl MetadataWriter {
pub fn new() -> Self {
MetadataWriter {
buf: OleanWriter::new(),
entry_count: 0,
}
}
pub fn write_str_entry(&mut self, key: &str, value: &str) {
self.buf.write_u8(0);
self.buf.write_string(key);
self.buf.write_string(value);
self.entry_count += 1;
}
pub fn write_u64_entry(&mut self, key: &str, value: u64) {
self.buf.write_u8(1);
self.buf.write_string(key);
self.buf.write_u64(value);
self.entry_count += 1;
}
pub fn write_bool_entry(&mut self, key: &str, value: bool) {
self.buf.write_u8(2);
self.buf.write_string(key);
self.buf.write_bool(value);
self.entry_count += 1;
}
pub fn entry_count(&self) -> u32 {
self.entry_count
}
pub fn finish(self) -> Vec<u8> {
let mut w = OleanWriter::new();
w.write_u32(self.entry_count);
let inner_bytes = self.buf.finish();
w.buf.extend_from_slice(&inner_bytes);
w.finish()
}
}
#[allow(dead_code)]
pub struct SectionTable {
headers: Vec<SectionHeader>,
}
#[allow(dead_code)]
impl SectionTable {
pub fn new() -> Self {
SectionTable {
headers: Vec::new(),
}
}
pub fn add(&mut self, header: SectionHeader) {
self.headers.push(header);
}
pub fn find(&self, tag: u8) -> Option<&SectionHeader> {
self.headers.iter().find(|h| h.tag == tag)
}
pub fn len(&self) -> usize {
self.headers.len()
}
pub fn is_empty(&self) -> bool {
self.headers.is_empty()
}
pub fn write(&self, w: &mut OleanWriter) {
w.write_u32(self.headers.len() as u32);
for h in &self.headers {
h.write(w);
}
}
pub fn read(r: &mut OleanReader<'_>) -> Result<Self, OleanError> {
let count = r.read_u32()? as usize;
let mut table = SectionTable::new();
for _ in 0..count {
table.add(SectionHeader::read(r)?);
}
Ok(table)
}
}
#[allow(dead_code)]
pub struct CheckpointedReader<'a> {
data: &'a [u8],
pos: usize,
checkpoint: Option<usize>,
}
#[allow(dead_code)]
impl<'a> CheckpointedReader<'a> {
pub fn new(data: &'a [u8]) -> Self {
CheckpointedReader {
data,
pos: 0,
checkpoint: None,
}
}
pub fn save(&mut self) {
self.checkpoint = Some(self.pos);
}
pub fn rollback(&mut self) -> bool {
if let Some(cp) = self.checkpoint {
self.pos = cp;
true
} else {
false
}
}
pub fn remaining(&self) -> usize {
self.data.len().saturating_sub(self.pos)
}
pub fn read_u8(&mut self) -> Result<u8, OleanError> {
if self.remaining() < 1 {
return Err(OleanError::UnexpectedEof);
}
let v = self.data[self.pos];
self.pos += 1;
Ok(v)
}
pub fn read_u32(&mut self) -> Result<u32, OleanError> {
if self.remaining() < 4 {
return Err(OleanError::UnexpectedEof);
}
let bytes: [u8; 4] = self.data[self.pos..self.pos + 4]
.try_into()
.expect("slice length must match array size");
self.pos += 4;
Ok(u32::from_le_bytes(bytes))
}
pub fn read_string(&mut self) -> Result<String, OleanError> {
let len = self.read_u32()? as usize;
if self.remaining() < len {
return Err(OleanError::UnexpectedEof);
}
let s = String::from_utf8(self.data[self.pos..self.pos + len].to_vec())?;
self.pos += len;
Ok(s)
}
pub fn pos(&self) -> usize {
self.pos
}
}
#[allow(dead_code)]
pub struct BufferedOleanWriter {
buf: Vec<u8>,
flush_threshold: usize,
total_written: usize,
}
#[allow(dead_code)]
impl BufferedOleanWriter {
pub fn new(flush_threshold: usize) -> Self {
BufferedOleanWriter {
buf: Vec::with_capacity(flush_threshold),
flush_threshold,
total_written: 0,
}
}
pub fn write_u8(&mut self, b: u8) {
self.buf.push(b);
self.total_written += 1;
}
pub fn write_u32(&mut self, v: u32) {
for b in v.to_le_bytes() {
self.write_u8(b);
}
}
pub fn write_u64(&mut self, v: u64) {
for b in v.to_le_bytes() {
self.write_u8(b);
}
}
pub fn write_string(&mut self, s: &str) {
self.write_u32(s.len() as u32);
for b in s.as_bytes() {
self.write_u8(*b);
}
}
pub fn total_written(&self) -> usize {
self.total_written
}
pub fn buffered(&self) -> usize {
self.buf.len()
}
pub fn flush(self) -> Vec<u8> {
self.buf
}
pub fn should_flush(&self) -> bool {
self.buf.len() >= self.flush_threshold
}
}
#[allow(dead_code)]
pub enum MergeStrategy {
Union,
Intersection,
PreferFirst,
PreferSecond,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct FormatDiagnostics {
pub version: u32,
pub decl_count: u32,
pub total_bytes: usize,
pub magic_ok: bool,
pub checksum_ok: Option<bool>,
pub sections: Vec<String>,
}
#[allow(dead_code)]
impl FormatDiagnostics {
pub fn from_bytes(data: &[u8]) -> Self {
let magic_ok = has_valid_magic(data);
let version = peek_version(data).unwrap_or(0);
let decl_count = peek_decl_count(data).unwrap_or(0);
FormatDiagnostics {
version,
decl_count,
total_bytes: data.len(),
magic_ok,
checksum_ok: None,
sections: Vec::new(),
}
}
pub fn report(&self) -> String {
format!(
"magic={} version={} decls={} bytes={}",
self.magic_ok, self.version, self.decl_count, self.total_bytes
)
}
pub fn is_well_formed(&self) -> bool {
self.magic_ok && self.version > 0
}
}
#[derive(Debug, Clone)]
pub struct OleanHeader {
pub version: u32,
pub decl_count: u32,
pub metadata_offset: u64,
}
#[derive(Debug, Clone, PartialEq)]
pub enum SerialDecl {
Axiom {
name: String,
kind_tag: u8,
},
Definition {
name: String,
kind_tag: u8,
},
Theorem {
name: String,
kind_tag: u8,
},
Opaque {
name: String,
kind_tag: u8,
},
Inductive {
name: String,
ctor_count: u32,
kind_tag: u8,
},
Other {
name: String,
kind_tag: u8,
},
}
impl SerialDecl {
pub fn name(&self) -> &str {
match self {
SerialDecl::Axiom { name, .. } => name,
SerialDecl::Definition { name, .. } => name,
SerialDecl::Theorem { name, .. } => name,
SerialDecl::Opaque { name, .. } => name,
SerialDecl::Inductive { name, .. } => name,
SerialDecl::Other { name, .. } => name,
}
}
pub fn kind_tag(&self) -> u8 {
match self {
SerialDecl::Axiom { kind_tag, .. } => *kind_tag,
SerialDecl::Definition { kind_tag, .. } => *kind_tag,
SerialDecl::Theorem { kind_tag, .. } => *kind_tag,
SerialDecl::Opaque { kind_tag, .. } => *kind_tag,
SerialDecl::Inductive { kind_tag, .. } => *kind_tag,
SerialDecl::Other { kind_tag, .. } => *kind_tag,
}
}
}
pub struct OleanWriter {
buf: Vec<u8>,
}
impl OleanWriter {
pub fn new() -> Self {
OleanWriter { buf: Vec::new() }
}
pub fn write_header(&mut self, decl_count: u32) -> &mut Self {
self.buf.extend_from_slice(MAGIC);
self.write_u32(VERSION);
self.write_u32(decl_count);
self.write_u64(HEADER_SIZE as u64);
self
}
pub fn write_string(&mut self, s: &str) -> &mut Self {
let bytes = s.as_bytes();
self.write_u32(bytes.len() as u32);
self.buf.extend_from_slice(bytes);
self
}
pub fn write_u8(&mut self, v: u8) -> &mut Self {
self.buf.push(v);
self
}
pub fn write_u32(&mut self, v: u32) -> &mut Self {
self.buf.extend_from_slice(&v.to_le_bytes());
self
}
pub fn write_u64(&mut self, v: u64) -> &mut Self {
self.buf.extend_from_slice(&v.to_le_bytes());
self
}
pub fn write_i64(&mut self, v: i64) -> &mut Self {
self.buf.extend_from_slice(&v.to_le_bytes());
self
}
pub fn write_bool(&mut self, v: bool) -> &mut Self {
self.write_u8(if v { 1 } else { 0 })
}
pub fn len(&self) -> usize {
self.buf.len()
}
pub fn is_empty(&self) -> bool {
self.buf.is_empty()
}
pub fn finish(self) -> Vec<u8> {
self.buf
}
}
#[allow(dead_code)]
pub struct DeltaList {
deltas: Vec<i32>,
base: u32,
}
#[allow(dead_code)]
impl DeltaList {
pub fn encode(values: &[u32]) -> Self {
let mut deltas = Vec::with_capacity(values.len());
let mut prev = 0u32;
for &v in values {
let delta = v as i64 - prev as i64;
deltas.push(delta as i32);
prev = v;
}
DeltaList {
deltas,
base: values.first().copied().unwrap_or(0),
}
}
pub fn decode(&self) -> Vec<u32> {
let mut result = Vec::with_capacity(self.deltas.len());
let mut cur: i64 = 0;
for &d in &self.deltas {
cur += d as i64;
result.push(cur as u32);
}
result
}
pub fn len(&self) -> usize {
self.deltas.len()
}
pub fn is_empty(&self) -> bool {
self.deltas.is_empty()
}
pub fn write(&self, w: &mut OleanWriter) {
w.write_u32(self.base);
w.write_u32(self.deltas.len() as u32);
for &d in &self.deltas {
w.write_i64(d as i64);
}
}
pub fn read(r: &mut OleanReader<'_>) -> Result<Self, OleanError> {
let base = r.read_u32()?;
let count = r.read_u32()? as usize;
let mut deltas = Vec::with_capacity(count);
for _ in 0..count {
deltas.push(r.read_i64()? as i32);
}
Ok(DeltaList { deltas, base })
}
}
#[allow(dead_code)]
pub struct MetadataReader<'a> {
inner: OleanReader<'a>,
count: u32,
read: u32,
}
#[allow(dead_code)]
impl<'a> MetadataReader<'a> {
pub fn new(data: &'a [u8]) -> Result<Self, OleanError> {
let mut r = OleanReader::new(data);
let count = r.read_u32()?;
Ok(MetadataReader {
inner: r,
count,
read: 0,
})
}
pub fn has_next(&self) -> bool {
self.read < self.count
}
pub fn next_entry(&mut self) -> Result<(String, MetadataValue), OleanError> {
let tag = self.inner.read_u8()?;
let key = self.inner.read_string()?;
let value = match tag {
0 => MetadataValue::Str(self.inner.read_string()?),
1 => MetadataValue::U64(self.inner.read_u64()?),
2 => MetadataValue::Bool(self.inner.read_bool()?),
_ => return Err(OleanError::InvalidDeclKind(tag)),
};
self.read += 1;
Ok((key, value))
}
pub fn read_all(&mut self) -> Result<Vec<(String, MetadataValue)>, OleanError> {
let mut entries = Vec::new();
while self.has_next() {
entries.push(self.next_entry()?);
}
Ok(entries)
}
}
#[allow(dead_code)]
pub struct BinarySection {
pub header: SectionHeader,
pub data: Vec<u8>,
}
#[allow(dead_code)]
impl BinarySection {
pub fn new(tag: u8, data: Vec<u8>, offset: u64) -> Self {
let length = data.len() as u32;
BinarySection {
header: SectionHeader::new(tag, length, offset),
data,
}
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut w = OleanWriter::new();
self.header.write(&mut w);
w.finish()
.into_iter()
.chain(self.data.iter().copied())
.collect()
}
pub fn tag(&self) -> u8 {
self.header.tag
}
pub fn data_len(&self) -> usize {
self.data.len()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub enum MetadataValue {
Str(String),
U64(u64),
Bool(bool),
}
#[allow(dead_code)]
#[derive(Debug)]
pub struct SerialError {
pub inner: OleanError,
pub context: String,
pub byte_offset: usize,
}
#[allow(dead_code)]
impl SerialError {
pub fn new(inner: OleanError, context: impl Into<String>, byte_offset: usize) -> Self {
SerialError {
inner,
context: context.into(),
byte_offset,
}
}
pub fn describe(&self) -> String {
format!(
"{} at byte {}: {}",
self.context, self.byte_offset, self.inner
)
}
}
#[allow(dead_code)]
pub struct CompatibilityChecker {
known_versions: Vec<u32>,
}
#[allow(dead_code)]
impl CompatibilityChecker {
pub fn new(known_versions: Vec<u32>) -> Self {
CompatibilityChecker { known_versions }
}
pub fn is_compatible(&self, version: u32) -> bool {
self.known_versions.contains(&version)
}
pub fn latest(&self) -> Option<u32> {
self.known_versions.iter().max().copied()
}
pub fn needs_upgrade(&self, old: u32, new: u32) -> bool {
old < new && self.is_compatible(new)
}
}
#[allow(dead_code)]
pub struct DeclDiff {
pub added: Vec<String>,
pub removed: Vec<String>,
pub unchanged: Vec<String>,
}
#[allow(dead_code)]
impl DeclDiff {
pub fn compute(old_names: &[String], new_names: &[String]) -> Self {
let added = new_names
.iter()
.filter(|n| !old_names.contains(n))
.cloned()
.collect();
let removed = old_names
.iter()
.filter(|n| !new_names.contains(n))
.cloned()
.collect();
let unchanged = old_names
.iter()
.filter(|n| new_names.contains(n))
.cloned()
.collect();
DeclDiff {
added,
removed,
unchanged,
}
}
pub fn has_changes(&self) -> bool {
!self.added.is_empty() || !self.removed.is_empty()
}
pub fn summary(&self) -> String {
format!(
"+{} -{} ={} declarations",
self.added.len(),
self.removed.len(),
self.unchanged.len()
)
}
}
#[allow(dead_code)]
pub struct OleanArchive {
files: Vec<(String, Vec<SerialDecl>)>,
}
#[allow(dead_code)]
impl OleanArchive {
pub fn new() -> Self {
OleanArchive { files: Vec::new() }
}
pub fn add_file(&mut self, name: impl Into<String>, decls: Vec<SerialDecl>) {
self.files.push((name.into(), decls));
}
pub fn total_decls(&self) -> usize {
self.files.iter().map(|(_, d)| d.len()).sum()
}
pub fn file_count(&self) -> usize {
self.files.len()
}
pub fn all_names(&self) -> Vec<&str> {
self.files
.iter()
.flat_map(|(_, d)| d.iter().map(|decl| decl.name()))
.collect()
}
pub fn find_decl(&self, name: &str) -> Option<(&str, &SerialDecl)> {
for (fname, decls) in &self.files {
if let Some(d) = decls.iter().find(|d| d.name() == name) {
return Some((fname.as_str(), d));
}
}
None
}
pub fn is_empty(&self) -> bool {
self.files.is_empty()
}
}