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 StorePath,
14 Value,
15 context::is_pure_eval,
16 error::check_err,
17 sys,
18};
19
20pub struct EvalStateBuilder {
25 inner: NonNull<sys::nix_eval_state_builder>,
26 store: Arc<Store>,
27 context: Arc<Context>,
28 skip_load: bool,
29}
30
31impl EvalStateBuilder {
32 pub fn new(store: &Arc<Store>) -> Result<Self> {
42 let builder_ptr = unsafe {
44 sys::nix_eval_state_builder_new(store._context.as_ptr(), store.as_ptr())
45 };
46
47 let inner = NonNull::new(builder_ptr).ok_or(Error::NullPointer)?;
48
49 Ok(EvalStateBuilder {
50 inner,
51 store: Arc::clone(store),
52 context: Arc::clone(&store._context),
53 skip_load: false,
54 })
55 }
56
57 pub fn set_lookup_path(self, paths: &[impl AsRef<str>]) -> Result<Self> {
66 let c_strings: Vec<CString> = paths
67 .iter()
68 .map(|s| CString::new(s.as_ref()))
69 .collect::<std::result::Result<_, _>>()?;
70
71 let mut ptrs: Vec<*const std::os::raw::c_char> =
72 c_strings.iter().map(|cs| cs.as_ptr()).collect();
73 ptrs.push(std::ptr::null());
74
75 unsafe {
77 check_err(
78 self.context.as_ptr(),
79 sys::nix_eval_state_builder_set_lookup_path(
80 self.context.as_ptr(),
81 self.inner.as_ptr(),
82 ptrs.as_mut_ptr(),
83 ),
84 )?;
85 }
86
87 Ok(self)
88 }
89
90 #[cfg(feature = "flake")]
99 pub fn with_flake_settings(
100 self,
101 settings: &crate::flake::FlakeSettings,
102 ) -> Result<Self> {
103 unsafe {
105 check_err(
106 self.context.as_ptr(),
107 sys::nix_flake_settings_add_to_eval_state_builder(
108 self.context.as_ptr(),
109 settings.as_ptr(),
110 self.inner.as_ptr(),
111 ),
112 )?;
113 }
114
115 Ok(self)
116 }
117
118 #[must_use]
125 pub fn no_load_config(mut self) -> Self {
126 self.skip_load = true;
127 self
128 }
129
130 pub fn build(self) -> Result<EvalState> {
136 if !self.skip_load {
137 unsafe {
139 check_err(
140 self.context.as_ptr(),
141 sys::nix_eval_state_builder_load(
142 self.context.as_ptr(),
143 self.inner.as_ptr(),
144 ),
145 )?;
146 }
147 }
148
149 let state_ptr = unsafe {
151 sys::nix_eval_state_build(self.context.as_ptr(), self.inner.as_ptr())
152 };
153
154 let inner = NonNull::new(state_ptr).ok_or(Error::NullPointer)?;
155
156 Ok(EvalState {
157 inner,
158 store: self.store.clone(),
159 context: self.context.clone(),
160 })
161 }
162}
163
164impl Drop for EvalStateBuilder {
165 fn drop(&mut self) {
166 unsafe {
168 sys::nix_eval_state_builder_free(self.inner.as_ptr());
169 }
170 }
171}
172
173pub struct EvalState {
178 pub(crate) inner: NonNull<sys::EvalState>,
179 #[expect(dead_code, reason = "keeps the Arc<Store> alive Drop side-effects")]
180 store: Arc<Store>,
181 pub(crate) context: Arc<Context>,
182}
183
184impl EvalState {
185 pub fn eval_from_string(&self, expr: &str, path: &str) -> Result<Value<'_>> {
196 let expr_c = CString::new(expr)?;
197 let path_c = CString::new(path)?;
198
199 let value_ptr = unsafe {
201 sys::nix_alloc_value(self.context.as_ptr(), self.inner.as_ptr())
202 };
203 if value_ptr.is_null() {
204 return Err(Error::NullPointer);
205 }
206
207 unsafe {
209 check_err(
210 self.context.as_ptr(),
211 sys::nix_expr_eval_from_string(
212 self.context.as_ptr(),
213 self.inner.as_ptr(),
214 expr_c.as_ptr(),
215 path_c.as_ptr(),
216 value_ptr,
217 ),
218 )?;
219 }
220
221 let inner = NonNull::new(value_ptr).ok_or(Error::NullPointer)?;
222
223 Ok(Value { inner, state: self })
224 }
225
226 pub fn eval_from_file(&self, path: impl AsRef<Path>) -> Result<Value<'_>> {
238 let path = path.as_ref();
239 let expr = std::fs::read_to_string(path).map_err(|e| {
240 Error::Unknown(format!("Failed to read file {}: {e}", path.display()))
241 })?;
242 let base_path = path.parent().unwrap_or_else(|| Path::new("."));
243 let base_str = base_path.to_string_lossy();
244 self.eval_from_string(&expr, &base_str)
245 }
246
247 pub fn alloc_value(&self) -> Result<Value<'_>> {
253 let value_ptr = unsafe {
255 sys::nix_alloc_value(self.context.as_ptr(), self.inner.as_ptr())
256 };
257 let inner = NonNull::new(value_ptr).ok_or(Error::NullPointer)?;
258
259 Ok(Value { inner, state: self })
260 }
261
262 pub fn make_int(&self, i: i64) -> Result<Value<'_>> {
268 let v = self.alloc_value()?;
269 unsafe {
271 check_err(
272 self.context.as_ptr(),
273 sys::nix_init_int(self.context.as_ptr(), v.inner.as_ptr(), i),
274 )?;
275 }
276 Ok(v)
277 }
278
279 pub fn make_float(&self, f: f64) -> Result<Value<'_>> {
285 let v = self.alloc_value()?;
286 unsafe {
288 check_err(
289 self.context.as_ptr(),
290 sys::nix_init_float(self.context.as_ptr(), v.inner.as_ptr(), f),
291 )?;
292 }
293 Ok(v)
294 }
295
296 pub fn make_bool(&self, b: bool) -> Result<Value<'_>> {
302 let v = self.alloc_value()?;
303 unsafe {
305 check_err(
306 self.context.as_ptr(),
307 sys::nix_init_bool(self.context.as_ptr(), v.inner.as_ptr(), b),
308 )?;
309 }
310 Ok(v)
311 }
312
313 pub fn make_null(&self) -> Result<Value<'_>> {
319 let v = self.alloc_value()?;
320 unsafe {
322 check_err(
323 self.context.as_ptr(),
324 sys::nix_init_null(self.context.as_ptr(), v.inner.as_ptr()),
325 )?;
326 }
327 Ok(v)
328 }
329
330 pub fn make_string(&self, s: &str) -> Result<Value<'_>> {
337 let v = self.alloc_value()?;
338 let s_c = CString::new(s)?;
339 unsafe {
341 check_err(
342 self.context.as_ptr(),
343 sys::nix_init_string(
344 self.context.as_ptr(),
345 v.inner.as_ptr(),
346 s_c.as_ptr(),
347 ),
348 )?;
349 }
350 Ok(v)
351 }
352
353 pub fn make_path(&self, path: impl AsRef<Path>) -> Result<Value<'_>> {
370 let v = self.alloc_value()?;
371 let path_str = path
372 .as_ref()
373 .to_str()
374 .ok_or_else(|| Error::Unknown("Path is not valid UTF-8".to_string()))?;
375 let path_c = CString::new(path_str)?;
376
377 #[cfg(feature = "shim")]
380 if path.as_ref().is_absolute()
381 && path_str.starts_with("/nix/store/")
382 && is_pure_eval()
383 {
384 self.allow_store_path(path_str)?;
385 }
386
387 unsafe {
389 check_err(
390 self.context.as_ptr(),
391 sys::nix_init_path_string(
392 self.context.as_ptr(),
393 self.inner.as_ptr(),
394 v.inner.as_ptr(),
395 path_c.as_ptr(),
396 ),
397 )?;
398 }
399 Ok(v)
400 }
401
402 #[cfg(feature = "shim")]
413 pub fn allow_store_path(&self, path: &str) -> Result<()> {
414 let path_c = CString::new(path)?;
415 unsafe {
417 check_err(
418 self.context.as_ptr(),
419 sys::nix_eval_state_allow_path(
420 self.context.as_ptr(),
421 self.inner.as_ptr(),
422 path_c.as_ptr(),
423 ),
424 )
425 }
426 }
427
428 pub fn make_list(&self, items: &[&Value<'_>]) -> Result<Value<'_>> {
434 let builder = unsafe {
436 sys::nix_make_list_builder(
437 self.context.as_ptr(),
438 self.inner.as_ptr(),
439 items.len(),
440 )
441 };
442 if builder.is_null() {
443 return Err(Error::NullPointer);
444 }
445
446 struct ListBuilderGuard(*mut sys::ListBuilder);
447 impl Drop for ListBuilderGuard {
448 fn drop(&mut self) {
449 unsafe { sys::nix_list_builder_free(self.0) };
450 }
451 }
452 let _guard = ListBuilderGuard(builder);
453
454 for (i, item) in items.iter().enumerate() {
455 unsafe {
457 check_err(
458 self.context.as_ptr(),
459 sys::nix_list_builder_insert(
460 self.context.as_ptr(),
461 builder,
462 i as std::os::raw::c_uint,
463 item.inner.as_ptr(),
464 ),
465 )?;
466 }
467 }
468
469 let result = self.alloc_value()?;
470 unsafe {
472 check_err(
473 self.context.as_ptr(),
474 sys::nix_make_list(
475 self.context.as_ptr(),
476 builder,
477 result.inner.as_ptr(),
478 ),
479 )?;
480 }
481
482 Ok(result)
483 }
484
485 pub fn make_attrs<'s>(
492 &'s self,
493 pairs: &[(&str, &Value<'_>)],
494 ) -> Result<Value<'s>> {
495 let builder = unsafe {
497 sys::nix_make_bindings_builder(
498 self.context.as_ptr(),
499 self.inner.as_ptr(),
500 pairs.len(),
501 )
502 };
503 if builder.is_null() {
504 return Err(Error::NullPointer);
505 }
506
507 struct BindingsBuilderGuard(*mut sys::BindingsBuilder);
508 impl Drop for BindingsBuilderGuard {
509 fn drop(&mut self) {
510 unsafe { sys::nix_bindings_builder_free(self.0) };
511 }
512 }
513 let _guard = BindingsBuilderGuard(builder);
514
515 for (key, value) in pairs {
516 let key_c = CString::new(*key)?;
517 unsafe {
519 check_err(
520 self.context.as_ptr(),
521 sys::nix_bindings_builder_insert(
522 self.context.as_ptr(),
523 builder,
524 key_c.as_ptr(),
525 value.inner.as_ptr(),
526 ),
527 )?;
528 }
529 }
530
531 let result = self.alloc_value()?;
532 unsafe {
534 check_err(
535 self.context.as_ptr(),
536 sys::nix_make_attrs(
537 self.context.as_ptr(),
538 result.inner.as_ptr(),
539 builder,
540 ),
541 )?;
542 }
543
544 Ok(result)
545 }
546
547 #[cfg(feature = "shim")]
558 pub fn get_derivation(&self, value: &Value<'_>) -> Result<Option<StorePath>> {
559 let path_ptr = unsafe {
561 sys::nix_get_derivation(
562 self.context.as_ptr(),
563 self.inner.as_ptr(),
564 value.inner.as_ptr(),
565 false,
566 )
567 };
568
569 if path_ptr.is_null() {
570 unsafe {
574 let ctx = self.context.as_ptr();
575 let code = sys::nix_err_code(ctx);
576 check_err(ctx, code)?;
577 }
578 return Ok(None);
579 }
580
581 let inner = NonNull::new(path_ptr).ok_or(Error::NullPointer)?;
582 Ok(Some(StorePath {
583 inner,
584 _context: Arc::clone(&self.context),
585 }))
586 }
587
588 #[cfg(feature = "shim")]
604 pub fn auto_call_function<'s>(
605 &'s self,
606 auto_args: Option<&Value<'_>>,
607 fn_val: &Value<'_>,
608 ) -> Result<Value<'s>> {
609 let result = self.alloc_value()?;
610 let auto_args_ptr =
611 auto_args.map_or(std::ptr::null_mut(), |v| v.inner.as_ptr());
612
613 unsafe {
616 check_err(
617 self.context.as_ptr(),
618 sys::nix_value_auto_call_function(
619 self.context.as_ptr(),
620 self.inner.as_ptr(),
621 auto_args_ptr,
622 fn_val.inner.as_ptr(),
623 result.inner.as_ptr(),
624 ),
625 )?;
626 }
627
628 Ok(result)
629 }
630
631 pub(crate) unsafe fn as_ptr(&self) -> *mut sys::EvalState {
637 self.inner.as_ptr()
638 }
639}
640
641impl Drop for EvalState {
642 fn drop(&mut self) {
643 unsafe {
645 sys::nix_state_free(self.inner.as_ptr());
646 }
647 }
648}
649
650unsafe impl Send for EvalState {}