1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
use crate::io_utils;
use crate::serde::{Datum, DatumType, DatumTypeInt};
use crate::types::{SVShared, Value};
use anyhow::Result;
use owning_ref::OwningRef;
use std::io::{BufRead, Cursor, Write};
use std::str;
use std::sync::Arc;
mod test;
/// [`SubValueSpec`] specifies a contiguous sub-portion of a [`Value`].
///
/// The spec is a DSL for locating this sub-portion,
/// as well as an extractor of this sub-portion.
///
/// #### Specification
///
/// `member_idxs`:
/// - The empty `member_idxs` specifies the whole [`Value`].
/// - Each `member_idx` specifies a member within a [`Datum::Tuple`].
/// A series of `member_idx`s specifies members within nested Tuples.
///
/// `datum_type`:
/// - The `datum_type` equalling [`DatumType::Tuple`] specifies the whole (nested) Tuple.
///
/// For example, given a tuple-typed [`Value`]
///
/// ```text
/// Value(
/// Datum::Tuple(vec![
/// Datum::I64(0),
/// Datum::Tuple(vec![
/// Datum::I64(1),
/// Datum::Str(String::from("2")),
/// Datum::Tuple(vec![
/// Datum::I64(3),
/// Datum::Str(String::from("4")),
/// ])
/// ])
/// ])
/// )
/// ```
///
/// If you want to specify the `Datum::I64(1)`:
///
/// ```text
/// SubValueSpec {
/// member_idxs: vec![1, 0],
/// datum_type: DatumType::I64,
/// }
/// ```
///
/// If you want to specify the `Datum::Tuple` containing data 3 and 4:
///
/// ```text
/// SubValueSpec {
/// member_idxs: vec![1, 2],
/// datum_type: DatumType::Tuple,
/// }
/// ```
#[derive(PartialEq, Eq, Hash, Debug)]
pub struct SubValueSpec {
pub member_idxs: Vec<u32>,
pub datum_type: DatumType,
}
/* Shorthand helper for a non-nested spec. */
impl SubValueSpec {
pub fn whole(datum_type: DatumType) -> Self {
Self {
member_idxs: vec![],
datum_type,
}
}
}
/* Extraction. */
impl SubValueSpec {
pub fn extract(&self, pv: &Arc<Value>) -> Option<SVShared> {
let mut dat: &Datum = pv;
for member_idx in self.member_idxs.iter() {
let member_idx = *member_idx as usize;
match dat {
Datum::Tuple(members) if member_idx < members.len() => {
dat = &members[member_idx];
}
_ => return None,
}
}
if DatumType::from(dat) == self.datum_type {
let dat = dat as *const _;
let dat = unsafe { &*dat };
let ownref = OwningRef::new(Arc::clone(pv)).map(|_| dat);
return Some(SVShared::Ref(ownref));
}
None
}
}
/* De/Serialization. */
impl SubValueSpec {
pub fn ser<W: Write>(&self, w: &mut W) -> Result<()> {
/* datum_type */
let datum_type_int = DatumTypeInt::from(self.datum_type);
write!(w, "{};", *datum_type_int)?;
/* member_idxs */
for member_idx in self.member_idxs.iter() {
write!(w, "{},", member_idx)?;
}
Ok(())
}
pub fn deser<R: BufRead>(r: &mut R) -> Result<Self> {
let mut buf = vec![];
/* datum_type */
io_utils::read_until_then_trim(r, b';', &mut buf)?;
let datum_type_int = str::from_utf8(&buf)?.parse::<u8>()?;
let datum_type_int = DatumTypeInt::from(datum_type_int);
let datum_type = DatumType::try_from(datum_type_int)?;
/* member_idxs */
let mut member_idxs = vec![];
loop {
buf.clear();
io_utils::read_until_then_trim(r, b',', &mut buf)?;
if buf.is_empty() {
break;
}
let member_idx = str::from_utf8(&buf)?.parse::<u32>()?;
member_idxs.push(member_idx);
}
Ok(Self {
member_idxs,
datum_type,
})
}
pub fn ser_solo(&self) -> Result<Vec<u8>> {
let mut buf = vec![];
self.ser(&mut buf)?;
Ok(buf)
}
pub fn deser_solo(buf: &[u8]) -> Result<Self> {
let mut r = Cursor::new(&buf);
Self::deser(&mut r)
}
}