Repository: diesel-rs/diesel
File: diesel/src/pg/types/array.rs (Rust)

Code before:
use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
use core::fmt;
use std::io::Write;

use crate::deserialize::{self, FromSql, FromSqlRow};
use crate::pg::{Pg, PgTypeMetadata, PgValue};
use crate::query_builder::bind_collector::ByteWrapper;
use crate::serialize::{self, IsNull, Output, ToSql};
use crate::sql_types::{Array, HasSqlType, Nullable};

#[cfg(feature = "postgres_backend")]
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, AsExpression, FromSqlRow)]
#[diesel(sql_type = Array<T>)]
/// Postgres allows multi-dimensional arrays of at most 6 dimensions. Internally they are stored as a flattened
/// representation with the dimension information encoded in the header. This struct represents a
/// multi-dimensional array with elements of type `T` as opposed to Vec<T> which can be used for 1d-arrays.
pub struct NdArray<T> {
    pub dims: Vec<usize>,
    pub data: Vec<T>,
}

#[cfg(feature = "postgres_backend")]
impl<T> HasSqlType<Array<T>> for Pg
where
    Pg: HasSqlType<T>,
{
    fn metadata(lookup: &mut Self::MetadataLookup) -> PgTypeMetadata {
        match <Pg as HasSqlType<T>>::metadata(lookup).0 {
            Ok(tpe) => PgTypeMetadata::new(tpe.array_oid, 0),
            c @ Err(_) => PgTypeMetadata(c),
        }
    }
}

#[cfg(feature = "postgres_backend")]
impl<T, ST> FromSql<Array<ST>, Pg> for Vec<T>
where
    T: FromSql<ST, Pg>,
{
    fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
        let mut bytes = value.as_bytes();
        let num_dimensions = bytes.read_i32::<NetworkEndian>()?;

Review comment (refactor):
As a future extension outside of this PR we can provide some helper methods to just turn this datastructure into a 2/3/4d vector. 

(That doesn't need to be part of the initial Pr, I just try to sketch up a possible API)

Code after:
use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
use core::fmt;
use std::io::Write;

use crate::deserialize::{self, FromSql, FromSqlRow};
use crate::pg::{Pg, PgTypeMetadata, PgValue};
use crate::query_builder::bind_collector::ByteWrapper;
use crate::serialize::{self, IsNull, Output, ToSql};
use crate::sql_types::{Array, HasSqlType, Nullable};

#[cfg(feature = "postgres_backend")]
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, AsExpression, FromSqlRow)]
#[diesel(sql_type = Array<T>)]
/// Postgres allows multi-dimensional arrays of at most 6 dimensions. Internally they are stored as a flattened
/// representation with the dimension information encoded in the header. This struct represents a
/// multi-dimensional array with elements of type `T` as opposed to Vec<T> which can be used for 1d-arrays.
pub struct NdArray<T> {
    /// A list that describes how many values for each dimension are returned
    pub dims: Vec<usize>,
    /// The actual data flattened to a single array
    ///
    /// This array contains values ordered by the left most dimension
    /// which means there will be dim[0] values for the first element of the second dimension
    /// followed by dim[0] values for the second element of the second dimensions
    /// and so up to dim[1] times. Afterwards that number of values is repeated for dim[2],
    /// and so for all dimensions in the dimension field above
    pub data: Vec<T>,
}

#[cfg(feature = "postgres_backend")]
impl<T> HasSqlType<Array<T>> for Pg
where
    Pg: HasSqlType<T>,
{
    fn metadata(lookup: &mut Self::MetadataLookup) -> PgTypeMetadata {
        match <Pg as HasSqlType<T>>::metadata(lookup).0 {
            Ok(tpe) => PgTypeMetadata::new(tpe.array_oid, 0),
            c @ Err(_) => PgTypeMetadata(c),
        }
    }
}