extendr_api/wrapper/dataframe.rs
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
//! This provides an abstraction for R's `data.frame`-constructor in Rust.
//! For a given `struct` say `CustomRow`, one may implement or derive [`IntoDataFrameRow`],
//! thus being able to convert `Vec<CustomRow>` to an instance of `Dataframe<CustomRow>`,
//! see [`Dataframe`].
//!
//!
//! [`IntoDataFrameRow`]: ::extendr_macros::IntoDataFrameRow
//!
use super::*;
/// A trait to convert a collection of `IntoDataFrameRow` into
/// [`Dataframe`]. Typical usage involves using the derive-macro [`IntoDataFrameRow`]
/// on a struct, which would generate `impl IntoDataframe<T> for Vec<T>`.
///
/// [`IntoDataFrameRow`]: ::extendr_macros::IntoDataFrameRow
pub trait IntoDataFrameRow<T> {
fn into_dataframe(self) -> Result<Dataframe<T>>;
}
/// A representation of a typed `data.frame`
///
/// A `data.frame` can be created from Rust by using the [`IntoDataFrameRow`] trait
/// which can be derived for a single `struct` that represents a single row.
/// The type of the row is captured by the marker `T`.
///
/// Note that at present, you can create a `Dataframe<T>` but you cannot extract
/// `T` from the object. `<T>` is purely a marker that indicates the struct that
/// was used to create its rows.
///
/// As a result, using `Dataframe<T>` as a function argument _will not_ perform
/// any type checking on the type.
#[derive(PartialEq, Clone)]
pub struct Dataframe<T> {
pub(crate) robj: Robj,
_marker: std::marker::PhantomData<T>,
}
impl<T> From<Dataframe<T>> for Robj {
fn from(value: Dataframe<T>) -> Self {
value.robj
}
}
impl<T> std::convert::TryFrom<&Robj> for Dataframe<T> {
type Error = Error;
fn try_from(robj: &Robj) -> Result<Self> {
// TODO: check type using derived trait.
if !(robj.is_list() && robj.inherits("data.frame")) {
return Err(Error::ExpectedDataframe(robj.clone()));
}
Ok(Dataframe {
robj: robj.clone(),
_marker: std::marker::PhantomData,
})
}
}
impl<T> std::convert::TryFrom<Robj> for Dataframe<T> {
type Error = Error;
fn try_from(robj: Robj) -> Result<Self> {
(&robj).try_into()
}
}
impl<T> Dataframe<T> {
/// Use `#[derive(IntoDataFrameRow)]` to use this.
pub fn try_from_values<I: IntoDataFrameRow<T>>(iter: I) -> Result<Self> {
iter.into_dataframe()
}
}
impl<T> Attributes for Dataframe<T> {}
impl<T> std::fmt::Debug for Dataframe<T>
where
T: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"dataframe!({})",
self.as_list()
.unwrap()
.iter()
.map(|(k, v)| if !k.is_empty() {
format!("{}={:?}", k, v)
} else {
format!("{:?}", v)
})
.collect::<Vec<_>>()
.join(", ")
)
}
}
impl<T> From<Option<Dataframe<T>>> for Robj {
fn from(value: Option<Dataframe<T>>) -> Self {
match value {
None => nil_value(),
Some(value) => value.into(),
}
}
}