1#![cfg(feature = "expr")]
5
6use std::{ffi::CString, path::Path, ptr::NonNull, sync::Arc};
7
8use crate::{
9 Context,
10 Error,
11 Result,
12 Store,
13 Value,
14 context::is_pure_eval,
15 error::check_err,
16 sys,
17};
18
19pub struct EvalStateBuilder {
24 inner: NonNull<sys::nix_eval_state_builder>,
25 store: Arc<Store>,
26 context: Arc<Context>,
27 skip_load: bool,
28}
29
30impl EvalStateBuilder {
31 pub fn new(store: &Arc<Store>) -> Result<Self> {
41 let builder_ptr = unsafe {
43 sys::nix_eval_state_builder_new(store._context.as_ptr(), store.as_ptr())
44 };
45
46 let inner = NonNull::new(builder_ptr).ok_or(Error::NullPointer)?;
47
48 Ok(EvalStateBuilder {
49 inner,
50 store: Arc::clone(store),
51 context: Arc::clone(&store._context),
52 skip_load: false,
53 })
54 }
55
56 pub fn set_lookup_path(self, paths: &[impl AsRef<str>]) -> Result<Self> {
65 let c_strings: Vec<CString> = paths
66 .iter()
67 .map(|s| CString::new(s.as_ref()))
68 .collect::<std::result::Result<_, _>>()?;
69
70 let mut ptrs: Vec<*const std::os::raw::c_char> =
71 c_strings.iter().map(|cs| cs.as_ptr()).collect();
72 ptrs.push(std::ptr::null());
73
74 unsafe {
76 check_err(
77 self.context.as_ptr(),
78 sys::nix_eval_state_builder_set_lookup_path(
79 self.context.as_ptr(),
80 self.inner.as_ptr(),
81 ptrs.as_mut_ptr(),
82 ),
83 )?;
84 }
85
86 Ok(self)
87 }
88
89 #[cfg(feature = "flake")]
98 pub fn with_flake_settings(
99 self,
100 settings: &crate::flake::FlakeSettings,
101 ) -> Result<Self> {
102 unsafe {
104 check_err(
105 self.context.as_ptr(),
106 sys::nix_flake_settings_add_to_eval_state_builder(
107 self.context.as_ptr(),
108 settings.as_ptr(),
109 self.inner.as_ptr(),
110 ),
111 )?;
112 }
113
114 Ok(self)
115 }
116
117 #[must_use]
124 pub fn no_load_config(mut self) -> Self {
125 self.skip_load = true;
126 self
127 }
128
129 pub fn build(self) -> Result<EvalState> {
135 if !self.skip_load {
136 unsafe {
138 check_err(
139 self.context.as_ptr(),
140 sys::nix_eval_state_builder_load(
141 self.context.as_ptr(),
142 self.inner.as_ptr(),
143 ),
144 )?;
145 }
146 }
147
148 let state_ptr = unsafe {
150 sys::nix_eval_state_build(self.context.as_ptr(), self.inner.as_ptr())
151 };
152
153 let inner = NonNull::new(state_ptr).ok_or(Error::NullPointer)?;
154
155 Ok(EvalState {
156 inner,
157 store: self.store.clone(),
158 context: self.context.clone(),
159 })
160 }
161}
162
163impl Drop for EvalStateBuilder {
164 fn drop(&mut self) {
165 unsafe {
167 sys::nix_eval_state_builder_free(self.inner.as_ptr());
168 }
169 }
170}
171
172pub struct EvalState {
177 pub(crate) inner: NonNull<sys::EvalState>,
178 #[expect(dead_code, reason = "keeps the Arc<Store> alive Drop side-effects")]
179 store: Arc<Store>,
180 pub(crate) context: Arc<Context>,
181}
182
183impl EvalState {
184 pub fn eval_from_string(&self, expr: &str, path: &str) -> Result<Value<'_>> {
195 let expr_c = CString::new(expr)?;
196 let path_c = CString::new(path)?;
197
198 let value_ptr = unsafe {
200 sys::nix_alloc_value(self.context.as_ptr(), self.inner.as_ptr())
201 };
202 if value_ptr.is_null() {
203 return Err(Error::NullPointer);
204 }
205
206 unsafe {
208 check_err(
209 self.context.as_ptr(),
210 sys::nix_expr_eval_from_string(
211 self.context.as_ptr(),
212 self.inner.as_ptr(),
213 expr_c.as_ptr(),
214 path_c.as_ptr(),
215 value_ptr,
216 ),
217 )?;
218 }
219
220 let inner = NonNull::new(value_ptr).ok_or(Error::NullPointer)?;
221
222 Ok(Value { inner, state: self })
223 }
224
225 pub fn eval_from_file(&self, path: impl AsRef<Path>) -> Result<Value<'_>> {
237 let path = path.as_ref();
238 let expr = std::fs::read_to_string(path).map_err(|e| {
239 Error::Unknown(format!("Failed to read file {}: {e}", path.display()))
240 })?;
241 let base_path = path.parent().unwrap_or_else(|| Path::new("."));
242 let base_str = base_path.to_string_lossy();
243 self.eval_from_string(&expr, &base_str)
244 }
245
246 pub fn alloc_value(&self) -> Result<Value<'_>> {
252 let value_ptr = unsafe {
254 sys::nix_alloc_value(self.context.as_ptr(), self.inner.as_ptr())
255 };
256 let inner = NonNull::new(value_ptr).ok_or(Error::NullPointer)?;
257
258 Ok(Value { inner, state: self })
259 }
260
261 pub fn make_int(&self, i: i64) -> Result<Value<'_>> {
267 let v = self.alloc_value()?;
268 unsafe {
270 check_err(
271 self.context.as_ptr(),
272 sys::nix_init_int(self.context.as_ptr(), v.inner.as_ptr(), i),
273 )?;
274 }
275 Ok(v)
276 }
277
278 pub fn make_float(&self, f: f64) -> Result<Value<'_>> {
284 let v = self.alloc_value()?;
285 unsafe {
287 check_err(
288 self.context.as_ptr(),
289 sys::nix_init_float(self.context.as_ptr(), v.inner.as_ptr(), f),
290 )?;
291 }
292 Ok(v)
293 }
294
295 pub fn make_bool(&self, b: bool) -> Result<Value<'_>> {
301 let v = self.alloc_value()?;
302 unsafe {
304 check_err(
305 self.context.as_ptr(),
306 sys::nix_init_bool(self.context.as_ptr(), v.inner.as_ptr(), b),
307 )?;
308 }
309 Ok(v)
310 }
311
312 pub fn make_null(&self) -> Result<Value<'_>> {
318 let v = self.alloc_value()?;
319 unsafe {
321 check_err(
322 self.context.as_ptr(),
323 sys::nix_init_null(self.context.as_ptr(), v.inner.as_ptr()),
324 )?;
325 }
326 Ok(v)
327 }
328
329 pub fn make_string(&self, s: &str) -> Result<Value<'_>> {
336 let v = self.alloc_value()?;
337 let s_c = CString::new(s)?;
338 unsafe {
340 check_err(
341 self.context.as_ptr(),
342 sys::nix_init_string(
343 self.context.as_ptr(),
344 v.inner.as_ptr(),
345 s_c.as_ptr(),
346 ),
347 )?;
348 }
349 Ok(v)
350 }
351
352 pub fn make_path(&self, path: impl AsRef<Path>) -> Result<Value<'_>> {
369 let v = self.alloc_value()?;
370 let path_str = path
371 .as_ref()
372 .to_str()
373 .ok_or_else(|| Error::Unknown("Path is not valid UTF-8".to_string()))?;
374 let path_c = CString::new(path_str)?;
375
376 #[cfg(feature = "shim")]
379 if path.as_ref().is_absolute()
380 && path_str.starts_with("/nix/store/")
381 && is_pure_eval()
382 {
383 unsafe {
385 check_err(
386 self.context.as_ptr(),
387 sys::nix_eval_state_allow_path(
388 self.context.as_ptr(),
389 self.inner.as_ptr(),
390 path_c.as_ptr(),
391 ),
392 )?;
393 }
394 }
395
396 unsafe {
398 check_err(
399 self.context.as_ptr(),
400 sys::nix_init_path_string(
401 self.context.as_ptr(),
402 self.inner.as_ptr(),
403 v.inner.as_ptr(),
404 path_c.as_ptr(),
405 ),
406 )?;
407 }
408 Ok(v)
409 }
410
411 pub fn make_list(&self, items: &[&Value<'_>]) -> Result<Value<'_>> {
417 let builder = unsafe {
419 sys::nix_make_list_builder(
420 self.context.as_ptr(),
421 self.inner.as_ptr(),
422 items.len(),
423 )
424 };
425 if builder.is_null() {
426 return Err(Error::NullPointer);
427 }
428
429 struct ListBuilderGuard(*mut sys::ListBuilder);
430 impl Drop for ListBuilderGuard {
431 fn drop(&mut self) {
432 unsafe { sys::nix_list_builder_free(self.0) };
433 }
434 }
435 let _guard = ListBuilderGuard(builder);
436
437 for (i, item) in items.iter().enumerate() {
438 unsafe {
440 check_err(
441 self.context.as_ptr(),
442 sys::nix_list_builder_insert(
443 self.context.as_ptr(),
444 builder,
445 i as std::os::raw::c_uint,
446 item.inner.as_ptr(),
447 ),
448 )?;
449 }
450 }
451
452 let result = self.alloc_value()?;
453 unsafe {
455 check_err(
456 self.context.as_ptr(),
457 sys::nix_make_list(
458 self.context.as_ptr(),
459 builder,
460 result.inner.as_ptr(),
461 ),
462 )?;
463 }
464
465 Ok(result)
466 }
467
468 pub fn make_attrs<'s>(
475 &'s self,
476 pairs: &[(&str, &Value<'_>)],
477 ) -> Result<Value<'s>> {
478 let builder = unsafe {
480 sys::nix_make_bindings_builder(
481 self.context.as_ptr(),
482 self.inner.as_ptr(),
483 pairs.len(),
484 )
485 };
486 if builder.is_null() {
487 return Err(Error::NullPointer);
488 }
489
490 struct BindingsBuilderGuard(*mut sys::BindingsBuilder);
491 impl Drop for BindingsBuilderGuard {
492 fn drop(&mut self) {
493 unsafe { sys::nix_bindings_builder_free(self.0) };
494 }
495 }
496 let _guard = BindingsBuilderGuard(builder);
497
498 for (key, value) in pairs {
499 let key_c = CString::new(*key)?;
500 unsafe {
502 check_err(
503 self.context.as_ptr(),
504 sys::nix_bindings_builder_insert(
505 self.context.as_ptr(),
506 builder,
507 key_c.as_ptr(),
508 value.inner.as_ptr(),
509 ),
510 )?;
511 }
512 }
513
514 let result = self.alloc_value()?;
515 unsafe {
517 check_err(
518 self.context.as_ptr(),
519 sys::nix_make_attrs(
520 self.context.as_ptr(),
521 result.inner.as_ptr(),
522 builder,
523 ),
524 )?;
525 }
526
527 Ok(result)
528 }
529
530 pub(crate) unsafe fn as_ptr(&self) -> *mut sys::EvalState {
536 self.inner.as_ptr()
537 }
538}
539
540impl Drop for EvalState {
541 fn drop(&mut self) {
542 unsafe {
544 sys::nix_state_free(self.inner.as_ptr());
545 }
546 }
547}
548
549unsafe impl Send for EvalState {}