1#![allow(non_upper_case_globals)]
2
3use std::ffi::CStr;
22use std::fmt;
23use std::os::raw::c_char;
24
25use illumos_nvpair_sys::{
26 data_type_t, data_type_t_DATA_TYPE_BOOLEAN, data_type_t_DATA_TYPE_BOOLEAN_ARRAY,
27 data_type_t_DATA_TYPE_BOOLEAN_VALUE, data_type_t_DATA_TYPE_BYTE,
28 data_type_t_DATA_TYPE_BYTE_ARRAY, data_type_t_DATA_TYPE_DOUBLE, data_type_t_DATA_TYPE_HRTIME,
29 data_type_t_DATA_TYPE_INT8, data_type_t_DATA_TYPE_INT8_ARRAY, data_type_t_DATA_TYPE_INT16,
30 data_type_t_DATA_TYPE_INT16_ARRAY, data_type_t_DATA_TYPE_INT32,
31 data_type_t_DATA_TYPE_INT32_ARRAY, data_type_t_DATA_TYPE_INT64,
32 data_type_t_DATA_TYPE_INT64_ARRAY, data_type_t_DATA_TYPE_NVLIST,
33 data_type_t_DATA_TYPE_NVLIST_ARRAY, data_type_t_DATA_TYPE_STRING,
34 data_type_t_DATA_TYPE_STRING_ARRAY, data_type_t_DATA_TYPE_UINT8,
35 data_type_t_DATA_TYPE_UINT8_ARRAY, data_type_t_DATA_TYPE_UINT16,
36 data_type_t_DATA_TYPE_UINT16_ARRAY, data_type_t_DATA_TYPE_UINT32,
37 data_type_t_DATA_TYPE_UINT32_ARRAY, data_type_t_DATA_TYPE_UINT64,
38 data_type_t_DATA_TYPE_UINT64_ARRAY, nvlist_next_nvpair, nvlist_t, nvpair_name, nvpair_t,
39 nvpair_type, nvpair_value_boolean_array, nvpair_value_boolean_value, nvpair_value_byte,
40 nvpair_value_byte_array, nvpair_value_double, nvpair_value_hrtime, nvpair_value_int8,
41 nvpair_value_int8_array, nvpair_value_int16, nvpair_value_int16_array, nvpair_value_int32,
42 nvpair_value_int32_array, nvpair_value_int64, nvpair_value_int64_array, nvpair_value_nvlist,
43 nvpair_value_nvlist_array, nvpair_value_string, nvpair_value_string_array, nvpair_value_uint8,
44 nvpair_value_uint8_array, nvpair_value_uint16, nvpair_value_uint16_array, nvpair_value_uint32,
45 nvpair_value_uint32_array, nvpair_value_uint64, nvpair_value_uint64_array, uint_t,
46};
47
48#[derive(Debug, Clone, PartialEq)]
50pub enum NvError {
51 ValueReadFailed {
53 pair_name: String,
54 type_code: data_type_t,
55 errno: i32,
56 },
57 NullPointer {
59 pair_name: String,
60 type_code: data_type_t,
61 },
62 NullName,
64}
65
66impl fmt::Display for NvError {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 match self {
69 NvError::ValueReadFailed {
70 pair_name,
71 type_code,
72 errno,
73 } => {
74 write!(
75 f,
76 "nvpair_value failed for pair {:?} (type {type_code}): errno {errno}",
77 pair_name,
78 )
79 }
80 NvError::NullPointer {
81 pair_name,
82 type_code,
83 } => {
84 write!(
85 f,
86 "nvpair_value returned null for pair {:?} (type {type_code})",
87 pair_name,
88 )
89 }
90 NvError::NullName => {
91 write!(f, "nvpair_name returned null")
92 }
93 }
94 }
95}
96
97impl std::error::Error for NvError {}
98
99#[derive(Debug, Clone, PartialEq)]
101pub struct NvList {
102 pairs: Vec<(String, NvValue)>,
103}
104
105impl NvList {
106 pub unsafe fn from_raw(nvl: *mut nvlist_t) -> Result<NvList, NvError> {
119 let mut pairs = Vec::new();
120 let mut nvp: *mut nvpair_t = std::ptr::null_mut();
121
122 loop {
123 nvp = unsafe { nvlist_next_nvpair(nvl, nvp) };
124 if nvp.is_null() {
125 break;
126 }
127
128 let name_ptr = unsafe { nvpair_name(nvp) };
129 if name_ptr.is_null() {
130 return Err(NvError::NullName);
131 }
132 let name = unsafe { CStr::from_ptr(name_ptr).to_string_lossy().into_owned() };
133 let dtype = unsafe { nvpair_type(nvp) };
134 let value = unsafe { read_pair_value(nvp, &name, dtype)? };
135 pairs.push((name, value));
136 }
137
138 Ok(NvList { pairs })
139 }
140
141 pub fn lookup(&self, name: &str) -> Option<&NvValue> {
143 self.pairs.iter().find(|(n, _)| n == name).map(|(_, v)| v)
144 }
145
146 pub fn iter(&self) -> impl Iterator<Item = (&str, &NvValue)> {
148 self.pairs.iter().map(|(n, v)| (n.as_str(), v))
149 }
150
151 pub fn len(&self) -> usize {
153 self.pairs.len()
154 }
155
156 pub fn is_empty(&self) -> bool {
158 self.pairs.is_empty()
159 }
160}
161
162impl IntoIterator for NvList {
163 type Item = (String, NvValue);
164 type IntoIter = std::vec::IntoIter<(String, NvValue)>;
165
166 fn into_iter(self) -> Self::IntoIter {
167 self.pairs.into_iter()
168 }
169}
170
171impl<'a> IntoIterator for &'a NvList {
172 type Item = (&'a str, &'a NvValue);
173 type IntoIter = std::iter::Map<
174 std::slice::Iter<'a, (String, NvValue)>,
175 fn(&'a (String, NvValue)) -> (&'a str, &'a NvValue),
176 >;
177
178 fn into_iter(self) -> Self::IntoIter {
179 self.pairs.iter().map(|(n, v)| (n.as_str(), v))
180 }
181}
182
183#[derive(Debug, Clone, PartialEq)]
185pub enum NvValue {
186 Boolean,
188 BooleanValue(bool),
190 Byte(u8),
191 Int8(i8),
192 UInt8(u8),
193 Int16(i16),
194 UInt16(u16),
195 Int32(i32),
196 UInt32(u32),
197 Int64(i64),
198 UInt64(u64),
199 Double(f64),
200 String(String),
201 Hrtime(i64),
203 NvList(NvList),
204 BooleanArray(Vec<bool>),
205 ByteArray(Vec<u8>),
206 Int8Array(Vec<i8>),
207 UInt8Array(Vec<u8>),
208 Int16Array(Vec<i16>),
209 UInt16Array(Vec<u16>),
210 Int32Array(Vec<i32>),
211 UInt32Array(Vec<u32>),
212 Int64Array(Vec<i64>),
213 UInt64Array(Vec<u64>),
214 StringArray(Vec<String>),
215 NvListArray(Vec<NvList>),
216 Unknown {
218 type_code: data_type_t,
219 },
220}
221
222
223fn check_rc(rc: i32, pair_name: &str, type_code: data_type_t) -> Result<(), NvError> {
226 if rc != 0 {
227 Err(NvError::ValueReadFailed {
228 pair_name: pair_name.to_owned(),
229 type_code,
230 errno: rc,
231 })
232 } else {
233 Ok(())
234 }
235}
236
237unsafe fn array_to_vec<T: Clone>(
247 p: *const T,
248 n: uint_t,
249 pair_name: &str,
250 type_code: data_type_t,
251) -> Result<Vec<T>, NvError> {
252 unsafe {
253 let len = n as usize;
254 if len == 0 {
255 return Ok(Vec::new());
256 }
257 if p.is_null() {
258 return Err(NvError::NullPointer {
259 pair_name: pair_name.to_owned(),
260 type_code,
261 });
262 }
263 Ok(std::slice::from_raw_parts(p, len).to_vec())
264 }
265}
266
267unsafe fn read_pair_value(
268 nvp: *mut nvpair_t,
269 pair_name: &str,
270 dtype: data_type_t,
271) -> Result<NvValue, NvError> {
272 unsafe {
273 match dtype {
274 data_type_t_DATA_TYPE_BOOLEAN => Ok(NvValue::Boolean),
275 data_type_t_DATA_TYPE_BOOLEAN_VALUE => {
276 let mut v: illumos_nvpair_sys::boolean_t = 0;
277 check_rc(nvpair_value_boolean_value(nvp, &mut v), pair_name, dtype)?;
278 Ok(NvValue::BooleanValue(v != 0))
279 }
280 data_type_t_DATA_TYPE_BYTE => {
281 let mut v: illumos_nvpair_sys::uchar_t = 0;
282 check_rc(nvpair_value_byte(nvp, &mut v), pair_name, dtype)?;
283 Ok(NvValue::Byte(v))
284 }
285 data_type_t_DATA_TYPE_INT8 => {
286 let mut v: i8 = 0;
287 check_rc(nvpair_value_int8(nvp, &mut v), pair_name, dtype)?;
288 Ok(NvValue::Int8(v))
289 }
290 data_type_t_DATA_TYPE_UINT8 => {
291 let mut v: u8 = 0;
292 check_rc(nvpair_value_uint8(nvp, &mut v), pair_name, dtype)?;
293 Ok(NvValue::UInt8(v))
294 }
295 data_type_t_DATA_TYPE_INT16 => {
296 let mut v: i16 = 0;
297 check_rc(nvpair_value_int16(nvp, &mut v), pair_name, dtype)?;
298 Ok(NvValue::Int16(v))
299 }
300 data_type_t_DATA_TYPE_UINT16 => {
301 let mut v: u16 = 0;
302 check_rc(nvpair_value_uint16(nvp, &mut v), pair_name, dtype)?;
303 Ok(NvValue::UInt16(v))
304 }
305 data_type_t_DATA_TYPE_INT32 => {
306 let mut v: i32 = 0;
307 check_rc(nvpair_value_int32(nvp, &mut v), pair_name, dtype)?;
308 Ok(NvValue::Int32(v))
309 }
310 data_type_t_DATA_TYPE_UINT32 => {
311 let mut v: u32 = 0;
312 check_rc(nvpair_value_uint32(nvp, &mut v), pair_name, dtype)?;
313 Ok(NvValue::UInt32(v))
314 }
315 data_type_t_DATA_TYPE_INT64 => {
316 let mut v: i64 = 0;
317 check_rc(nvpair_value_int64(nvp, &mut v), pair_name, dtype)?;
318 Ok(NvValue::Int64(v))
319 }
320 data_type_t_DATA_TYPE_UINT64 => {
321 let mut v: u64 = 0;
322 check_rc(nvpair_value_uint64(nvp, &mut v), pair_name, dtype)?;
323 Ok(NvValue::UInt64(v))
324 }
325 data_type_t_DATA_TYPE_DOUBLE => {
326 let mut v: f64 = 0.0;
327 check_rc(nvpair_value_double(nvp, &mut v), pair_name, dtype)?;
328 Ok(NvValue::Double(v))
329 }
330 data_type_t_DATA_TYPE_STRING => {
331 let mut p: *mut c_char = std::ptr::null_mut();
332 check_rc(nvpair_value_string(nvp, &mut p), pair_name, dtype)?;
333 if p.is_null() {
334 return Err(NvError::NullPointer {
335 pair_name: pair_name.to_owned(),
336 type_code: dtype,
337 });
338 }
339 let s = CStr::from_ptr(p).to_string_lossy().into_owned();
340 Ok(NvValue::String(s))
341 }
342 data_type_t_DATA_TYPE_HRTIME => {
343 let mut v: illumos_nvpair_sys::hrtime_t = 0;
344 check_rc(nvpair_value_hrtime(nvp, &mut v), pair_name, dtype)?;
345 Ok(NvValue::Hrtime(v))
346 }
347 data_type_t_DATA_TYPE_NVLIST => {
348 let mut p: *mut nvlist_t = std::ptr::null_mut();
349 check_rc(nvpair_value_nvlist(nvp, &mut p), pair_name, dtype)?;
350 if p.is_null() {
351 return Err(NvError::NullPointer {
352 pair_name: pair_name.to_owned(),
353 type_code: dtype,
354 });
355 }
356 Ok(NvValue::NvList(NvList::from_raw(p)?))
357 }
358 data_type_t_DATA_TYPE_BOOLEAN_ARRAY => {
359 let mut p: *mut illumos_nvpair_sys::boolean_t = std::ptr::null_mut();
360 let mut n: uint_t = 0;
361 check_rc(
362 nvpair_value_boolean_array(nvp, &mut p, &mut n),
363 pair_name,
364 dtype,
365 )?;
366 let raw = array_to_vec(p, n, pair_name, dtype)?;
367 Ok(NvValue::BooleanArray(raw.iter().map(|&v| v != 0).collect()))
368 }
369 data_type_t_DATA_TYPE_BYTE_ARRAY => {
370 let mut p: *mut illumos_nvpair_sys::uchar_t = std::ptr::null_mut();
371 let mut n: uint_t = 0;
372 check_rc(
373 nvpair_value_byte_array(nvp, &mut p, &mut n),
374 pair_name,
375 dtype,
376 )?;
377 Ok(NvValue::ByteArray(array_to_vec(p, n, pair_name, dtype)?))
378 }
379 data_type_t_DATA_TYPE_INT8_ARRAY => {
380 let mut p: *mut i8 = std::ptr::null_mut();
381 let mut n: uint_t = 0;
382 check_rc(
383 nvpair_value_int8_array(nvp, &mut p, &mut n),
384 pair_name,
385 dtype,
386 )?;
387 Ok(NvValue::Int8Array(array_to_vec(p, n, pair_name, dtype)?))
388 }
389 data_type_t_DATA_TYPE_UINT8_ARRAY => {
390 let mut p: *mut u8 = std::ptr::null_mut();
391 let mut n: uint_t = 0;
392 check_rc(
393 nvpair_value_uint8_array(nvp, &mut p, &mut n),
394 pair_name,
395 dtype,
396 )?;
397 Ok(NvValue::UInt8Array(array_to_vec(p, n, pair_name, dtype)?))
398 }
399 data_type_t_DATA_TYPE_INT16_ARRAY => {
400 let mut p: *mut i16 = std::ptr::null_mut();
401 let mut n: uint_t = 0;
402 check_rc(
403 nvpair_value_int16_array(nvp, &mut p, &mut n),
404 pair_name,
405 dtype,
406 )?;
407 Ok(NvValue::Int16Array(array_to_vec(p, n, pair_name, dtype)?))
408 }
409 data_type_t_DATA_TYPE_UINT16_ARRAY => {
410 let mut p: *mut u16 = std::ptr::null_mut();
411 let mut n: uint_t = 0;
412 check_rc(
413 nvpair_value_uint16_array(nvp, &mut p, &mut n),
414 pair_name,
415 dtype,
416 )?;
417 Ok(NvValue::UInt16Array(array_to_vec(p, n, pair_name, dtype)?))
418 }
419 data_type_t_DATA_TYPE_INT32_ARRAY => {
420 let mut p: *mut i32 = std::ptr::null_mut();
421 let mut n: uint_t = 0;
422 check_rc(
423 nvpair_value_int32_array(nvp, &mut p, &mut n),
424 pair_name,
425 dtype,
426 )?;
427 Ok(NvValue::Int32Array(array_to_vec(p, n, pair_name, dtype)?))
428 }
429 data_type_t_DATA_TYPE_UINT32_ARRAY => {
430 let mut p: *mut u32 = std::ptr::null_mut();
431 let mut n: uint_t = 0;
432 check_rc(
433 nvpair_value_uint32_array(nvp, &mut p, &mut n),
434 pair_name,
435 dtype,
436 )?;
437 Ok(NvValue::UInt32Array(array_to_vec(p, n, pair_name, dtype)?))
438 }
439 data_type_t_DATA_TYPE_INT64_ARRAY => {
440 let mut p: *mut i64 = std::ptr::null_mut();
441 let mut n: uint_t = 0;
442 check_rc(
443 nvpair_value_int64_array(nvp, &mut p, &mut n),
444 pair_name,
445 dtype,
446 )?;
447 Ok(NvValue::Int64Array(array_to_vec(p, n, pair_name, dtype)?))
448 }
449 data_type_t_DATA_TYPE_UINT64_ARRAY => {
450 let mut p: *mut u64 = std::ptr::null_mut();
451 let mut n: uint_t = 0;
452 check_rc(
453 nvpair_value_uint64_array(nvp, &mut p, &mut n),
454 pair_name,
455 dtype,
456 )?;
457 Ok(NvValue::UInt64Array(array_to_vec(p, n, pair_name, dtype)?))
458 }
459 data_type_t_DATA_TYPE_STRING_ARRAY => {
460 let mut p: *mut *mut c_char = std::ptr::null_mut();
461 let mut n: uint_t = 0;
462 check_rc(
463 nvpair_value_string_array(nvp, &mut p, &mut n),
464 pair_name,
465 dtype,
466 )?;
467 let ptrs = array_to_vec(p as *const *mut c_char, n, pair_name, dtype)?;
468 let mut strings = Vec::with_capacity(ptrs.len());
469 for &s in &ptrs {
470 if s.is_null() {
471 return Err(NvError::NullPointer {
472 pair_name: pair_name.to_owned(),
473 type_code: dtype,
474 });
475 }
476 strings.push(CStr::from_ptr(s).to_string_lossy().into_owned());
477 }
478 Ok(NvValue::StringArray(strings))
479 }
480 data_type_t_DATA_TYPE_NVLIST_ARRAY => {
481 let mut p: *mut *mut nvlist_t = std::ptr::null_mut();
482 let mut n: uint_t = 0;
483 check_rc(
484 nvpair_value_nvlist_array(nvp, &mut p, &mut n),
485 pair_name,
486 dtype,
487 )?;
488 let ptrs = array_to_vec(p as *const *mut nvlist_t, n, pair_name, dtype)?;
489 let mut lists = Vec::with_capacity(ptrs.len());
490 for &nvl in &ptrs {
491 if nvl.is_null() {
492 return Err(NvError::NullPointer {
493 pair_name: pair_name.to_owned(),
494 type_code: dtype,
495 });
496 }
497 lists.push(NvList::from_raw(nvl)?);
498 }
499 Ok(NvValue::NvListArray(lists))
500 }
501 other => Ok(NvValue::Unknown { type_code: other }),
502 }
503 }
504}
505
506#[cfg(test)]
507mod tests {
508 use super::*;
509 use illumos_nvpair_sys::{
510 NV_UNIQUE_NAME, boolean_t, nvlist_add_boolean, nvlist_add_boolean_array,
511 nvlist_add_boolean_value, nvlist_add_byte, nvlist_add_byte_array, nvlist_add_double,
512 nvlist_add_hrtime, nvlist_add_int8, nvlist_add_int8_array, nvlist_add_int16,
513 nvlist_add_int16_array, nvlist_add_int32, nvlist_add_int32_array, nvlist_add_int64,
514 nvlist_add_int64_array, nvlist_add_nvlist, nvlist_add_nvlist_array, nvlist_add_string,
515 nvlist_add_string_array, nvlist_add_uint8, nvlist_add_uint8_array, nvlist_add_uint16,
516 nvlist_add_uint16_array, nvlist_add_uint32, nvlist_add_uint32_array, nvlist_add_uint64,
517 nvlist_add_uint64_array, nvlist_alloc, nvlist_free,
518 };
519 use std::ffi::CString;
520
521 struct NvListBuilder {
523 ptr: *mut nvlist_t,
524 }
525
526 impl NvListBuilder {
527 fn new() -> Self {
528 let mut ptr: *mut nvlist_t = std::ptr::null_mut();
529 let rc = unsafe { nvlist_alloc(&mut ptr, NV_UNIQUE_NAME, 0) };
530 assert_eq!(rc, 0, "nvlist_alloc failed");
531 assert!(!ptr.is_null());
532 NvListBuilder { ptr }
533 }
534
535 fn to_rust(&self) -> NvList {
536 unsafe { NvList::from_raw(self.ptr) }.expect("nvlist_to_rust failed")
537 }
538
539 fn add_boolean(&self, name: &str) -> &Self {
540 let cname = CString::new(name).unwrap();
541 let rc = unsafe { nvlist_add_boolean(self.ptr, cname.as_ptr()) };
542 assert_eq!(rc, 0, "nvlist_add_boolean failed for {name}");
543 self
544 }
545
546 fn add_boolean_value(&self, name: &str, val: bool) -> &Self {
547 let cname = CString::new(name).unwrap();
548 let cval: boolean_t = if val { 1 } else { 0 };
549 let rc = unsafe { nvlist_add_boolean_value(self.ptr, cname.as_ptr(), cval) };
550 assert_eq!(rc, 0, "nvlist_add_boolean_value failed for {name}");
551 self
552 }
553
554 fn add_byte(&self, name: &str, val: u8) -> &Self {
555 let cname = CString::new(name).unwrap();
556 let rc = unsafe { nvlist_add_byte(self.ptr, cname.as_ptr(), val) };
557 assert_eq!(rc, 0, "nvlist_add_byte failed for {name}");
558 self
559 }
560
561 fn add_int8(&self, name: &str, val: i8) -> &Self {
562 let cname = CString::new(name).unwrap();
563 let rc = unsafe { nvlist_add_int8(self.ptr, cname.as_ptr(), val) };
564 assert_eq!(rc, 0, "nvlist_add_int8 failed for {name}");
565 self
566 }
567
568 fn add_uint8(&self, name: &str, val: u8) -> &Self {
569 let cname = CString::new(name).unwrap();
570 let rc = unsafe { nvlist_add_uint8(self.ptr, cname.as_ptr(), val) };
571 assert_eq!(rc, 0, "nvlist_add_uint8 failed for {name}");
572 self
573 }
574
575 fn add_int16(&self, name: &str, val: i16) -> &Self {
576 let cname = CString::new(name).unwrap();
577 let rc = unsafe { nvlist_add_int16(self.ptr, cname.as_ptr(), val) };
578 assert_eq!(rc, 0, "nvlist_add_int16 failed for {name}");
579 self
580 }
581
582 fn add_uint16(&self, name: &str, val: u16) -> &Self {
583 let cname = CString::new(name).unwrap();
584 let rc = unsafe { nvlist_add_uint16(self.ptr, cname.as_ptr(), val) };
585 assert_eq!(rc, 0, "nvlist_add_uint16 failed for {name}");
586 self
587 }
588
589 fn add_int32(&self, name: &str, val: i32) -> &Self {
590 let cname = CString::new(name).unwrap();
591 let rc = unsafe { nvlist_add_int32(self.ptr, cname.as_ptr(), val) };
592 assert_eq!(rc, 0, "nvlist_add_int32 failed for {name}");
593 self
594 }
595
596 fn add_uint32(&self, name: &str, val: u32) -> &Self {
597 let cname = CString::new(name).unwrap();
598 let rc = unsafe { nvlist_add_uint32(self.ptr, cname.as_ptr(), val) };
599 assert_eq!(rc, 0, "nvlist_add_uint32 failed for {name}");
600 self
601 }
602
603 fn add_int64(&self, name: &str, val: i64) -> &Self {
604 let cname = CString::new(name).unwrap();
605 let rc = unsafe { nvlist_add_int64(self.ptr, cname.as_ptr(), val) };
606 assert_eq!(rc, 0, "nvlist_add_int64 failed for {name}");
607 self
608 }
609
610 fn add_uint64(&self, name: &str, val: u64) -> &Self {
611 let cname = CString::new(name).unwrap();
612 let rc = unsafe { nvlist_add_uint64(self.ptr, cname.as_ptr(), val) };
613 assert_eq!(rc, 0, "nvlist_add_uint64 failed for {name}");
614 self
615 }
616
617 fn add_double(&self, name: &str, val: f64) -> &Self {
618 let cname = CString::new(name).unwrap();
619 let rc = unsafe { nvlist_add_double(self.ptr, cname.as_ptr(), val) };
620 assert_eq!(rc, 0, "nvlist_add_double failed for {name}");
621 self
622 }
623
624 fn add_string(&self, name: &str, val: &str) -> &Self {
625 let cname = CString::new(name).unwrap();
626 let cval = CString::new(val).unwrap();
627 let rc = unsafe { nvlist_add_string(self.ptr, cname.as_ptr(), cval.as_ptr()) };
628 assert_eq!(rc, 0, "nvlist_add_string failed for {name}");
629 self
630 }
631
632 fn add_hrtime(&self, name: &str, val: i64) -> &Self {
633 let cname = CString::new(name).unwrap();
634 let rc = unsafe { nvlist_add_hrtime(self.ptr, cname.as_ptr(), val) };
635 assert_eq!(rc, 0, "nvlist_add_hrtime failed for {name}");
636 self
637 }
638
639 fn add_nvlist(&self, name: &str, child: &NvListBuilder) -> &Self {
640 let cname = CString::new(name).unwrap();
641 let rc = unsafe { nvlist_add_nvlist(self.ptr, cname.as_ptr(), child.ptr) };
642 assert_eq!(rc, 0, "nvlist_add_nvlist failed for {name}");
643 self
644 }
645
646 fn add_boolean_array(&self, name: &str, vals: &[bool]) -> &Self {
647 let cname = CString::new(name).unwrap();
648 let cvals: Vec<boolean_t> = vals.iter().map(|&v| if v { 1 } else { 0 }).collect();
649 let rc = unsafe {
650 nvlist_add_boolean_array(
651 self.ptr,
652 cname.as_ptr(),
653 cvals.as_ptr() as *mut boolean_t,
654 cvals.len() as uint_t,
655 )
656 };
657 assert_eq!(rc, 0, "nvlist_add_boolean_array failed for {name}");
658 self
659 }
660
661 fn add_byte_array(&self, name: &str, vals: &[u8]) -> &Self {
662 let cname = CString::new(name).unwrap();
663 let rc = unsafe {
664 nvlist_add_byte_array(
665 self.ptr,
666 cname.as_ptr(),
667 vals.as_ptr() as *mut u8,
668 vals.len() as uint_t,
669 )
670 };
671 assert_eq!(rc, 0, "nvlist_add_byte_array failed for {name}");
672 self
673 }
674
675 fn add_int8_array(&self, name: &str, vals: &[i8]) -> &Self {
676 let cname = CString::new(name).unwrap();
677 let rc = unsafe {
678 nvlist_add_int8_array(
679 self.ptr,
680 cname.as_ptr(),
681 vals.as_ptr() as *mut i8,
682 vals.len() as uint_t,
683 )
684 };
685 assert_eq!(rc, 0, "nvlist_add_int8_array failed for {name}");
686 self
687 }
688
689 fn add_uint8_array(&self, name: &str, vals: &[u8]) -> &Self {
690 let cname = CString::new(name).unwrap();
691 let rc = unsafe {
692 nvlist_add_uint8_array(
693 self.ptr,
694 cname.as_ptr(),
695 vals.as_ptr() as *mut u8,
696 vals.len() as uint_t,
697 )
698 };
699 assert_eq!(rc, 0, "nvlist_add_uint8_array failed for {name}");
700 self
701 }
702
703 fn add_int16_array(&self, name: &str, vals: &[i16]) -> &Self {
704 let cname = CString::new(name).unwrap();
705 let rc = unsafe {
706 nvlist_add_int16_array(
707 self.ptr,
708 cname.as_ptr(),
709 vals.as_ptr() as *mut i16,
710 vals.len() as uint_t,
711 )
712 };
713 assert_eq!(rc, 0, "nvlist_add_int16_array failed for {name}");
714 self
715 }
716
717 fn add_uint16_array(&self, name: &str, vals: &[u16]) -> &Self {
718 let cname = CString::new(name).unwrap();
719 let rc = unsafe {
720 nvlist_add_uint16_array(
721 self.ptr,
722 cname.as_ptr(),
723 vals.as_ptr() as *mut u16,
724 vals.len() as uint_t,
725 )
726 };
727 assert_eq!(rc, 0, "nvlist_add_uint16_array failed for {name}");
728 self
729 }
730
731 fn add_int32_array(&self, name: &str, vals: &[i32]) -> &Self {
732 let cname = CString::new(name).unwrap();
733 let rc = unsafe {
734 nvlist_add_int32_array(
735 self.ptr,
736 cname.as_ptr(),
737 vals.as_ptr() as *mut i32,
738 vals.len() as uint_t,
739 )
740 };
741 assert_eq!(rc, 0, "nvlist_add_int32_array failed for {name}");
742 self
743 }
744
745 fn add_uint32_array(&self, name: &str, vals: &[u32]) -> &Self {
746 let cname = CString::new(name).unwrap();
747 let rc = unsafe {
748 nvlist_add_uint32_array(
749 self.ptr,
750 cname.as_ptr(),
751 vals.as_ptr() as *mut u32,
752 vals.len() as uint_t,
753 )
754 };
755 assert_eq!(rc, 0, "nvlist_add_uint32_array failed for {name}");
756 self
757 }
758
759 fn add_int64_array(&self, name: &str, vals: &[i64]) -> &Self {
760 let cname = CString::new(name).unwrap();
761 let rc = unsafe {
762 nvlist_add_int64_array(
763 self.ptr,
764 cname.as_ptr(),
765 vals.as_ptr() as *mut i64,
766 vals.len() as uint_t,
767 )
768 };
769 assert_eq!(rc, 0, "nvlist_add_int64_array failed for {name}");
770 self
771 }
772
773 fn add_uint64_array(&self, name: &str, vals: &[u64]) -> &Self {
774 let cname = CString::new(name).unwrap();
775 let rc = unsafe {
776 nvlist_add_uint64_array(
777 self.ptr,
778 cname.as_ptr(),
779 vals.as_ptr() as *mut u64,
780 vals.len() as uint_t,
781 )
782 };
783 assert_eq!(rc, 0, "nvlist_add_uint64_array failed for {name}");
784 self
785 }
786
787 fn add_string_array(&self, name: &str, vals: &[&str]) -> &Self {
788 let cname = CString::new(name).unwrap();
789 let cvals: Vec<CString> = vals.iter().map(|s| CString::new(*s).unwrap()).collect();
790 let ptrs: Vec<*mut c_char> = cvals.iter().map(|c| c.as_ptr() as *mut c_char).collect();
791 let rc = unsafe {
792 nvlist_add_string_array(
793 self.ptr,
794 cname.as_ptr(),
795 ptrs.as_ptr(),
796 ptrs.len() as uint_t,
797 )
798 };
799 assert_eq!(rc, 0, "nvlist_add_string_array failed for {name}");
800 self
801 }
802
803 fn add_nvlist_array(&self, name: &str, children: &[&NvListBuilder]) -> &Self {
804 let cname = CString::new(name).unwrap();
805 let mut ptrs: Vec<*mut nvlist_t> = children.iter().map(|c| c.ptr).collect();
806 let rc = unsafe {
807 nvlist_add_nvlist_array(
808 self.ptr,
809 cname.as_ptr(),
810 ptrs.as_mut_ptr(),
811 ptrs.len() as uint_t,
812 )
813 };
814 assert_eq!(rc, 0, "nvlist_add_nvlist_array failed for {name}");
815 self
816 }
817 }
818
819 impl Drop for NvListBuilder {
820 fn drop(&mut self) {
821 unsafe { nvlist_free(self.ptr) }
822 }
823 }
824
825 #[test]
828 fn test_boolean() {
829 let nvl = NvListBuilder::new();
830 nvl.add_boolean("flag");
831 let result = nvl.to_rust();
832 assert_eq!(result.lookup("flag"), Some(&NvValue::Boolean));
833 }
834
835 #[test]
836 fn test_boolean_value_true() {
837 let nvl = NvListBuilder::new();
838 nvl.add_boolean_value("enabled", true);
839 let result = nvl.to_rust();
840 assert_eq!(result.lookup("enabled"), Some(&NvValue::BooleanValue(true)));
841 }
842
843 #[test]
844 fn test_boolean_value_false() {
845 let nvl = NvListBuilder::new();
846 nvl.add_boolean_value("enabled", false);
847 let result = nvl.to_rust();
848 assert_eq!(
849 result.lookup("enabled"),
850 Some(&NvValue::BooleanValue(false))
851 );
852 }
853
854 #[test]
855 fn test_byte() {
856 let nvl = NvListBuilder::new();
857 nvl.add_byte("b", 0xAB);
858 let result = nvl.to_rust();
859 assert_eq!(result.lookup("b"), Some(&NvValue::Byte(0xAB)));
860 }
861
862 #[test]
863 fn test_int8() {
864 let nvl = NvListBuilder::new();
865 nvl.add_int8("v", i8::MIN);
866 let result = nvl.to_rust();
867 assert_eq!(result.lookup("v"), Some(&NvValue::Int8(i8::MIN)));
868 }
869
870 #[test]
871 fn test_uint8() {
872 let nvl = NvListBuilder::new();
873 nvl.add_uint8("v", u8::MAX);
874 let result = nvl.to_rust();
875 assert_eq!(result.lookup("v"), Some(&NvValue::UInt8(u8::MAX)));
876 }
877
878 #[test]
879 fn test_int16() {
880 let nvl = NvListBuilder::new();
881 nvl.add_int16("v", i16::MIN);
882 let result = nvl.to_rust();
883 assert_eq!(result.lookup("v"), Some(&NvValue::Int16(i16::MIN)));
884 }
885
886 #[test]
887 fn test_uint16() {
888 let nvl = NvListBuilder::new();
889 nvl.add_uint16("v", u16::MAX);
890 let result = nvl.to_rust();
891 assert_eq!(result.lookup("v"), Some(&NvValue::UInt16(u16::MAX)));
892 }
893
894 #[test]
895 fn test_int32() {
896 let nvl = NvListBuilder::new();
897 nvl.add_int32("v", i32::MIN);
898 let result = nvl.to_rust();
899 assert_eq!(result.lookup("v"), Some(&NvValue::Int32(i32::MIN)));
900 }
901
902 #[test]
903 fn test_uint32() {
904 let nvl = NvListBuilder::new();
905 nvl.add_uint32("v", u32::MAX);
906 let result = nvl.to_rust();
907 assert_eq!(result.lookup("v"), Some(&NvValue::UInt32(u32::MAX)));
908 }
909
910 #[test]
911 fn test_int64() {
912 let nvl = NvListBuilder::new();
913 nvl.add_int64("v", i64::MIN);
914 let result = nvl.to_rust();
915 assert_eq!(result.lookup("v"), Some(&NvValue::Int64(i64::MIN)));
916 }
917
918 #[test]
919 fn test_uint64() {
920 let nvl = NvListBuilder::new();
921 nvl.add_uint64("v", u64::MAX);
922 let result = nvl.to_rust();
923 assert_eq!(result.lookup("v"), Some(&NvValue::UInt64(u64::MAX)));
924 }
925
926 #[test]
927 fn test_double() {
928 let nvl = NvListBuilder::new();
929 nvl.add_double("pi", std::f64::consts::PI);
930 let result = nvl.to_rust();
931 assert_eq!(
932 result.lookup("pi"),
933 Some(&NvValue::Double(std::f64::consts::PI))
934 );
935 }
936
937 #[test]
938 fn test_string() {
939 let nvl = NvListBuilder::new();
940 nvl.add_string("greeting", "hello world");
941 let result = nvl.to_rust();
942 assert_eq!(
943 result.lookup("greeting"),
944 Some(&NvValue::String("hello world".into()))
945 );
946 }
947
948 #[test]
949 fn test_hrtime() {
950 let nvl = NvListBuilder::new();
951 nvl.add_hrtime("ts", 123_456_789);
952 let result = nvl.to_rust();
953 assert_eq!(result.lookup("ts"), Some(&NvValue::Hrtime(123_456_789)));
954 }
955
956 #[test]
959 fn test_boolean_array() {
960 let nvl = NvListBuilder::new();
961 nvl.add_boolean_array("flags", &[true, false, true]);
962 let result = nvl.to_rust();
963 assert_eq!(
964 result.lookup("flags"),
965 Some(&NvValue::BooleanArray(vec![true, false, true]))
966 );
967 }
968
969 #[test]
970 fn test_byte_array() {
971 let nvl = NvListBuilder::new();
972 nvl.add_byte_array("data", &[1, 2, 3]);
973 let result = nvl.to_rust();
974 assert_eq!(
975 result.lookup("data"),
976 Some(&NvValue::ByteArray(vec![1, 2, 3]))
977 );
978 }
979
980 #[test]
981 fn test_int8_array() {
982 let nvl = NvListBuilder::new();
983 nvl.add_int8_array("vals", &[i8::MIN, 0, i8::MAX]);
984 let result = nvl.to_rust();
985 assert_eq!(
986 result.lookup("vals"),
987 Some(&NvValue::Int8Array(vec![i8::MIN, 0, i8::MAX]))
988 );
989 }
990
991 #[test]
992 fn test_uint8_array() {
993 let nvl = NvListBuilder::new();
994 nvl.add_uint8_array("vals", &[0, 128, u8::MAX]);
995 let result = nvl.to_rust();
996 assert_eq!(
997 result.lookup("vals"),
998 Some(&NvValue::UInt8Array(vec![0, 128, u8::MAX]))
999 );
1000 }
1001
1002 #[test]
1003 fn test_int16_array() {
1004 let nvl = NvListBuilder::new();
1005 nvl.add_int16_array("vals", &[i16::MIN, 0, i16::MAX]);
1006 let result = nvl.to_rust();
1007 assert_eq!(
1008 result.lookup("vals"),
1009 Some(&NvValue::Int16Array(vec![i16::MIN, 0, i16::MAX]))
1010 );
1011 }
1012
1013 #[test]
1014 fn test_uint16_array() {
1015 let nvl = NvListBuilder::new();
1016 nvl.add_uint16_array("vals", &[0, u16::MAX]);
1017 let result = nvl.to_rust();
1018 assert_eq!(
1019 result.lookup("vals"),
1020 Some(&NvValue::UInt16Array(vec![0, u16::MAX]))
1021 );
1022 }
1023
1024 #[test]
1025 fn test_int32_array() {
1026 let nvl = NvListBuilder::new();
1027 nvl.add_int32_array("vals", &[i32::MIN, 0, i32::MAX]);
1028 let result = nvl.to_rust();
1029 assert_eq!(
1030 result.lookup("vals"),
1031 Some(&NvValue::Int32Array(vec![i32::MIN, 0, i32::MAX]))
1032 );
1033 }
1034
1035 #[test]
1036 fn test_uint32_array() {
1037 let nvl = NvListBuilder::new();
1038 nvl.add_uint32_array("vals", &[0, u32::MAX]);
1039 let result = nvl.to_rust();
1040 assert_eq!(
1041 result.lookup("vals"),
1042 Some(&NvValue::UInt32Array(vec![0, u32::MAX]))
1043 );
1044 }
1045
1046 #[test]
1047 fn test_int64_array() {
1048 let nvl = NvListBuilder::new();
1049 nvl.add_int64_array("vals", &[i64::MIN, 0, i64::MAX]);
1050 let result = nvl.to_rust();
1051 assert_eq!(
1052 result.lookup("vals"),
1053 Some(&NvValue::Int64Array(vec![i64::MIN, 0, i64::MAX]))
1054 );
1055 }
1056
1057 #[test]
1058 fn test_uint64_array() {
1059 let nvl = NvListBuilder::new();
1060 nvl.add_uint64_array("vals", &[0, u64::MAX]);
1061 let result = nvl.to_rust();
1062 assert_eq!(
1063 result.lookup("vals"),
1064 Some(&NvValue::UInt64Array(vec![0, u64::MAX]))
1065 );
1066 }
1067
1068 #[test]
1069 fn test_string_array() {
1070 let nvl = NvListBuilder::new();
1071 nvl.add_string_array("names", &["alpha", "beta", "gamma"]);
1072 let result = nvl.to_rust();
1073 assert_eq!(
1074 result.lookup("names"),
1075 Some(&NvValue::StringArray(vec![
1076 "alpha".into(),
1077 "beta".into(),
1078 "gamma".into(),
1079 ]))
1080 );
1081 }
1082
1083 #[test]
1084 fn test_nvlist_array() {
1085 let child1 = NvListBuilder::new();
1086 child1.add_string("name", "first");
1087 let child2 = NvListBuilder::new();
1088 child2.add_string("name", "second");
1089
1090 let nvl = NvListBuilder::new();
1091 nvl.add_nvlist_array("items", &[&child1, &child2]);
1092
1093 let result = nvl.to_rust();
1094 let items = result.lookup("items").unwrap();
1095 if let NvValue::NvListArray(arr) = items {
1096 assert_eq!(arr.len(), 2);
1097 assert_eq!(
1098 arr[0].lookup("name"),
1099 Some(&NvValue::String("first".into()))
1100 );
1101 assert_eq!(
1102 arr[1].lookup("name"),
1103 Some(&NvValue::String("second".into()))
1104 );
1105 } else {
1106 panic!("expected NvListArray, got {items:?}");
1107 }
1108 }
1109
1110 #[test]
1113 fn test_empty_nvlist() {
1114 let nvl = NvListBuilder::new();
1115 let result = nvl.to_rust();
1116 assert!(result.is_empty());
1117 assert_eq!(result.len(), 0);
1118 }
1119
1120 #[test]
1121 fn test_multiple_pairs_in_order() {
1122 let nvl = NvListBuilder::new();
1123 nvl.add_string("first", "a");
1124 nvl.add_int32("second", 42);
1125 nvl.add_boolean("third");
1126 nvl.add_uint64("fourth", 999);
1127 nvl.add_double("fifth", 2.5);
1128
1129 let result = nvl.to_rust();
1130 assert_eq!(result.len(), 5);
1131
1132 let pairs: Vec<_> = result.iter().collect();
1133 assert_eq!(pairs[0].0, "first");
1134 assert_eq!(pairs[1].0, "second");
1135 assert_eq!(pairs[2].0, "third");
1136 assert_eq!(pairs[3].0, "fourth");
1137 assert_eq!(pairs[4].0, "fifth");
1138
1139 assert_eq!(*pairs[0].1, NvValue::String("a".into()));
1140 assert_eq!(*pairs[1].1, NvValue::Int32(42));
1141 assert_eq!(*pairs[2].1, NvValue::Boolean);
1142 assert_eq!(*pairs[3].1, NvValue::UInt64(999));
1143 assert_eq!(*pairs[4].1, NvValue::Double(2.5));
1144 }
1145
1146 #[test]
1147 fn test_nested_nvlist() {
1148 let inner = NvListBuilder::new();
1149 inner.add_string("key", "value");
1150
1151 let outer = NvListBuilder::new();
1152 outer.add_nvlist("child", &inner);
1153
1154 let result = outer.to_rust();
1155 if let Some(NvValue::NvList(child)) = result.lookup("child") {
1156 assert_eq!(child.lookup("key"), Some(&NvValue::String("value".into())));
1157 } else {
1158 panic!("expected nested NvList");
1159 }
1160 }
1161
1162 #[test]
1163 fn test_deeply_nested_nvlist() {
1164 let innermost = NvListBuilder::new();
1165 innermost.add_string("depth", "three");
1166
1167 let middle = NvListBuilder::new();
1168 middle.add_nvlist("inner", &innermost);
1169
1170 let outer = NvListBuilder::new();
1171 outer.add_nvlist("middle", &middle);
1172
1173 let result = outer.to_rust();
1174 if let Some(NvValue::NvList(mid)) = result.lookup("middle") {
1175 if let Some(NvValue::NvList(inn)) = mid.lookup("inner") {
1176 assert_eq!(inn.lookup("depth"), Some(&NvValue::String("three".into())));
1177 } else {
1178 panic!("expected inner NvList");
1179 }
1180 } else {
1181 panic!("expected middle NvList");
1182 }
1183 }
1184
1185 #[test]
1186 fn test_empty_string() {
1187 let nvl = NvListBuilder::new();
1188 nvl.add_string("empty", "");
1189 let result = nvl.to_rust();
1190 assert_eq!(
1191 result.lookup("empty"),
1192 Some(&NvValue::String(String::new()))
1193 );
1194 }
1195
1196 #[test]
1197 fn test_lookup_missing_key() {
1198 let nvl = NvListBuilder::new();
1199 nvl.add_string("exists", "yes");
1200 let result = nvl.to_rust();
1201 assert_eq!(
1202 result.lookup("exists"),
1203 Some(&NvValue::String("yes".into()))
1204 );
1205 assert_eq!(result.lookup("missing"), None);
1206 }
1207}