neon/types_impl/extract/
array.rs1use std::{error, fmt, mem::MaybeUninit};
2
3use crate::{
4 context::{internal::ContextInternal, Context, Cx},
5 handle::Handle,
6 result::{JsResult, NeonResult, Throw},
7 sys,
8 types::{
9 extract::{private, TryFromJs, TryIntoJs, TypeExpected},
10 private::ValueInternal,
11 JsArray, JsValue,
12 },
13};
14
15pub struct Array<T>(pub T);
44
45impl<T> private::Sealed for Array<T> {}
46
47impl<'cx, T> TryFromJs<'cx> for Array<T>
48where
49 T: FromIterator<T::Item>,
50 T: IntoIterator,
51 T::Item: TryFromJs<'cx>,
52{
53 type Error = ArrayError<<T::Item as TryFromJs<'cx>>::Error>;
54
55 fn try_from_js(
56 cx: &mut Cx<'cx>,
57 v: Handle<'cx, JsValue>,
58 ) -> NeonResult<Result<Self, Self::Error>> {
59 let env = cx.env().to_raw();
60 let v = v.to_local();
61 let len = unsafe {
62 let mut len = 0;
63
64 match sys::get_array_length(env, v, &mut len) {
65 Err(sys::Status::PendingException) => return Err(Throw::new()),
66 Err(sys::Status::ArrayExpected) => return Ok(Err(ArrayError::array())),
67 res => res.unwrap(),
68 }
69
70 len
71 };
72
73 (0..len)
74 .map(|i| {
75 let item = unsafe {
76 let mut item = MaybeUninit::uninit();
77
78 match sys::get_element(env, v, i, item.as_mut_ptr()) {
79 Err(sys::Status::PendingException) => return Err(Throw::new()),
80 res => res.unwrap(),
81 }
82
83 Handle::new_internal(JsValue::from_local(cx.env(), item.assume_init()))
84 };
85
86 match T::Item::try_from_js(cx, item) {
87 Ok(Ok(item)) => Ok(Ok(item)),
88 Ok(Err(err)) => Ok(Err(ArrayError::item(err))),
89 Err(err) => Err(err),
90 }
91 })
92 .collect::<Result<Result<T, Self::Error>, Throw>>()
93 .map(|v| v.map(Array))
94 }
95}
96
97impl<'cx, T> TryIntoJs<'cx> for Array<T>
98where
99 T: IntoIterator,
100 T::Item: TryIntoJs<'cx>,
101{
102 type Value = JsArray;
103
104 fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> {
105 let iter = self.0.into_iter();
106 let env = cx.env().to_raw();
107 let (len, _) = iter.size_hint();
108 let arr = unsafe {
109 let mut arr = MaybeUninit::uninit();
110
111 match sys::create_array_with_length(env, len, arr.as_mut_ptr()) {
112 Err(sys::Status::PendingException) => return Err(Throw::new()),
113 res => res.unwrap(),
114 }
115
116 arr.assume_init()
117 };
118
119 for (i, item) in iter.enumerate() {
120 let item = item.try_into_js(cx)?.to_local();
121 let Ok(i) = u32::try_from(i) else {
122 return cx.throw_error("Exceeded maximum length of an array");
123 };
124
125 unsafe {
126 match sys::set_element(env, arr, i, item) {
127 Err(sys::Status::PendingException) => return Err(Throw::new()),
128 res => res.unwrap(),
129 }
130 }
131 }
132
133 unsafe { Ok(Handle::new_internal(JsArray::from_local(cx.env(), arr))) }
134 }
135}
136
137#[derive(Debug)]
139pub enum ArrayError<E> {
140 Array(TypeExpected<JsArray>),
142 Item(E),
144}
145
146impl<E> ArrayError<E> {
147 fn array() -> Self {
148 Self::Array(TypeExpected::<JsArray>::new())
149 }
150
151 fn item(err: E) -> Self {
152 Self::Item(err)
153 }
154}
155
156impl<E> fmt::Display for ArrayError<E>
157where
158 E: fmt::Display,
159{
160 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
161 match self {
162 ArrayError::Array(err) => write!(f, "{}", err),
163 ArrayError::Item(err) => write!(f, "{}", err),
164 }
165 }
166}
167
168impl<E> error::Error for ArrayError<E> where E: error::Error {}
169
170impl<'cx, E> TryIntoJs<'cx> for ArrayError<E>
171where
172 E: TryIntoJs<'cx>,
173{
174 type Value = JsValue;
175
176 fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> {
177 match self {
178 ArrayError::Array(err) => err.try_into_js(cx).map(|v| v.upcast()),
179 ArrayError::Item(err) => err.try_into_js(cx).map(|v| v.upcast()),
180 }
181 }
182}
183
184impl<'cx, E> private::Sealed for ArrayError<E> where E: TryIntoJs<'cx> {}