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