1use std::{ffi::CString, ptr::NonNull, sync::Arc};
20
21use crate::{Context, Error, EvalState, Result, Value, check_err, sys};
22
23pub struct FlakeSettings {
49 pub(crate) inner: NonNull<sys::nix_flake_settings>,
50 _context: Arc<Context>,
51}
52
53impl FlakeSettings {
54 pub fn new(context: &Arc<Context>) -> Result<Self> {
60 let ptr = unsafe { sys::nix_flake_settings_new(context.as_ptr()) };
62
63 let inner = NonNull::new(ptr).ok_or(Error::NullPointer)?;
64
65 Ok(FlakeSettings {
66 inner,
67 _context: Arc::clone(context),
68 })
69 }
70
71 pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_flake_settings {
73 self.inner.as_ptr()
74 }
75}
76
77impl Drop for FlakeSettings {
78 fn drop(&mut self) {
79 unsafe {
81 sys::nix_flake_settings_free(self.inner.as_ptr());
82 }
83 }
84}
85
86unsafe impl Send for FlakeSettings {}
92
93pub struct FetchersSettings {
99 inner: NonNull<sys::nix_fetchers_settings>,
100 _context: Arc<Context>,
101}
102
103impl FetchersSettings {
104 pub fn new(context: &Arc<Context>) -> Result<Self> {
110 let ptr = unsafe { sys::nix_fetchers_settings_new(context.as_ptr()) };
112 let inner = NonNull::new(ptr).ok_or(Error::NullPointer)?;
113 Ok(FetchersSettings {
114 inner,
115 _context: Arc::clone(context),
116 })
117 }
118
119 pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_fetchers_settings {
120 self.inner.as_ptr()
121 }
122}
123
124impl Drop for FetchersSettings {
125 fn drop(&mut self) {
126 unsafe {
128 sys::nix_fetchers_settings_free(self.inner.as_ptr());
129 }
130 }
131}
132
133unsafe impl Send for FetchersSettings {}
139
140pub struct FlakeReferenceParseFlags {
146 inner: NonNull<sys::nix_flake_reference_parse_flags>,
147 _context: Arc<Context>,
148}
149
150impl FlakeReferenceParseFlags {
151 pub fn new(
157 context: &Arc<Context>,
158 flake_settings: &FlakeSettings,
159 ) -> Result<Self> {
160 let ptr = unsafe {
162 sys::nix_flake_reference_parse_flags_new(
163 context.as_ptr(),
164 flake_settings.as_ptr(),
165 )
166 };
167 let inner = NonNull::new(ptr).ok_or(Error::NullPointer)?;
168 Ok(FlakeReferenceParseFlags {
169 inner,
170 _context: Arc::clone(context),
171 })
172 }
173
174 pub fn set_base_directory(self, dir: &str) -> Result<Self> {
180 let bytes = dir.as_bytes();
181 unsafe {
183 check_err(
184 self._context.as_ptr(),
185 sys::nix_flake_reference_parse_flags_set_base_directory(
186 self._context.as_ptr(),
187 self.inner.as_ptr(),
188 bytes.as_ptr().cast(),
189 bytes.len(),
190 ),
191 )?;
192 }
193 Ok(self)
194 }
195
196 pub(crate) unsafe fn as_ptr(
197 &self,
198 ) -> *mut sys::nix_flake_reference_parse_flags {
199 self.inner.as_ptr()
200 }
201}
202
203impl Drop for FlakeReferenceParseFlags {
204 fn drop(&mut self) {
205 unsafe {
207 sys::nix_flake_reference_parse_flags_free(self.inner.as_ptr());
208 }
209 }
210}
211
212unsafe impl Send for FlakeReferenceParseFlags {}
218
219#[derive(Debug, Clone, Copy, PartialEq, Eq)]
226pub enum LockMode {
227 Check,
229 Virtual,
231 WriteAsNeeded,
233}
234
235pub struct LockFlags {
240 inner: NonNull<sys::nix_flake_lock_flags>,
241 _context: Arc<Context>,
242 _settings: Arc<FlakeSettings>,
243}
244
245impl LockFlags {
246 pub fn new(
252 context: &Arc<Context>,
253 flake_settings: &Arc<FlakeSettings>,
254 ) -> Result<Self> {
255 let ptr = unsafe {
257 sys::nix_flake_lock_flags_new(context.as_ptr(), flake_settings.as_ptr())
258 };
259 let inner = NonNull::new(ptr).ok_or(Error::NullPointer)?;
260 Ok(LockFlags {
261 inner,
262 _context: Arc::clone(context),
263 _settings: Arc::clone(flake_settings),
264 })
265 }
266
267 pub fn set_mode(self, mode: LockMode) -> Result<Self> {
273 unsafe {
275 let err = match mode {
276 LockMode::Check => {
277 sys::nix_flake_lock_flags_set_mode_check(
278 self._context.as_ptr(),
279 self.inner.as_ptr(),
280 )
281 },
282 LockMode::Virtual => {
283 sys::nix_flake_lock_flags_set_mode_virtual(
284 self._context.as_ptr(),
285 self.inner.as_ptr(),
286 )
287 },
288 LockMode::WriteAsNeeded => {
289 sys::nix_flake_lock_flags_set_mode_write_as_needed(
290 self._context.as_ptr(),
291 self.inner.as_ptr(),
292 )
293 },
294 };
295 check_err(self._context.as_ptr(), err)?;
296 }
297 Ok(self)
298 }
299
300 pub fn add_input_override(
308 self,
309 input_path: &str,
310 flake_ref: &FlakeReference,
311 ) -> Result<Self> {
312 let path_c = CString::new(input_path)?;
313 unsafe {
315 check_err(
316 self._context.as_ptr(),
317 sys::nix_flake_lock_flags_add_input_override(
318 self._context.as_ptr(),
319 self.inner.as_ptr(),
320 path_c.as_ptr(),
321 flake_ref.inner.as_ptr(),
322 ),
323 )?;
324 }
325 Ok(self)
326 }
327
328 pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_flake_lock_flags {
329 self.inner.as_ptr()
330 }
331}
332
333impl Drop for LockFlags {
334 fn drop(&mut self) {
335 unsafe {
337 sys::nix_flake_lock_flags_free(self.inner.as_ptr());
338 }
339 }
340}
341
342unsafe impl Send for LockFlags {}
348
349unsafe extern "C" fn collect_fragment_cb(
352 start: *const std::os::raw::c_char,
353 n: std::os::raw::c_uint,
354 user_data: *mut std::os::raw::c_void,
355) {
356 let result = unsafe { &mut *(user_data as *mut Option<String>) };
357 if !start.is_null() {
358 let bytes =
359 unsafe { std::slice::from_raw_parts(start.cast::<u8>(), n as usize) };
360 *result = std::str::from_utf8(bytes).ok().map(|s| s.to_owned());
361 }
362}
363
364pub struct FlakeReference {
369 inner: NonNull<sys::nix_flake_reference>,
370 _context: Arc<Context>,
371}
372
373impl FlakeReference {
374 pub fn parse(
384 context: &Arc<Context>,
385 fetch_settings: &FetchersSettings,
386 flake_settings: &FlakeSettings,
387 parse_flags: &FlakeReferenceParseFlags,
388 s: &str,
389 ) -> Result<(Self, String)> {
390 let bytes = s.as_bytes();
391
392 let mut out_ptr: *mut sys::nix_flake_reference = std::ptr::null_mut();
393 let mut fragment: Option<String> = None;
394
395 let err = unsafe {
397 sys::nix_flake_reference_and_fragment_from_string(
398 context.as_ptr(),
399 fetch_settings.as_ptr(),
400 flake_settings.as_ptr(),
401 parse_flags.as_ptr(),
402 bytes.as_ptr().cast(),
403 bytes.len(),
404 &mut out_ptr as *mut *mut sys::nix_flake_reference,
405 Some(collect_fragment_cb),
406 &mut fragment as *mut Option<String> as *mut std::os::raw::c_void,
407 )
408 };
409
410 check_err(unsafe { context.as_ptr() }, err)?;
411
412 let inner = NonNull::new(out_ptr).ok_or(Error::NullPointer)?;
413
414 let frag = fragment.unwrap_or_default();
415
416 Ok((
417 FlakeReference {
418 inner,
419 _context: Arc::clone(context),
420 },
421 frag,
422 ))
423 }
424}
425
426impl Drop for FlakeReference {
427 fn drop(&mut self) {
428 unsafe {
430 sys::nix_flake_reference_free(self.inner.as_ptr());
431 }
432 }
433}
434
435unsafe impl Send for FlakeReference {}
443
444pub struct LockedFlake {
450 inner: NonNull<sys::nix_locked_flake>,
451 _context: Arc<Context>,
452}
453
454impl LockedFlake {
455 pub fn lock(
461 context: &Arc<Context>,
462 fetch_settings: &FetchersSettings,
463 flake_settings: &FlakeSettings,
464 eval_state: &EvalState,
465 lock_flags: &LockFlags,
466 flake_ref: &FlakeReference,
467 ) -> Result<Self> {
468 let ptr = unsafe {
470 sys::nix_flake_lock(
471 context.as_ptr(),
472 fetch_settings.as_ptr(),
473 flake_settings.as_ptr(),
474 eval_state.as_ptr(),
475 lock_flags.as_ptr(),
476 flake_ref.inner.as_ptr(),
477 )
478 };
479
480 let inner = NonNull::new(ptr).ok_or(Error::NullPointer)?;
481
482 Ok(LockedFlake {
483 inner,
484 _context: Arc::clone(context),
485 })
486 }
487
488 pub fn output_attrs<'s>(
496 &self,
497 flake_settings: &FlakeSettings,
498 eval_state: &'s EvalState,
499 ) -> Result<Value<'s>> {
500 let ptr = unsafe {
502 sys::nix_locked_flake_get_output_attrs(
503 self._context.as_ptr(),
504 flake_settings.as_ptr(),
505 eval_state.as_ptr(),
506 self.inner.as_ptr(),
507 )
508 };
509
510 let inner = std::ptr::NonNull::new(ptr).ok_or(Error::NullPointer)?;
511
512 Ok(Value {
513 inner,
514 state: eval_state,
515 })
516 }
517}
518
519impl Drop for LockedFlake {
520 fn drop(&mut self) {
521 unsafe {
523 sys::nix_locked_flake_free(self.inner.as_ptr());
524 }
525 }
526}
527
528unsafe impl Send for LockedFlake {}
534
535#[cfg(test)]
536mod tests {
537 use std::sync::Arc;
538
539 use serial_test::serial;
540
541 use super::*;
542 use crate::{Context, EvalStateBuilder, Store};
543
544 fn make_state(ctx: &Arc<Context>) -> (Arc<Store>, EvalState) {
545 let store = Arc::new(Store::open(ctx, None).expect("Failed to open store"));
546 let flake_settings =
547 FlakeSettings::new(ctx).expect("Failed to create flake settings");
548 let state = EvalStateBuilder::new(&store)
549 .expect("Failed to create builder")
550 .with_flake_settings(&flake_settings)
551 .expect("Failed to apply flake settings")
552 .build()
553 .expect("Failed to build state");
554 (store, state)
555 }
556
557 #[test]
558 #[serial]
559 fn test_flake_settings_new() {
560 let ctx = Arc::new(Context::new().expect("Failed to create context"));
561 let _settings =
562 FlakeSettings::new(&ctx).expect("Failed to create flake settings");
563 }
564
565 #[test]
566 #[serial]
567 fn test_flake_settings_with_eval_state() {
568 let ctx = Arc::new(Context::new().expect("Failed to create context"));
569 make_state(&ctx);
570 }
571
572 #[test]
573 #[serial]
574 fn test_fetchers_settings_new() {
575 let ctx = Arc::new(Context::new().expect("Failed to create context"));
576 let _s =
577 FetchersSettings::new(&ctx).expect("Failed to create fetcher settings");
578 }
579
580 #[test]
581 #[serial]
582 fn test_flake_reference_parse_flags_new() {
583 let ctx = Arc::new(Context::new().expect("Failed to create context"));
584 let settings = Arc::new(
585 FlakeSettings::new(&ctx).expect("Failed to create flake settings"),
586 );
587 let _f = FlakeReferenceParseFlags::new(&ctx, &settings)
588 .expect("Failed to create parse flags");
589 }
590
591 #[test]
592 #[serial]
593 fn test_flake_reference_parse_flags_set_base_directory() {
594 let ctx = Arc::new(Context::new().expect("Failed to create context"));
595 let settings = Arc::new(
596 FlakeSettings::new(&ctx).expect("Failed to create flake settings"),
597 );
598 let _f = FlakeReferenceParseFlags::new(&ctx, &settings)
599 .expect("Failed to create parse flags")
600 .set_base_directory("/tmp")
601 .expect("Failed to set base directory");
602 }
603
604 #[test]
605 #[serial]
606 fn test_lock_flags_new() {
607 let ctx = Arc::new(Context::new().expect("Failed to create context"));
608 let settings = Arc::new(
609 FlakeSettings::new(&ctx).expect("Failed to create flake settings"),
610 );
611 let _f =
612 LockFlags::new(&ctx, &settings).expect("Failed to create lock flags");
613 }
614
615 #[test]
616 #[serial]
617 fn test_lock_flags_set_modes() {
618 let ctx = Arc::new(Context::new().expect("Failed to create context"));
619 let settings = Arc::new(
620 FlakeSettings::new(&ctx).expect("Failed to create flake settings"),
621 );
622 let _check = LockFlags::new(&ctx, &settings)
623 .expect("create")
624 .set_mode(LockMode::Check)
625 .expect("set Check");
626 let _virtual = LockFlags::new(&ctx, &settings)
627 .expect("create")
628 .set_mode(LockMode::Virtual)
629 .expect("set Virtual");
630 let _write = LockFlags::new(&ctx, &settings)
631 .expect("create")
632 .set_mode(LockMode::WriteAsNeeded)
633 .expect("set WriteAsNeeded");
634 }
635}