miniextendr_api/list/
named.rs1use std::collections::HashMap;
8
9use crate::ffi::{SEXP, SexpExt};
10use crate::from_r::{SexpError, TryFromSexp};
11use crate::into_r::IntoR;
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]
73 pub fn get<T>(&self, name: &str) -> Option<T>
74 where
75 T: TryFromSexp<Error = SexpError>,
76 {
77 let &idx = self.index.get(name)?;
78 let idx_isize: isize = idx.try_into().ok()?;
79 let elem = self.list.as_sexp().vector_elt(idx_isize);
80 T::try_from_sexp(elem).ok()
81 }
82
83 #[inline]
85 pub fn get_raw(&self, name: &str) -> Option<SEXP> {
86 let &idx = self.index.get(name)?;
87 let idx_isize: isize = idx.try_into().ok()?;
88 Some(self.list.as_sexp().vector_elt(idx_isize))
89 }
90
91 #[inline]
95 pub fn get_index<T>(&self, idx: isize) -> Option<T>
96 where
97 T: TryFromSexp<Error = SexpError>,
98 {
99 self.list.get_index(idx)
100 }
101
102 #[inline]
104 pub fn contains(&self, name: &str) -> bool {
105 self.index.contains_key(name)
106 }
107
108 #[inline]
110 pub fn len(&self) -> isize {
111 self.list.len()
112 }
113
114 #[inline]
116 pub fn is_empty(&self) -> bool {
117 self.list.is_empty()
118 }
119
120 #[inline]
122 pub fn named_len(&self) -> usize {
123 self.index.len()
124 }
125
126 #[inline]
128 pub fn as_list(&self) -> List {
129 self.list
130 }
131
132 #[inline]
134 pub fn into_list(self) -> List {
135 self.list
136 }
137
138 pub fn names(&self) -> impl Iterator<Item = &str> {
140 self.index.keys().map(|s| s.as_str())
141 }
142
143 pub fn entries(&self) -> impl Iterator<Item = (&str, usize)> {
145 self.index.iter().map(|(k, &v)| (k.as_str(), v))
146 }
147}
148
149impl IntoR for NamedList {
150 type Error = std::convert::Infallible;
151 fn try_into_sexp(self) -> Result<SEXP, Self::Error> {
152 Ok(self.into_sexp())
153 }
154 unsafe fn try_into_sexp_unchecked(self) -> Result<SEXP, Self::Error> {
155 self.try_into_sexp()
156 }
157 #[inline]
158 fn into_sexp(self) -> SEXP {
159 self.list.into_sexp()
160 }
161}
162
163impl TryFromSexp for NamedList {
164 type Error = SexpError;
165
166 fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
167 let list = List::try_from_sexp(sexp).map_err(|e| SexpError::InvalidValue(e.to_string()))?;
168 NamedList::new(list)
169 .ok_or_else(|| SexpError::InvalidValue("list has no names attribute".into()))
170 }
171}
172
173impl TryFromSexp for Option<NamedList> {
174 type Error = SexpError;
175
176 fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
177 if sexp == SEXP::nil() {
178 return Ok(None);
179 }
180 let named = NamedList::try_from_sexp(sexp)?;
181 Ok(Some(named))
182 }
183}
184
185