coda_api/ext/
parse_cell_value.rs

1use crate::types::{CellValue, RichSingleValue, RichValue, ScalarValue, Value, ValueVariant0};
2use thiserror::Error;
3
4impl TryFrom<CellValue> for String {
5    type Error = ConvertCellValueToStringError;
6
7    fn try_from(cell_value: CellValue) -> Result<Self, Self::Error> {
8        use ConvertCellValueToStringError::*;
9        match cell_value {
10            CellValue::Value(Value::Variant0(ValueVariant0::Variant0(value))) => Ok(value),
11            CellValue::RichValue(RichValue::Variant0(RichSingleValue::ScalarValue(ScalarValue::Variant0(value)))) => Ok(value),
12            cell_value => Err(InvalidCellValue {
13                cell_value,
14            }),
15        }
16    }
17}
18
19impl TryFrom<CellValue> for f64 {
20    type Error = ConvertCellValueToF64Error;
21
22    fn try_from(cell_value: CellValue) -> Result<Self, Self::Error> {
23        use ConvertCellValueToF64Error::*;
24        match cell_value {
25            CellValue::Value(Value::Variant0(ValueVariant0::Variant1(value))) => Ok(value),
26            CellValue::RichValue(RichValue::Variant0(RichSingleValue::ScalarValue(ScalarValue::Variant1(value)))) => Ok(value),
27            cell_value => Err(InvalidCellValue {
28                cell_value,
29            }),
30        }
31    }
32}
33
34/// Coda allows to "unset" the boolean value by hitting Delete while the cell is focused (the checkbox becomes greyed out). For ValueFormat != Rich, such "unset" values are returned as empty strings. During parsing, such values will be converted to `None`.
35impl TryFrom<CellValue> for Option<bool> {
36    type Error = ConvertCellValueToBoolError;
37
38    fn try_from(cell_value: CellValue) -> Result<Self, Self::Error> {
39        use ConvertCellValueToBoolError::*;
40        match cell_value {
41            CellValue::Value(Value::Variant0(ValueVariant0::Variant0(value))) if value.is_empty() => Ok(None),
42            CellValue::Value(Value::Variant0(ValueVariant0::Variant2(value))) => Ok(Some(value)),
43            CellValue::RichValue(RichValue::Variant0(RichSingleValue::ScalarValue(ScalarValue::Variant2(value)))) => Ok(Some(value)),
44            cell_value => Err(InvalidCellValue {
45                cell_value,
46            }),
47        }
48    }
49}
50
51#[cfg(feature = "time")]
52mod time_impls {
53    use super::*;
54    use crate::{DurationValueParserError, parse_duration_value};
55    use errgonomic::handle;
56    use thiserror::Error;
57    use time::format_description::well_known::Rfc3339;
58    use time::{Duration, OffsetDateTime};
59
60    impl TryFrom<CellValue> for Option<OffsetDateTime> {
61        type Error = ConvertCellValueToOptionOffsetDateTimeError;
62
63        fn try_from(value: CellValue) -> Result<Self, Self::Error> {
64            use ConvertCellValueToOptionOffsetDateTimeError::*;
65            let string = handle!(String::try_from(value), ConvertCellValueToStringFailed);
66            if string.is_empty() {
67                Ok(None)
68            } else {
69                let value = handle!(OffsetDateTime::parse(&string, &Rfc3339), OffsetDateTimeParseFailed);
70                Ok(Some(value))
71            }
72        }
73    }
74
75    impl TryFrom<CellValue> for Option<Duration> {
76        type Error = ConvertCellValueToOptionDurationError;
77
78        fn try_from(value: CellValue) -> Result<Self, Self::Error> {
79            use ConvertCellValueToOptionDurationError::{ConvertCellValueToStringFailed, DurationParseFailed};
80
81            let string = handle!(String::try_from(value), ConvertCellValueToStringFailed);
82            Ok(handle!(parse_duration_value(&string), DurationParseFailed))
83        }
84    }
85
86    #[derive(Debug, Error)]
87    pub enum ConvertCellValueToOptionDurationError {
88        #[error("failed to convert cell value to duration string: {source}")]
89        ConvertCellValueToStringFailed {
90            #[source]
91            source: ConvertCellValueToStringError,
92        },
93        #[error("failed to parse duration value: {source}")]
94        DurationParseFailed {
95            #[source]
96            source: DurationValueParserError,
97        },
98    }
99
100    #[derive(Debug, Error)]
101    pub enum ConvertCellValueToOptionOffsetDateTimeError {
102        #[error("failed to convert cell value to timestamp string: {source}")]
103        ConvertCellValueToStringFailed {
104            #[source]
105            source: ConvertCellValueToStringError,
106        },
107        #[error("failed to parse RFC3339 timestamp: {source}")]
108        OffsetDateTimeParseFailed {
109            #[source]
110            source: time::Error,
111        },
112    }
113}
114
115#[cfg(feature = "time")]
116pub use time_impls::*;
117
118#[derive(Debug, Error)]
119pub enum ConvertCellValueToStringError {
120    #[error("cell value is not a string: {cell_value:?}")]
121    InvalidCellValue { cell_value: CellValue },
122}
123
124#[derive(Debug, Error)]
125pub enum ConvertCellValueToF64Error {
126    #[error("cell value is not a number: {cell_value:?}")]
127    InvalidCellValue { cell_value: CellValue },
128}
129
130#[derive(Debug, Error)]
131pub enum ConvertCellValueToBoolError {
132    #[error("cell value is not a boolean: {cell_value:?}")]
133    InvalidCellValue { cell_value: CellValue },
134}
135
136#[derive(Debug, Error)]
137pub enum ConvertCellValueError {
138    #[error("failed to convert cell value to string: {source}")]
139    ConvertCellValueToStringFailed {
140        #[source]
141        source: ConvertCellValueToStringError,
142    },
143    #[error("failed to convert cell value to number: {source}")]
144    ConvertCellValueToF64Failed {
145        #[source]
146        source: ConvertCellValueToF64Error,
147    },
148    #[error("failed to convert cell value to boolean: {source}")]
149    ConvertCellValueToBoolFailed {
150        #[source]
151        source: ConvertCellValueToBoolError,
152    },
153    #[cfg(feature = "time")]
154    #[error("failed to convert cell value to timestamp: {source}")]
155    ConvertCellValueToOffsetDateTimeFailed {
156        #[source]
157        source: ConvertCellValueToOptionOffsetDateTimeError,
158    },
159}