miniextendr_api/list/
named.rs1use std::collections::HashMap;
8
9use crate::from_r::{SexpError, TryFromSexp};
10use crate::into_r::IntoR;
11use crate::{SEXP, SexpExt};
12
13use super::List;
14
15pub struct NamedList {
35 list: List,
36 index: HashMap<String, usize>,
37}
38
39impl NamedList {
40 pub fn new(list: List) -> Option<Self> {
44 let names_sexp = list.names()?;
45 let n: usize = list
46 .len()
47 .try_into()
48 .expect("list length must be non-negative");
49 let mut index = HashMap::with_capacity(n);
50
51 for i in 0..n {
52 let idx: isize = i.try_into().expect("index exceeds isize::MAX");
53 let name_sexp = names_sexp.string_elt(idx);
54 if name_sexp == SEXP::na_string() {
55 continue;
56 }
57 let name_ptr = name_sexp.r_char();
58 let name_cstr = unsafe { std::ffi::CStr::from_ptr(name_ptr) };
59 if let Ok(s) = name_cstr.to_str() {
60 if !s.is_empty() {
61 index.insert(s.to_owned(), i);
62 }
63 }
64 }
65
66 Some(NamedList { list, index })
67 }
68
69 #[inline]
75 pub fn get<T>(&self, name: &str) -> Option<T>
76 where
77 T: TryFromSexp,
78 {
79 let &idx = self.index.get(name)?;
80 let idx_isize: isize = idx.try_into().ok()?;
81 let elem = self.list.as_sexp().vector_elt(idx_isize);
82 T::try_from_sexp(elem).ok()
83 }
84
85 #[inline]
87 pub fn get_raw(&self, name: &str) -> Option<SEXP> {
88 let &idx = self.index.get(name)?;
89 let idx_isize: isize = idx.try_into().ok()?;
90 Some(self.list.as_sexp().vector_elt(idx_isize))
91 }
92
93 #[inline]
97 pub fn get_index<T>(&self, idx: isize) -> Option<T>
98 where
99 T: TryFromSexp,
100 {
101 self.list.get_index(idx)
102 }
103
104 #[inline]
106 pub fn contains(&self, name: &str) -> bool {
107 self.index.contains_key(name)
108 }
109
110 #[inline]
112 pub fn len(&self) -> isize {
113 self.list.len()
114 }
115
116 #[inline]
118 pub fn is_empty(&self) -> bool {
119 self.list.is_empty()
120 }
121
122 #[inline]
124 pub fn named_len(&self) -> usize {
125 self.index.len()
126 }
127
128 #[inline]
130 pub fn as_list(&self) -> List {
131 self.list
132 }
133
134 #[inline]
136 pub fn into_list(self) -> List {
137 self.list
138 }
139
140 pub fn names(&self) -> impl Iterator<Item = &str> {
142 self.index.keys().map(|s| s.as_str())
143 }
144
145 pub fn entries(&self) -> impl Iterator<Item = (&str, usize)> {
147 self.index.iter().map(|(k, &v)| (k.as_str(), v))
148 }
149}
150
151impl IntoR for NamedList {
152 type Error = std::convert::Infallible;
153 fn try_into_sexp(self) -> Result<SEXP, Self::Error> {
154 Ok(self.into_sexp())
155 }
156 unsafe fn try_into_sexp_unchecked(self) -> Result<SEXP, Self::Error> {
157 self.try_into_sexp()
158 }
159 #[inline]
160 fn into_sexp(self) -> SEXP {
161 self.list.into_sexp()
162 }
163}
164
165impl TryFromSexp for NamedList {
166 type Error = SexpError;
167
168 fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
169 let list = List::try_from_sexp(sexp).map_err(|e| SexpError::InvalidValue(e.to_string()))?;
170 NamedList::new(list)
171 .ok_or_else(|| SexpError::InvalidValue("list has no names attribute".into()))
172 }
173}
174
175impl TryFromSexp for Option<NamedList> {
176 type Error = SexpError;
177
178 fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
179 if sexp == SEXP::nil() {
180 return Ok(None);
181 }
182 let named = NamedList::try_from_sexp(sexp)?;
183 Ok(Some(named))
184 }
185}
186
187