Skip to main content

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}