use super::decode::{Row, schema_for};
use super::sys;
use crate::contract::Variant;
#[derive(Debug, Clone, Copy)]
pub enum Value<'a> {
Text(&'a str),
Int(i64),
Real(f64),
Bool(bool),
Enum(Variant),
Texts(Texts<'a>),
Rows(RowSeq<'a>),
}
impl<'a> Value<'a> {
#[must_use]
pub fn as_str(self) -> Option<&'a str> {
match self {
Self::Text(s) => Some(s),
_ => None,
}
}
#[must_use]
pub fn as_i64(self) -> Option<i64> {
match self {
Self::Int(v) => Some(v),
_ => None,
}
}
#[must_use]
pub fn as_f64(self) -> Option<f64> {
match self {
Self::Real(v) => Some(v),
_ => None,
}
}
#[must_use]
pub fn as_bool(self) -> Option<bool> {
match self {
Self::Bool(v) => Some(v),
_ => None,
}
}
#[must_use]
pub fn as_variant(self) -> Option<Variant> {
match self {
Self::Enum(v) => Some(v),
_ => None,
}
}
#[must_use]
pub fn as_texts(self) -> Option<Texts<'a>> {
match self {
Self::Texts(t) => Some(t),
_ => None,
}
}
#[must_use]
pub fn as_rows(self) -> Option<RowSeq<'a>> {
match self {
Self::Rows(r) => Some(r),
_ => None,
}
}
#[must_use]
pub fn to_owned(self) -> OwnedValue {
match self {
Self::Text(s) => OwnedValue::Text(s.to_owned()),
Self::Int(v) => OwnedValue::Int(v),
Self::Real(v) => OwnedValue::Real(v),
Self::Bool(v) => OwnedValue::Bool(v),
Self::Enum(v) => OwnedValue::Enum(v),
Self::Texts(t) => OwnedValue::Texts(t.to_vec()),
Self::Rows(r) => OwnedValue::Rows(r.iter().map(|row| row.to_owned()).collect()),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Texts<'a>(TextsRepr<'a>);
#[derive(Debug, Clone, Copy)]
enum TextsRepr<'a> {
Wire(&'a [sys::Text]),
Owned(&'a [String]),
}
impl<'a> Texts<'a> {
#[must_use]
pub fn len(self) -> usize {
match self.0 {
TextsRepr::Wire(s) => s.len(),
TextsRepr::Owned(s) => s.len(),
}
}
#[must_use]
pub fn is_empty(self) -> bool {
self.len() == 0
}
#[must_use]
pub fn get(self, i: usize) -> Option<&'a str> {
match self.0 {
TextsRepr::Wire(s) => s.get(i).map(|t| unsafe { text_unchecked(t.ptr, t.len) }),
TextsRepr::Owned(s) => s.get(i).map(String::as_str),
}
}
pub fn iter(self) -> impl ExactSizeIterator<Item = &'a str> {
(0..self.len()).map(move |i| self.get(i).unwrap_or_default())
}
#[must_use]
pub fn to_vec(self) -> Vec<String> {
self.iter().map(str::to_owned).collect()
}
}
#[derive(Debug, Clone, Copy)]
pub struct RowSeq<'a>(RowSeqRepr<'a>);
#[derive(Debug, Clone, Copy)]
enum RowSeqRepr<'a> {
Wire(&'a [sys::Row]),
Owned(&'a [OwnedRow]),
}
impl<'a> RowSeq<'a> {
#[must_use]
pub fn len(self) -> usize {
match self.0 {
RowSeqRepr::Wire(s) => s.len(),
RowSeqRepr::Owned(s) => s.len(),
}
}
#[must_use]
pub fn is_empty(self) -> bool {
self.len() == 0
}
#[must_use]
pub fn get(self, i: usize) -> Option<Row<'a>> {
match self.0 {
RowSeqRepr::Wire(s) => s.get(i).map(Row::wire_unchecked),
RowSeqRepr::Owned(s) => s.get(i).and_then(OwnedRow::view),
}
}
pub fn iter(self) -> impl Iterator<Item = Row<'a>> {
(0..self.len()).filter_map(move |i| self.get(i))
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct OwnedRow {
pub schema_id: u32,
pub values: Vec<Option<OwnedValue>>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum OwnedValue {
Text(String),
Int(i64),
Real(f64),
Bool(bool),
Enum(Variant),
Texts(Vec<String>),
Rows(Vec<OwnedRow>),
}
impl OwnedRow {
#[must_use]
pub fn new(schema_id: u32) -> Self {
let n = schema_for(schema_id).map_or(0, |s| s.fields.len());
Self {
schema_id,
values: vec![None; n],
}
}
pub fn set(&mut self, name: &str, value: OwnedValue) {
let Some(i) =
schema_for(self.schema_id).and_then(|s| s.fields.iter().position(|f| f.name == name))
else {
return;
};
if i < self.values.len() {
self.values[i] = Some(value);
}
}
#[must_use]
pub fn view(&self) -> Option<Row<'_>> {
Row::over(self.schema_id, &self.values)
}
}
pub(super) fn read<'a>(field: &crate::contract::schema::FieldDef, v: &'a sys::Value) -> Value<'a> {
match field.tag {
sys::VAL_I64 => Value::Int(v.integer),
sys::VAL_F64 => Value::Real(v.real),
sys::VAL_BOOL => Value::Bool(v.integer != 0),
sys::VAL_ENUM => Value::Enum(Variant {
enum_id: field.nested,
ordinal: v.integer,
}),
sys::VAL_TEXTS => Value::Texts(Texts(TextsRepr::Wire(unsafe {
slice(v.ptr.cast::<sys::Text>(), v.len)
}))),
sys::VAL_ROWS => Value::Rows(RowSeq(RowSeqRepr::Wire(unsafe {
slice(v.ptr.cast::<sys::Row>(), v.len)
}))),
_ => Value::Text(unsafe { text_unchecked(v.ptr.cast::<u8>(), v.len) }),
}
}
pub(super) fn borrow(v: &OwnedValue) -> Value<'_> {
match v {
OwnedValue::Text(s) => Value::Text(s),
OwnedValue::Int(i) => Value::Int(*i),
OwnedValue::Real(f) => Value::Real(*f),
OwnedValue::Bool(b) => Value::Bool(*b),
OwnedValue::Enum(e) => Value::Enum(*e),
OwnedValue::Texts(t) => Value::Texts(Texts(TextsRepr::Owned(t))),
OwnedValue::Rows(r) => Value::Rows(RowSeq(RowSeqRepr::Owned(r))),
}
}
pub(super) unsafe fn slice<'a, T>(ptr: *const T, len: usize) -> &'a [T] {
if ptr.is_null() || len == 0 {
return &[];
}
unsafe { std::slice::from_raw_parts(ptr, len) }
}
pub(super) unsafe fn text_unchecked<'a>(ptr: *const u8, len: usize) -> &'a str {
let bytes = unsafe { slice(ptr, len) };
std::str::from_utf8(bytes).unwrap_or_default()
}