cue_rs/value/mod.rs
1//! CUE value type, wrapping the `cue_value` handle from libcue.
2
3#[cfg(test)]
4mod tests;
5
6use core::ffi::c_char;
7
8use crate::{
9 Ctx, drop,
10 error::{CueError, Error},
11};
12
13/// Opaque handle to a libcue value (`cue_value` = `uintptr_t`).
14type CueValueHandle = usize;
15
16unsafe extern "C" {
17 fn cue_validate(
18 v: CueValueHandle,
19 opts: *mut core::ffi::c_void,
20 ) -> usize;
21 fn cue_is_equal(
22 a: CueValueHandle,
23 b: CueValueHandle,
24 ) -> bool;
25 fn cue_unify(
26 a: CueValueHandle,
27 b: CueValueHandle,
28 ) -> CueValueHandle;
29 fn cue_compile_string(
30 ctx: usize,
31 src: *mut c_char,
32 opts: *mut core::ffi::c_void,
33 out: *mut CueValueHandle,
34 ) -> usize;
35 fn cue_compile_bytes(
36 ctx: usize,
37 data: *mut core::ffi::c_void,
38 len: usize,
39 opts: *mut core::ffi::c_void,
40 out: *mut CueValueHandle,
41 ) -> usize;
42 fn cue_dec_json(
43 v: CueValueHandle,
44 res: *mut *mut core::ffi::c_void,
45 size: *mut usize,
46 ) -> usize;
47}
48
49/// A CUE value backed by a libcue `cue_value` handle.
50///
51/// Construct one via [`Value::compile_string`] or [`Value::compile_bytes`];
52/// the underlying handle is freed automatically when this value is dropped.
53///
54/// A successfully constructed `Value` may still represent an invalid CUE
55/// value (e.g. a bottom value produced by a conflicting unification).
56/// Call [`Value::is_valid`] to confirm the value is error-free before using it.
57#[derive(Debug)]
58pub struct Value(CueValueHandle);
59
60impl Drop for Value {
61 fn drop(&mut self) {
62 unsafe { drop::cue_free(self.0) }
63 }
64}
65
66impl PartialEq for Value {
67 fn eq(
68 &self,
69 other: &Self,
70 ) -> bool {
71 unsafe { cue_is_equal(self.0, other.0) }
72 }
73}
74
75impl Value {
76 /// Compiles a CUE source string into a [`Value`].
77 ///
78 /// # Errors
79 ///
80 /// Returns [`Error::StringContainsNul`] if `src` contains interior nul
81 /// bytes, or [`Error::Cue`] if libcue reports a compilation error.
82 pub fn compile_string(
83 ctx: &Ctx,
84 src: &str,
85 ) -> Result<Self, Error> {
86 let cstr = std::ffi::CString::new(src).map_err(Error::StringContainsNul)?;
87 let mut handle: CueValueHandle = 0;
88 let err = unsafe {
89 cue_compile_string(
90 ctx.handle(),
91 cstr.as_ptr().cast_mut(),
92 core::ptr::null_mut(),
93 &raw mut handle,
94 )
95 };
96 if err != 0 {
97 return Err(Error::Cue(CueError(err)));
98 }
99 Ok(Self(handle))
100 }
101
102 /// Compiles a CUE source byte slice into a [`Value`].
103 ///
104 /// Unlike [`Value::compile_string`], this accepts source that may contain
105 /// interior nul bytes (since it is passed by pointer and length rather than
106 /// as a C string).
107 ///
108 /// # Errors
109 ///
110 /// Returns [`Error::Cue`] if libcue reports a compilation error.
111 pub fn compile_bytes(
112 ctx: &Ctx,
113 src: &[u8],
114 ) -> Result<Self, Error> {
115 let mut handle: CueValueHandle = 0;
116 let err = unsafe {
117 cue_compile_bytes(
118 ctx.handle(),
119 src.as_ptr().cast::<core::ffi::c_void>().cast_mut(),
120 src.len(),
121 core::ptr::null_mut(),
122 &raw mut handle,
123 )
124 };
125 if err != 0 {
126 return Err(Error::Cue(CueError(err)));
127 }
128 Ok(Self(handle))
129 }
130
131 /// Encodes this CUE value as JSON.
132 ///
133 /// Calls `cue_dec_json` from libcue and copies the result into an owned
134 /// [`bytes::Bytes`] buffer containing the raw JSON bytes. The C-allocated
135 /// buffer is freed before returning.
136 ///
137 /// # Errors
138 ///
139 /// Returns [`Error::Cue`] if libcue reports an error (e.g. the value
140 /// cannot be represented as JSON).
141 pub fn to_json_bytes(&self) -> Result<bytes::Bytes, Error> {
142 let mut ptr: *mut core::ffi::c_void = core::ptr::null_mut();
143 let mut size: usize = 0;
144 let err = unsafe { cue_dec_json(self.0, &raw mut ptr, &raw mut size) };
145 if err != 0 {
146 return Err(Error::Cue(CueError(err)));
147 }
148 let result = bytes::Bytes::copy_from_slice(unsafe {
149 core::slice::from_raw_parts(ptr.cast::<u8>(), size)
150 });
151 unsafe { drop::libc_free(ptr) };
152 Ok(result)
153 }
154
155 /// Unifies two CUE values, returning the meet of the two.
156 ///
157 /// Calls `cue_unify` from libcue. In CUE, unification is the `&`
158 /// operator: the result is the most specific value that satisfies both
159 /// operands. If the two values are incompatible the result is the bottom
160 /// value (`_|_`); call [`Value::is_valid`] to check.
161 #[must_use]
162 pub fn unify(
163 v1: &Value,
164 v2: &Value,
165 ) -> Self {
166 let handle = unsafe { cue_unify(v1.0, v2.0) };
167 Self(handle)
168 }
169
170 /// Validates this CUE value, returning an error if it is not valid.
171 ///
172 /// Calls `cue_validate` from libcue with no export options. A value is
173 /// valid when it contains no errors (e.g. it is not a bottom value).
174 ///
175 /// # Errors
176 ///
177 /// Returns [`Error::Cue`] if libcue reports a validation error.
178 pub fn is_valid(&self) -> Result<(), Error> {
179 let err = unsafe { cue_validate(self.0, core::ptr::null_mut()) };
180 if err != 0 {
181 return Err(Error::Cue(CueError(err)));
182 }
183 Ok(())
184 }
185}