Skip to main content

formualizer_sheetport/
value.rs

1use formualizer_common::LiteralValue;
2use std::collections::BTreeMap;
3
4/// Generic value container for a manifest port.
5#[derive(Debug, Clone, PartialEq)]
6pub enum PortValue {
7    Scalar(LiteralValue),
8    Record(BTreeMap<String, LiteralValue>),
9    Range(Vec<Vec<LiteralValue>>),
10    Table(TableValue),
11}
12
13impl PortValue {
14    pub fn as_scalar(&self) -> Option<&LiteralValue> {
15        match self {
16            PortValue::Scalar(value) => Some(value),
17            _ => None,
18        }
19    }
20
21    pub fn as_record(&self) -> Option<&BTreeMap<String, LiteralValue>> {
22        match self {
23            PortValue::Record(map) => Some(map),
24            _ => None,
25        }
26    }
27
28    pub fn as_range(&self) -> Option<&[Vec<LiteralValue>]> {
29        match self {
30            PortValue::Range(rows) => Some(rows),
31            _ => None,
32        }
33    }
34
35    pub fn as_table(&self) -> Option<&TableValue> {
36        match self {
37            PortValue::Table(table) => Some(table),
38            _ => None,
39        }
40    }
41
42    /// Returns true when the port carries no concrete data.
43    pub fn is_empty(&self) -> bool {
44        match self {
45            PortValue::Scalar(value) => matches!(value, LiteralValue::Empty),
46            PortValue::Record(fields) => fields
47                .values()
48                .all(|value| matches!(value, LiteralValue::Empty)),
49            PortValue::Range(rows) => {
50                if rows.is_empty() {
51                    return true;
52                }
53                rows.iter()
54                    .all(|row| row.iter().all(|cell| matches!(cell, LiteralValue::Empty)))
55            }
56            PortValue::Table(table) => table.is_empty(),
57        }
58    }
59}
60
61/// Table-shaped value consisting of ordered rows.
62#[derive(Debug, Clone, PartialEq, Default)]
63pub struct TableValue {
64    pub rows: Vec<TableRow>,
65}
66
67impl TableValue {
68    pub fn new(rows: Vec<TableRow>) -> Self {
69        Self { rows }
70    }
71
72    pub fn is_empty(&self) -> bool {
73        self.rows.is_empty() || self.rows.iter().all(TableRow::is_empty)
74    }
75}
76
77/// Single logical table row.
78#[derive(Debug, Clone, PartialEq, Default)]
79pub struct TableRow {
80    pub values: BTreeMap<String, LiteralValue>,
81}
82
83impl TableRow {
84    pub fn new(values: BTreeMap<String, LiteralValue>) -> Self {
85        Self { values }
86    }
87
88    pub fn get(&self, column: &str) -> Option<&LiteralValue> {
89        self.values.get(column)
90    }
91
92    pub fn is_empty(&self) -> bool {
93        self.values
94            .values()
95            .all(|value| matches!(value, LiteralValue::Empty))
96    }
97}
98
99/// Snapshot of current input values keyed by port id.
100#[derive(Debug, Clone, PartialEq, Default)]
101pub struct InputSnapshot(pub BTreeMap<String, PortValue>);
102
103impl InputSnapshot {
104    pub fn new(map: BTreeMap<String, PortValue>) -> Self {
105        Self(map)
106    }
107
108    pub fn inner(&self) -> &BTreeMap<String, PortValue> {
109        &self.0
110    }
111
112    pub fn into_inner(self) -> BTreeMap<String, PortValue> {
113        self.0
114    }
115
116    pub fn to_update(&self) -> InputUpdate {
117        InputUpdate(self.0.clone())
118    }
119
120    pub fn get(&self, id: &str) -> Option<&PortValue> {
121        self.0.get(id)
122    }
123}
124
125/// Snapshot of outputs keyed by port id.
126#[derive(Debug, Clone, PartialEq, Default)]
127pub struct OutputSnapshot(pub BTreeMap<String, PortValue>);
128
129impl OutputSnapshot {
130    pub fn new(map: BTreeMap<String, PortValue>) -> Self {
131        Self(map)
132    }
133
134    pub fn inner(&self) -> &BTreeMap<String, PortValue> {
135        &self.0
136    }
137
138    pub fn into_inner(self) -> BTreeMap<String, PortValue> {
139        self.0
140    }
141
142    pub fn get(&self, id: &str) -> Option<&PortValue> {
143        self.0.get(id)
144    }
145}
146
147/// Set of inputs to apply (partial updates allowed).
148#[derive(Debug, Clone, PartialEq, Default)]
149pub struct InputUpdate(pub BTreeMap<String, PortValue>);
150
151impl InputUpdate {
152    pub fn new() -> Self {
153        Self(BTreeMap::new())
154    }
155
156    pub fn insert(&mut self, id: impl Into<String>, value: PortValue) {
157        self.0.insert(id.into(), value);
158    }
159
160    pub fn into_inner(self) -> BTreeMap<String, PortValue> {
161        self.0
162    }
163
164    pub fn is_empty(&self) -> bool {
165        self.0.is_empty()
166    }
167}