extendr_api/wrapper/
dataframe.rs

1//! This provides an abstraction for R's `data.frame`-constructor in Rust.
2//! For a given `struct` say `CustomRow`, one may implement or derive [`IntoDataFrameRow`],
3//! thus being able to convert `Vec<CustomRow>` to an instance of `Dataframe<CustomRow>`,
4//! see [`Dataframe`].
5//!
6//!
7//! [`IntoDataFrameRow`]: ::extendr_macros::IntoDataFrameRow
8//!
9use super::*;
10
11/// A trait to convert a collection of `IntoDataFrameRow` into
12/// [`Dataframe`]. Typical usage involves using the derive-macro [`IntoDataFrameRow`]
13/// on a struct, which would generate `impl IntoDataframe<T> for Vec<T>`.
14///
15/// [`IntoDataFrameRow`]: ::extendr_macros::IntoDataFrameRow
16pub trait IntoDataFrameRow<T> {
17    fn into_dataframe(self) -> Result<Dataframe<T>>;
18}
19
20/// A representation of a typed `data.frame`
21///
22/// A `data.frame` can be created from Rust by using the [`IntoDataFrameRow`] trait
23/// which can be derived for a single `struct` that represents a single row.
24/// The type of the row is captured by the marker `T`.
25///
26/// Note that at present, you can create a `Dataframe<T>` but you cannot extract
27/// `T` from the object. `<T>` is purely a marker that indicates the struct that
28/// was used to create its rows.
29///
30/// As a result, using `Dataframe<T>` as a function argument _will not_ perform
31/// any type checking on the type.
32#[derive(PartialEq, Clone)]
33pub struct Dataframe<T> {
34    pub(crate) robj: Robj,
35    _marker: std::marker::PhantomData<T>,
36}
37
38impl<T> From<Dataframe<T>> for Robj {
39    fn from(value: Dataframe<T>) -> Self {
40        value.robj
41    }
42}
43
44impl<T> std::convert::TryFrom<&Robj> for Dataframe<T> {
45    type Error = Error;
46    fn try_from(robj: &Robj) -> Result<Self> {
47        // TODO: check type using derived trait.
48        if !(robj.is_list() && robj.inherits("data.frame")) {
49            return Err(Error::ExpectedDataframe(robj.clone()));
50        }
51        Ok(Dataframe {
52            robj: robj.clone(),
53            _marker: std::marker::PhantomData,
54        })
55    }
56}
57
58impl<T> std::convert::TryFrom<Robj> for Dataframe<T> {
59    type Error = Error;
60    fn try_from(robj: Robj) -> Result<Self> {
61        (&robj).try_into()
62    }
63}
64
65impl<T> Dataframe<T> {
66    /// Use `#[derive(IntoDataFrameRow)]` to use this.
67    pub fn try_from_values<I: IntoDataFrameRow<T>>(iter: I) -> Result<Self> {
68        iter.into_dataframe()
69    }
70}
71
72impl<T> Attributes for Dataframe<T> {}
73
74impl<T> std::fmt::Debug for Dataframe<T>
75where
76    T: std::fmt::Debug,
77{
78    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79        write!(
80            f,
81            "dataframe!({})",
82            self.as_list()
83                .unwrap()
84                .iter()
85                .map(|(k, v)| if !k.is_empty() {
86                    format!("{}={:?}", k, v)
87                } else {
88                    format!("{:?}", v)
89                })
90                .collect::<Vec<_>>()
91                .join(", ")
92        )
93    }
94}
95
96impl<T> From<Option<Dataframe<T>>> for Robj {
97    fn from(value: Option<Dataframe<T>>) -> Self {
98        match value {
99            None => nil_value(),
100            Some(value) => value.into(),
101        }
102    }
103}