rline_api 1.0.0

rline public API to create, manipulate, and convert rows of data, making it easy to work with datasets
Documentation
//! # row
//!
//! Module defining the Row struct, representing a dataset row.
//!
//! A `Row` in the dataset is a collection of named columns, each associated with a `Value`.
//! This module provides functionality to create, manipulate, and convert rows.
//!
//! ## Examples
//!
//! Creating a new Row:
//!
//! ```rust
//! use rline_api::row::Row;
//! use rline_api::value::Value;
//!
//! let row = Row::from(vec![
//!     ("name".to_string(), Value::String("Alice".to_string())),
//!     ("age".to_string(), Value::Integer(30)),
//! ]);
//!
//! assert_eq!(row.get("name"), Some(&Value::String("Alice".to_string())));
//! assert_eq!(row.get("age"), Some(&Value::Integer(30)));
//! ```
//!
//! Converting a Row into a serde_json::Value:
//!
//! ```rust
//! use rline_api::row::Row;
//! use rline_api::value::Value;
//! use serde_json::json;
//!
//! let row = Row::from(vec![
//!     ("name".to_string(), Value::String("Charlie".to_string())),
//!     ("age".to_string(), Value::Integer(40)),
//! ]);
//!
//! let json_value: serde_json::Value = row.into();
//! assert_eq!(
//!     json_value,
//!     json!({
//!         "name": "Charlie",
//!         "age": 40
//!     })
//! );
//! ```
//!
//! Retrieving values from a Row:
//!
//! ```rust
//! use rline_api::row::Row;
//! use rline_api::value::Value;
//!
//! let row = Row::from(vec![
//!     ("name".to_string(), Value::String("Bob".to_string())),
//!     ("age".to_string(), Value::Integer(25)),
//! ]);
//!
//! assert_eq!(row.get("name"), Some(&Value::String("Bob".to_string())));
//! assert_eq!(row.get("age"), Some(&Value::Integer(25)));
//! assert_eq!(row.get("nonexistent"), None);
//! ```
//!
//! ## Usage
//!
//! This module is used within the `rline_api` library to represent rows in datasets. Rows can be
//! created, manipulated, and converted to various formats using the provided functions and methods.
//! The `Row` struct and its associated types are integral to working with tabular data in the
//! `rline_api` crate.
//!
//! ## License
//!
//! This module is part of the `rline_api` crate, licensed under the Apache-2.0 License.
use std::collections::hash_map::{IntoIter, Iter};
use std::collections::HashMap;
use std::ops::{Deref, DerefMut};

use bincode::config::{standard, Configuration, Fixint, LittleEndian};
use bincode::{Decode as BincodeDecode, Encode as BincodeEncode};
use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize};
use serde_json::Value as SerdeJsonValue;

use crate::rline_error::RlineError;
use crate::rline_error::RlineError::RuntimeError;
use crate::value::Value;

type WrappedType = HashMap<String, Value>;

const BINCODE_CONFIG: Configuration<LittleEndian, Fixint> = standard().with_fixed_int_encoding();

/// A Row is a set of columns with their value. It associates column names with typed values.
#[derive(
    SerdeSerialize, SerdeDeserialize, BincodeDecode, BincodeEncode, Clone, Debug, PartialEq,
)]
pub struct Row(WrappedType);

impl Row {
    /// Creates an empty Row.
    /// The Row is initially created with a capacity of 0,
    /// so it will not allocate until it is first inserted into.
    pub fn new() -> Self {
        Row(HashMap::new())
    }

    /// Creates an empty Row with at least the specified capacity.
    /// The Row will be able to hold at least capacity elements without reallocating.
    /// This method is allowed to allocate for more elements than capacity.
    /// If capacity is 0, the hash map will not allocate.
    pub fn with_capacity(capacity: usize) -> Self {
        Row(HashMap::with_capacity(capacity))
    }
}

impl Default for Row {
    /// Creates an empty Row.
    fn default() -> Self {
        Self::new()
    }
}

impl Deref for Row {
    type Target = WrappedType;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for Row {
    fn deref_mut(&mut self) -> &mut WrappedType {
        &mut self.0
    }
}

type ConsumedItem = (String, Value);
type BorrowedItem<'a> = (&'a String, &'a Value);

impl IntoIterator for Row {
    type Item = ConsumedItem;
    type IntoIter = TableIntoIterator;

    /// Creates a consuming iterator, that is, one that moves each column name-value pair out
    /// of the Row in arbitrary order.
    /// The Row cannot be used after calling this.
    fn into_iter(self) -> Self::IntoIter {
        TableIntoIterator {
            iterator: self.0.into_iter(),
        }
    }
}

pub struct TableIntoIterator {
    iterator: IntoIter<String, Value>,
}

impl Iterator for TableIntoIterator {
    type Item = ConsumedItem;

    /// Advances the iterator and returns the next value.
    /// Returns None when iteration is finished.
    fn next(&mut self) -> Option<ConsumedItem> {
        self.iterator.next()
    }
}

impl<'a> IntoIterator for &'a Row {
    type Item = BorrowedItem<'a>;
    type IntoIter = TableIterator<'a>;

    /// An iterator visiting all column name-value pairs in arbitrary order.
    fn into_iter(self) -> Self::IntoIter {
        TableIterator {
            iterator: self.0.iter(),
        }
    }
}

pub struct TableIterator<'a> {
    iterator: Iter<'a, String, Value>,
}

impl<'a> Iterator for TableIterator<'a> {
    type Item = BorrowedItem<'a>;

    /// Advances the iterator and returns the next value.
    /// Returns None when iteration is finished.
    fn next(&mut self) -> Option<BorrowedItem<'a>> {
        self.iterator.next()
    }
}

/// Supported serialization format for row.
#[derive(SerdeDeserialize)]
pub enum SerializationFormat {
    #[serde(alias = "json", alias = "JSON")]
    Json,
    #[serde(alias = "bincode", alias = "BINCODE")]
    Bincode,
}

impl Row {
    /// Serialize this row to bytes in json format.
    pub fn to_bytes_json(&self) -> Result<Vec<u8>, RlineError> {
        serde_json::to_vec(self).map_err(|e| RlineError::runtime_error(&e))
    }

    /// Serialize this row to bytes in bincode format.
    pub fn to_bytes_bincode(&self) -> Result<Vec<u8>, RlineError> {
        bincode::encode_to_vec(self, BINCODE_CONFIG).map_err(|e| RlineError::runtime_error(&e))
    }

    /// Serialize a Result<Row, String> to bytes in bincode format.
    pub fn to_bytes_bincode_result(result: Result<Row, String>) -> Result<Vec<u8>, RlineError> {
        bincode::encode_to_vec(result, BINCODE_CONFIG).map_err(|e| RlineError::runtime_error(&e))
    }

    /// Serialize a Result<Row, String> to bytes in json format.
    pub fn to_bytes_json_result(result: Result<Row, String>) -> Result<Vec<u8>, RlineError> {
        serde_json::to_vec(&result).map_err(|e| RlineError::runtime_error(&e))
    }

    /// Deserialize the bytes in json format.
    pub fn from_bytes_json(from: &[u8]) -> Result<Self, RlineError> {
        serde_json::from_slice(from).map_err(|e| RlineError::runtime_error(&e))
    }

    /// Deserialize the bytes in bincode format.
    pub fn from_bytes_bincode(from: &[u8]) -> Result<Self, RlineError> {
        let (decoded, _) = bincode::decode_from_slice(from, BINCODE_CONFIG)
            .map_err(|e| RlineError::runtime_error(&e))?;
        Ok(decoded)
    }

    /// Deserialize the bytes of a Result<Row, String> in json format.
    pub fn from_bytes_json_result(from: &[u8]) -> Result<Self, RlineError> {
        let decoded: Result<Self, String> =
            serde_json::from_slice(from).map_err(|e| RlineError::runtime_error(&e))?;
        decoded.map_err(RuntimeError)
    }

    /// Deserialize the bytes of a Result<Row, String> in bincode format.
    pub fn from_bytes_bincode_result(from: &[u8]) -> Result<Self, RlineError> {
        let (decoded, _): (Result<Self, String>, usize) =
            bincode::decode_from_slice(from, BINCODE_CONFIG)
                .map_err(|e| RlineError::runtime_error(&e))?;
        decoded.map_err(RuntimeError)
    }

    fn json_value_to_row(json_value: SerdeJsonValue) -> Result<Row, RlineError> {
        match json_value {
            SerdeJsonValue::Null
            | SerdeJsonValue::Bool(_)
            | SerdeJsonValue::Number(_)
            | SerdeJsonValue::String(_)
            | SerdeJsonValue::Array(_) => Err(RuntimeError(
                format!("Json value {} does not represent a row. A row is a map associating column names to their values.", json_value))
            ),
            SerdeJsonValue::Object(map_key_value) => {
                let mut row = Row::with_capacity(map_key_value.len());
                for (column_name, json_value) in map_key_value {
                    row.insert(column_name, Value::from_json_value(json_value)?);
                }
                Ok(row)
            }
        }
    }

    /// Deserialize the bytes in json format infering the Value type from a json value
    pub fn from_bytes_json_infer_types(from: &[u8]) -> Result<Vec<Row>, RlineError> {
        let json_value: SerdeJsonValue =
            serde_json::from_slice(from).map_err(|e| RlineError::runtime_error(&e))?;

        match json_value {
            SerdeJsonValue::Null
            | SerdeJsonValue::Bool(_)
            | SerdeJsonValue::Number(_)
            | SerdeJsonValue::String(_)
            | SerdeJsonValue::Object(_) => Err(RuntimeError(format!(
                "Json value {} does not represent a list of ros.",
                json_value
            ))),
            SerdeJsonValue::Array(values) => {
                values.into_iter().map(Row::json_value_to_row).collect()
            }
        }
    }
}

impl<const N: usize> From<[(String, Value); N]> for Row {
    /// # Examples
    ///
    /// ```
    /// use rline_api::row::Row;
    /// use rline_api::value::Value;
    ///
    /// let table1 = Row::from([
    ///   ("col1".to_string(), Value::from(42)),
    ///   ("col2".to_string(), Value::from("Hello World !".to_string())),
    /// ]);
    /// let table2: Row = [
    ///   ("col1".to_string(), Value::from(42)),
    ///   ("col2".to_string(), Value::from("Hello World !".to_string())),
    /// ].into();
    /// assert_eq!(table1, table2);
    /// ```
    fn from(arr: [(String, Value); N]) -> Self {
        Row(HashMap::from_iter(arr))
    }
}

impl From<HashMap<String, Value>> for Row {
    fn from(map: HashMap<String, Value>) -> Self {
        Row(map)
    }
}