1use super::{
2 ValueKindName, ViewError, decode_required, duplicate, invalid_dimensions, missing,
3 unexpected_length, unexpected_type,
4};
5use crate::{RObject, RStr, RValue};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct PackagesMatrix {
10 nrow: usize,
11 column_names: Vec<String>,
12 cells: Vec<Option<String>>,
13}
14
15impl PackagesMatrix {
16 pub fn from_object(object: &RObject) -> Result<Self, ViewError> {
18 let values = match &object.value() {
19 RValue::Character(values) => values,
20 value => {
21 return Err(unexpected_type(
22 "PACKAGES",
23 None,
24 "character vector",
25 value.kind_name(),
26 ));
27 }
28 };
29 let dimensions = object
30 .attributes()
31 .get("dim")
32 .ok_or_else(|| missing("PACKAGES.attributes.dim", None))?;
33 let dimensions = match &dimensions.value() {
34 RValue::Integer(values) => values,
35 value => {
36 return Err(unexpected_type(
37 "PACKAGES.attributes.dim",
38 None,
39 "integer vector",
40 value.kind_name(),
41 ));
42 }
43 };
44 if dimensions.len() != 2 {
45 return Err(unexpected_length(
46 "PACKAGES.attributes.dim",
47 None,
48 "2".to_owned(),
49 dimensions.len(),
50 ));
51 }
52 let mut shape = [0usize; 2];
53 for (index, value) in dimensions.iter().enumerate() {
54 let Some(value) = value else {
55 return Err(invalid_dimensions(
56 "PACKAGES.attributes.dim",
57 "dimensions must not contain NA",
58 ));
59 };
60 if *value < 0 {
61 return Err(invalid_dimensions(
62 "PACKAGES.attributes.dim",
63 "dimensions must not be negative",
64 ));
65 }
66 shape[index] = *value as usize;
67 }
68 let element_count = shape[0].checked_mul(shape[1]).ok_or_else(|| {
69 invalid_dimensions("PACKAGES.attributes.dim", "dimension product overflows")
70 })?;
71 if values.len() != element_count {
72 return Err(unexpected_length(
73 "PACKAGES",
74 None,
75 element_count.to_string(),
76 values.len(),
77 ));
78 }
79
80 let dimnames = object
81 .attributes()
82 .get("dimnames")
83 .ok_or_else(|| missing("PACKAGES.attributes.dimnames", None))?;
84 let dimnames = match &dimnames.value() {
85 RValue::List(values) => values,
86 value => {
87 return Err(unexpected_type(
88 "PACKAGES.attributes.dimnames",
89 None,
90 "list",
91 value.kind_name(),
92 ));
93 }
94 };
95 if dimnames.len() != 2 {
96 return Err(unexpected_length(
97 "PACKAGES.attributes.dimnames",
98 None,
99 "2".to_owned(),
100 dimnames.len(),
101 ));
102 }
103 validate_row_names(&dimnames[0], shape[0])?;
104 let column_values = match &dimnames[1].value() {
105 RValue::Character(values) => values,
106 value => {
107 return Err(unexpected_type(
108 "PACKAGES.attributes.dimnames[1]",
109 None,
110 "character vector",
111 value.kind_name(),
112 ));
113 }
114 };
115 if column_values.len() != shape[1] {
116 return Err(unexpected_length(
117 "PACKAGES.attributes.dimnames[1]",
118 None,
119 shape[1].to_string(),
120 column_values.len(),
121 ));
122 }
123 let mut column_names = Vec::with_capacity(shape[1]);
124 let mut seen_names = std::collections::BTreeSet::new();
125 for (index, value) in column_values.iter().enumerate() {
126 let name = decode_required(
127 value,
128 &format!("PACKAGES.attributes.dimnames[1][{index}]"),
129 None,
130 )?;
131 if !seen_names.insert(name.clone()) {
132 return Err(duplicate(
133 &format!("PACKAGES.attributes.dimnames[1][{index}]"),
134 Some(name),
135 ));
136 }
137 column_names.push(name);
138 }
139
140 let mut cells = Vec::with_capacity(element_count);
141 for row in 0..shape[0] {
142 for column in 0..shape[1] {
143 cells.push(decode_matrix_cell(
144 &values[row + column * shape[0]],
145 row,
146 &column_names[column],
147 )?);
148 }
149 }
150 Ok(Self {
151 nrow: shape[0],
152 column_names,
153 cells,
154 })
155 }
156
157 pub fn len(&self) -> usize {
158 self.nrow
159 }
160 pub fn is_empty(&self) -> bool {
161 self.nrow == 0
162 }
163 pub fn column_names(&self) -> impl ExactSizeIterator<Item = &str> + '_ {
164 self.column_names.iter().map(String::as_str)
165 }
166 pub fn column(&self, name: &str) -> Option<PackagesColumn<'_>> {
167 self.column_names
168 .iter()
169 .position(|column| column == name)
170 .map(|index| PackagesColumn {
171 matrix: self,
172 index,
173 })
174 }
175 pub fn row(&self, index: usize) -> Option<PackagesRow<'_>> {
176 (index < self.nrow).then_some(PackagesRow {
177 matrix: self,
178 index,
179 })
180 }
181 pub fn rows(&self) -> impl ExactSizeIterator<Item = PackagesRow<'_>> + '_ {
182 (0..self.nrow).map(|index| PackagesRow {
183 matrix: self,
184 index,
185 })
186 }
187}
188
189impl TryFrom<&RObject> for PackagesMatrix {
190 type Error = ViewError;
191 fn try_from(value: &RObject) -> Result<Self, Self::Error> {
192 Self::from_object(value)
193 }
194}
195
196#[derive(Debug, Clone, Copy)]
198pub struct PackagesRow<'a> {
199 matrix: &'a PackagesMatrix,
200 index: usize,
201}
202
203impl<'a> PackagesRow<'a> {
204 pub fn index(&self) -> usize {
205 self.index
206 }
207 pub fn get(&self, column: &str) -> Option<Option<&'a str>> {
208 let column = self
209 .matrix
210 .column_names
211 .iter()
212 .position(|name| name == column)?;
213 Some(self.matrix.cells[self.index * self.matrix.column_names.len() + column].as_deref())
214 }
215}
216
217#[derive(Debug, Clone, Copy)]
219pub struct PackagesColumn<'a> {
220 matrix: &'a PackagesMatrix,
221 index: usize,
222}
223
224impl<'a> PackagesColumn<'a> {
225 pub fn name(&self) -> &str {
226 &self.matrix.column_names[self.index]
227 }
228 pub fn len(&self) -> usize {
229 self.matrix.nrow
230 }
231 pub fn is_empty(&self) -> bool {
232 self.matrix.is_empty()
233 }
234 pub fn get(&self, row: usize) -> Option<Option<&'a str>> {
235 (row < self.matrix.nrow).then(|| {
236 self.matrix.cells[row * self.matrix.column_names.len() + self.index].as_deref()
237 })
238 }
239}
240
241fn validate_row_names(object: &RObject, expected: usize) -> Result<(), ViewError> {
242 let values = match &object.value() {
243 RValue::Null => return Ok(()),
244 RValue::Character(values) => values,
245 value => {
246 return Err(unexpected_type(
247 "PACKAGES.attributes.dimnames[0]",
248 None,
249 "character vector",
250 value.kind_name(),
251 ));
252 }
253 };
254 if values.len() != expected {
255 return Err(unexpected_length(
256 "PACKAGES.attributes.dimnames[0]",
257 None,
258 expected.to_string(),
259 values.len(),
260 ));
261 }
262 for (index, value) in values.iter().enumerate() {
263 if let Some(Err(_)) = value.as_str() {
264 return Err(ViewError::InvalidStringEncoding {
265 path: format!("PACKAGES.attributes.dimnames[0][{index}]"),
266 field: None,
267 row: None,
268 column: None,
269 });
270 }
271 }
272 Ok(())
273}
274
275fn decode_matrix_cell(value: &RStr, row: usize, column: &str) -> Result<Option<String>, ViewError> {
276 match value.as_str() {
277 None => Ok(None),
278 Some(Ok(value)) => Ok(Some(value.into_owned())),
279 Some(Err(_)) => Err(ViewError::InvalidStringEncoding {
280 path: format!("PACKAGES[row={row},column=\"{column}\"]"),
281 field: Some(column.to_owned()),
282 row: Some(row),
283 column: Some(column.to_owned()),
284 }),
285 }
286}