1use bstr::{ByteSlice, ByteVec};
7use builtin_macros::builtins;
8use genawaiter::rc::Gen;
9use regex::Regex;
10use std::cmp::{self, Ordering};
11use std::collections::BTreeMap;
12use std::collections::VecDeque;
13use std::path::PathBuf;
14
15use crate::arithmetic_op;
16use crate::value::PointerEquality;
17use crate::vm::generators::{self, GenCo};
18use crate::warnings::WarningKind;
19use crate::{
20 self as snix_eval,
21 builtins::hash::hash_nix_string,
22 errors::{CatchableErrorKind, ErrorKind},
23 value::{CoercionKind, NixAttrs, NixList, NixString, Thunk, Value},
24};
25
26use self::versions::{VersionPart, VersionPartsIter};
27
28mod hash;
29mod to_xml;
30mod versions;
31
32#[cfg(test)]
33pub use to_xml::value_to_xml;
34
35#[cfg(feature = "impure")]
36mod impure;
37
38#[cfg(feature = "impure")]
39pub use impure::impure_builtins;
40
41pub const CURRENT_PLATFORM: &str = env!("SNIX_CURRENT_SYSTEM");
43
44pub async fn coerce_value_to_path(
52 co: &GenCo,
53 v: Value,
54) -> Result<Result<PathBuf, CatchableErrorKind>, ErrorKind> {
55 let value = generators::request_force(co, v).await;
56 if let Value::Path(p) = value {
57 return Ok(Ok(*p));
58 }
59
60 match generators::request_string_coerce(
61 co,
62 value,
63 CoercionKind {
64 strong: false,
65 import_paths: false,
66 },
67 )
68 .await
69 {
70 Ok(vs) => {
71 let path = vs.to_path()?.to_owned();
72 if path.is_absolute() {
73 Ok(Ok(path))
74 } else {
75 Err(ErrorKind::NotAnAbsolutePath(path))
76 }
77 }
78 Err(cek) => Ok(Err(cek)),
79 }
80}
81
82#[builtins]
83mod pure_builtins {
84 use std::ffi::OsString;
85
86 use bstr::{BString, ByteSlice, B};
87 use itertools::Itertools;
88 use os_str_bytes::OsStringBytes;
89 use rustc_hash::{FxHashMap, FxHashSet};
90
91 use crate::{value::PointerEquality, AddContext, NixContext, NixContextElement};
92
93 use super::*;
94
95 macro_rules! try_value {
96 ($value:expr) => {{
97 let val = $value;
98 if val.is_catchable() {
99 return Ok(val);
100 }
101 val
102 }};
103 }
104
105 #[builtin("abort")]
106 async fn builtin_abort(co: GenCo, message: Value) -> Result<Value, ErrorKind> {
107 Err(ErrorKind::Abort(message.to_contextful_str()?.to_string()))
113 }
114
115 #[builtin("add")]
116 async fn builtin_add(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
117 arithmetic_op!(&x, &y, +)
118 }
119
120 #[builtin("all")]
121 async fn builtin_all(co: GenCo, pred: Value, list: Value) -> Result<Value, ErrorKind> {
122 for value in list.to_list()?.into_iter() {
123 let pred_result = generators::request_call_with(&co, pred.clone(), [value]).await;
124 let pred_result = try_value!(generators::request_force(&co, pred_result).await);
125
126 if !pred_result.as_bool()? {
127 return Ok(Value::Bool(false));
128 }
129 }
130
131 Ok(Value::Bool(true))
132 }
133
134 #[builtin("any")]
135 async fn builtin_any(co: GenCo, pred: Value, list: Value) -> Result<Value, ErrorKind> {
136 for value in list.to_list()?.into_iter() {
137 let pred_result = generators::request_call_with(&co, pred.clone(), [value]).await;
138 let pred_result = try_value!(generators::request_force(&co, pred_result).await);
139
140 if pred_result.as_bool()? {
141 return Ok(Value::Bool(true));
142 }
143 }
144
145 Ok(Value::Bool(false))
146 }
147
148 #[builtin("attrNames")]
149 async fn builtin_attr_names(co: GenCo, set: Value) -> Result<Value, ErrorKind> {
150 let xs = set.to_attrs()?;
151 let mut output = Vec::with_capacity(xs.len());
152
153 for (key, _val) in xs.iter_sorted() {
154 output.push(Value::from(key.clone()));
155 }
156
157 Ok(Value::List(NixList::construct(output.len(), output)))
158 }
159
160 #[builtin("attrValues")]
161 async fn builtin_attr_values(co: GenCo, set: Value) -> Result<Value, ErrorKind> {
162 let xs = set.to_attrs()?;
163 let mut output = Vec::with_capacity(xs.len());
164
165 for (_key, val) in xs.iter_sorted() {
166 output.push(val.clone());
167 }
168
169 Ok(Value::List(NixList::construct(output.len(), output)))
170 }
171
172 #[builtin("baseNameOf")]
173 async fn builtin_base_name_of(co: GenCo, s: Value) -> Result<Value, ErrorKind> {
174 let span = generators::request_span(&co).await;
175 let s = s
176 .coerce_to_string(
177 co,
178 CoercionKind {
179 strong: false,
180 import_paths: false,
181 },
182 span,
183 )
184 .await?
185 .to_contextful_str()?;
186
187 let mut bs = (**s).to_owned();
188 if let Some(last_slash) = bs.rfind_char('/') {
189 bs = bs[(last_slash + 1)..].into();
190 }
191 Ok(NixString::new_inherit_context_from(&s, bs).into())
192 }
193
194 #[builtin("bitAnd")]
195 async fn builtin_bit_and(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
196 Ok(Value::Integer(x.as_int()? & y.as_int()?))
197 }
198
199 #[builtin("bitOr")]
200 async fn builtin_bit_or(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
201 Ok(Value::Integer(x.as_int()? | y.as_int()?))
202 }
203
204 #[builtin("bitXor")]
205 async fn builtin_bit_xor(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
206 Ok(Value::Integer(x.as_int()? ^ y.as_int()?))
207 }
208
209 #[builtin("catAttrs")]
210 async fn builtin_cat_attrs(co: GenCo, key: Value, list: Value) -> Result<Value, ErrorKind> {
211 let key = key.to_str()?;
212 let list = list.to_list()?;
213 let mut output = vec![];
214
215 for item in list.into_iter() {
216 let set = generators::request_force(&co, item).await.to_attrs()?;
217
218 if let Some(value) = set.select(&key) {
219 output.push(value.clone());
220 }
221 }
222
223 Ok(Value::List(NixList::construct(output.len(), output)))
224 }
225
226 #[builtin("ceil")]
227 async fn builtin_ceil(co: GenCo, double: Value) -> Result<Value, ErrorKind> {
228 Ok(Value::Integer(double.as_float()?.ceil() as i64))
229 }
230
231 #[builtin("compareVersions")]
232 async fn builtin_compare_versions(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
233 let s1 = x.to_str()?;
234 let s1 = VersionPartsIter::new_for_cmp((&s1).into());
235 let s2 = y.to_str()?;
236 let s2 = VersionPartsIter::new_for_cmp((&s2).into());
237
238 match s1.cmp(s2) {
239 std::cmp::Ordering::Less => Ok(Value::Integer(-1)),
240 std::cmp::Ordering::Equal => Ok(Value::Integer(0)),
241 std::cmp::Ordering::Greater => Ok(Value::Integer(1)),
242 }
243 }
244
245 #[builtin("concatLists")]
246 async fn builtin_concat_lists(co: GenCo, lists: Value) -> Result<Value, ErrorKind> {
247 let mut out = Vec::new();
248
249 for value in lists.to_list()? {
250 let list = try_value!(generators::request_force(&co, value).await).to_list()?;
251 out.extend(list.into_iter());
252 }
253
254 Ok(Value::List(out.into()))
255 }
256
257 #[builtin("concatMap")]
258 async fn builtin_concat_map(co: GenCo, f: Value, list: Value) -> Result<Value, ErrorKind> {
259 let list = list.to_list()?;
260 let mut res = Vec::new();
261 for val in list {
262 let out = generators::request_call_with(&co, f.clone(), [val]).await;
263 let out = try_value!(generators::request_force(&co, out).await);
264 res.extend(out.to_list()?);
265 }
266 Ok(Value::List(res.into()))
267 }
268
269 #[builtin("concatStringsSep")]
270 async fn builtin_concat_strings_sep(
271 co: GenCo,
272 separator: Value,
273 list: Value,
274 ) -> Result<Value, ErrorKind> {
275 let mut separator = separator.to_contextful_str()?;
276
277 let mut context = NixContext::new();
278 if let Some(sep_context) = separator.take_context() {
279 context.extend(sep_context.into_iter())
280 }
281 let list = list.to_list()?;
282 let mut res = BString::default();
283 for (i, val) in list.into_iter().enumerate() {
284 if i != 0 {
285 res.push_str(&separator);
286 }
287 match generators::request_string_coerce(
288 &co,
289 val,
290 CoercionKind {
291 strong: false,
292 import_paths: true,
293 },
294 )
295 .await
296 {
297 Ok(mut s) => {
298 res.push_str(&s);
299 if let Some(other_context) = s.take_context() {
300 context.extend(other_context.into_iter());
301 }
302 }
303 Err(c) => return Ok(Value::Catchable(Box::new(c))),
304 }
305 }
306 Ok(NixString::new_context_from(context, res).into())
308 }
309
310 #[builtin("deepSeq")]
311 async fn builtin_deep_seq(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
312 generators::request_deep_force(&co, x).await;
313 Ok(y)
314 }
315
316 #[builtin("div")]
317 async fn builtin_div(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
318 arithmetic_op!(&x, &y, /)
319 }
320
321 #[builtin("dirOf")]
322 async fn builtin_dir_of(co: GenCo, s: Value) -> Result<Value, ErrorKind> {
323 let is_path = s.is_path();
324 let span = generators::request_span(&co).await;
325 let str = s
326 .coerce_to_string(
327 co,
328 CoercionKind {
329 strong: false,
330 import_paths: false,
331 },
332 span,
333 )
334 .await?
335 .to_contextful_str()?;
336 let result = str
337 .rfind_char('/')
338 .map(|last_slash| {
339 let x = &str[..last_slash];
340 if x.is_empty() {
341 B("/")
342 } else {
343 x
344 }
345 })
346 .unwrap_or(b".");
347 if is_path {
348 Ok(Value::Path(Box::new(PathBuf::from(
349 OsString::assert_from_raw_vec(result.to_owned()),
350 ))))
351 } else {
352 Ok(Value::from(NixString::new_inherit_context_from(
353 &str, result,
354 )))
355 }
356 }
357
358 #[builtin("elem")]
359 async fn builtin_elem(co: GenCo, x: Value, xs: Value) -> Result<Value, ErrorKind> {
360 for val in xs.to_list()? {
361 match generators::check_equality(&co, x.clone(), val, PointerEquality::AllowAll).await?
362 {
363 Ok(true) => return Ok(true.into()),
364 Ok(false) => continue,
365 Err(cek) => return Ok(Value::from(cek)),
366 }
367 }
368 Ok(false.into())
369 }
370
371 #[builtin("elemAt")]
372 async fn builtin_elem_at(co: GenCo, xs: Value, i: Value) -> Result<Value, ErrorKind> {
373 let xs = xs.to_list()?;
374 let i = i.as_int()?;
375 if i < 0 {
376 Err(ErrorKind::IndexOutOfBounds { index: i })
377 } else {
378 match xs.get(i as usize) {
379 Some(x) => Ok(x.clone()),
380 None => Err(ErrorKind::IndexOutOfBounds { index: i }),
381 }
382 }
383 }
384
385 #[builtin("filter")]
386 async fn builtin_filter(co: GenCo, pred: Value, list: Value) -> Result<Value, ErrorKind> {
387 let list: NixList = list.to_list()?;
388 let mut out = Vec::new();
389
390 for value in list {
391 let result = generators::request_call_with(&co, pred.clone(), [value.clone()]).await;
392 let verdict = try_value!(generators::request_force(&co, result).await);
393 if verdict.as_bool()? {
394 out.push(value);
395 }
396 }
397
398 Ok(Value::List(out.into()))
399 }
400
401 #[builtin("floor")]
402 async fn builtin_floor(co: GenCo, double: Value) -> Result<Value, ErrorKind> {
403 Ok(Value::Integer(double.as_float()?.floor() as i64))
404 }
405
406 #[builtin("foldl'")]
407 async fn builtin_foldl(
408 co: GenCo,
409 op: Value,
410 #[lazy] nul: Value,
411 list: Value,
412 ) -> Result<Value, ErrorKind> {
413 let mut nul = nul;
414 let list = list.to_list()?;
415 for val in list {
416 nul = generators::request_call_with(&co, op.clone(), [nul, val]).await;
420 nul = generators::request_force(&co, nul).await;
421 if let c @ Value::Catchable(_) = nul {
422 return Ok(c);
423 }
424 }
425
426 Ok(nul)
427 }
428
429 #[builtin("functionArgs")]
430 async fn builtin_function_args(co: GenCo, f: Value) -> Result<Value, ErrorKind> {
431 let lambda = &f.as_closure()?.lambda();
432 let formals = if let Some(formals) = &lambda.formals {
433 formals
434 } else {
435 return Ok(Value::attrs(NixAttrs::empty()));
436 };
437 Ok(Value::attrs(NixAttrs::from_iter(
438 formals.arguments.iter().map(|(k, v)| (k.clone(), (*v))),
439 )))
440 }
441
442 #[builtin("fromJSON")]
443 async fn builtin_from_json(co: GenCo, json: Value) -> Result<Value, ErrorKind> {
444 let json_str = json.to_str()?;
445 serde_json::from_slice(&json_str).map_err(|err| err.into())
446 }
447
448 #[builtin("toJSON")]
449 async fn builtin_to_json(co: GenCo, val: Value) -> Result<Value, ErrorKind> {
450 match val.into_contextful_json(&co).await {
451 Err(ErrorKind::CatchableError(catchable)) => Ok(Value::Catchable(Box::new(catchable))),
452 Err(err) => Err(err),
453 Ok((json, context)) => {
454 let json_str = serde_json::to_string(&json)
455 .map_err(|err| ErrorKind::JsonError(err.to_string()))?;
456 Ok(Value::String(NixString::new_context_from(
457 context, json_str,
458 )))
459 }
460 }
461 }
462
463 #[builtin("fromTOML")]
464 async fn builtin_from_toml(co: GenCo, toml: Value) -> Result<Value, ErrorKind> {
465 let toml_str = toml.to_str()?;
466
467 toml::from_str(toml_str.to_str()?).map_err(|err| err.into())
468 }
469
470 #[builtin("genericClosure")]
471 async fn builtin_generic_closure(co: GenCo, input: Value) -> Result<Value, ErrorKind> {
472 let attrs = input.to_attrs()?;
473
474 let mut work_set: VecDeque<Value> =
477 generators::request_force(&co, attrs.select_required("startSet")?.clone())
478 .await
479 .to_list()?
480 .into_iter()
481 .collect();
482
483 let operator = attrs.select_required("operator")?;
484
485 let mut res = Vec::new();
486 let mut done_keys: Vec<Value> = vec![];
487
488 while let Some(val) = work_set.pop_front() {
489 let val = generators::request_force(&co, val).await;
490 let attrs = val.to_attrs()?;
491 let key = attrs.select_required("key")?;
492
493 let value_missing = bgc_insert_key(&co, key.clone(), &mut done_keys).await?;
494
495 if let Err(cek) = value_missing {
496 return Ok(Value::Catchable(Box::new(cek)));
497 }
498
499 if let Ok(false) = value_missing {
500 continue;
501 }
502
503 res.push(val.clone());
504
505 let op_result = generators::request_force(
506 &co,
507 generators::request_call_with(&co, operator.clone(), [val]).await,
508 )
509 .await;
510
511 work_set.extend(op_result.to_list()?.into_iter());
512 }
513
514 Ok(Value::List(NixList::from(res)))
515 }
516
517 #[builtin("genList")]
518 async fn builtin_gen_list(
519 co: GenCo,
520 #[lazy] generator: Value,
522 length: Value,
523 ) -> Result<Value, ErrorKind> {
524 let len = length.as_int()?;
525 let mut out = Vec::with_capacity(
526 len.try_into()
527 .map_err(|_| ErrorKind::Abort(format!("can not create list of size {}", len)))?,
528 );
529
530 let span = generators::request_span(&co).await;
532
533 for i in 0..len {
534 let val = Value::Thunk(Thunk::new_suspended_call(generator.clone(), i.into(), span));
535 out.push(val);
536 }
537
538 Ok(Value::List(out.into()))
539 }
540
541 #[builtin("getAttr")]
542 async fn builtin_get_attr(co: GenCo, key: Value, set: Value) -> Result<Value, ErrorKind> {
543 let k = key.to_str()?;
544 let xs = set.to_attrs()?;
545
546 match xs.select(&k) {
547 Some(x) => Ok(x.clone()),
548 None => Err(ErrorKind::AttributeNotFound {
549 name: k.to_string(),
550 }),
551 }
552 }
553
554 #[builtin("groupBy")]
555 async fn builtin_group_by(co: GenCo, f: Value, list: Value) -> Result<Value, ErrorKind> {
556 let mut res: BTreeMap<NixString, Vec<Value>> = BTreeMap::new();
557 for val in list.to_list()? {
558 let key = try_value!(
559 generators::request_force(
560 &co,
561 generators::request_call_with(&co, f.clone(), [val.clone()]).await,
562 )
563 .await
564 )
565 .to_str()?;
566
567 res.entry(key).or_default().push(val);
568 }
569 Ok(Value::attrs(NixAttrs::from_iter(
570 res.into_iter()
571 .map(|(k, v)| (k, Value::List(NixList::from(v)))),
572 )))
573 }
574
575 #[builtin("hasAttr")]
576 async fn builtin_has_attr(co: GenCo, key: Value, set: Value) -> Result<Value, ErrorKind> {
577 let k = key.to_str()?;
578 let xs = set.to_attrs()?;
579
580 Ok(Value::Bool(xs.contains(&k)))
581 }
582
583 #[builtin("hasContext")]
584 #[allow(non_snake_case)]
585 async fn builtin_hasContext(co: GenCo, e: Value) -> Result<Value, ErrorKind> {
586 if e.is_catchable() {
587 return Ok(e);
588 }
589
590 let v = e.to_contextful_str()?;
591 Ok(Value::Bool(v.has_context()))
592 }
593
594 #[builtin("getContext")]
595 #[allow(non_snake_case)]
596 async fn builtin_getContext(co: GenCo, e: Value) -> Result<Value, ErrorKind> {
597 if e.is_catchable() {
598 return Ok(e);
599 }
600
601 let span = generators::request_span(&co).await;
603 let v = e
604 .coerce_to_string(
605 co,
606 CoercionKind {
607 strong: true,
608 import_paths: true,
609 },
610 span,
611 )
612 .await?;
613 let s = v.to_contextful_str()?;
614
615 let groups = s
616 .iter_context()
617 .flat_map(|context| context.iter())
618 .into_grouping_map_by(|ctx_element| match ctx_element {
624 NixContextElement::Plain(spath) => spath,
625 NixContextElement::Single { derivation, .. } => derivation,
626 NixContextElement::Derivation(drv_path) => drv_path,
627 })
628 .collect::<Vec<_>>();
629
630 let elements = groups
631 .into_iter()
632 .map(|(key, group)| {
633 let mut outputs: Vec<NixString> = Vec::new();
634 let mut is_path = false;
635 let mut all_outputs = false;
636
637 for ctx_element in group {
638 match ctx_element {
639 NixContextElement::Plain(spath) => {
640 debug_assert!(spath == key, "Unexpected group containing mixed keys, expected: {:?}, encountered {:?}", key, spath);
641 is_path = true;
642 }
643
644 NixContextElement::Single { name, derivation } => {
645 debug_assert!(derivation == key, "Unexpected group containing mixed keys, expected: {:?}, encountered {:?}", key, derivation);
646 outputs.push(name.clone().into());
647 }
648
649 NixContextElement::Derivation(drv_path) => {
650 debug_assert!(drv_path == key, "Unexpected group containing mixed keys, expected: {:?}, encountered {:?}", key, drv_path);
651 all_outputs = true;
652 }
653 }
654 }
655
656 let mut vec_attrs: Vec<(&str, Value)> = Vec::new();
659
660 if is_path {
661 vec_attrs.push(("path", true.into()));
662 }
663
664 if all_outputs {
665 vec_attrs.push(("allOutputs", true.into()));
666 }
667
668 if !outputs.is_empty() {
669 outputs.sort();
670 vec_attrs.push(("outputs", Value::List(outputs
671 .into_iter()
672 .map(|s| s.into())
673 .collect::<Vec<Value>>()
674 .into()
675 )));
676 }
677
678 (key.clone(), Value::attrs(NixAttrs::from_iter(vec_attrs.into_iter())))
679 });
680
681 Ok(Value::attrs(NixAttrs::from_iter(elements)))
682 }
683
684 #[builtin("appendContext")]
685 #[allow(non_snake_case)]
686 async fn builtin_appendContext(
687 co: GenCo,
688 origin: Value,
689 added_context: Value,
690 ) -> Result<Value, ErrorKind> {
691 let mut ctx_elements: FxHashSet<NixContextElement> = FxHashSet::default();
710 let span = generators::request_span(&co).await;
711 let origin = origin
712 .coerce_to_string(
713 co,
714 CoercionKind {
715 strong: true,
716 import_paths: true,
717 },
718 span,
719 )
720 .await?;
721 let mut origin = origin.to_contextful_str()?;
722
723 let added_context = added_context.to_attrs()?;
724 for (context_key, context_element) in added_context.into_iter() {
725 let context_element = context_element.to_attrs()?;
729 if let Some(path) = context_element.select("path") {
730 if path.as_bool()? {
731 ctx_elements.insert(NixContextElement::Plain(context_key.to_string()));
732 }
733 }
734 if let Some(all_outputs) = context_element.select("allOutputs") {
735 if all_outputs.as_bool()? {
736 ctx_elements.insert(NixContextElement::Derivation(context_key.to_string()));
739 }
740 }
741 if let Some(some_outputs) = context_element.select("outputs") {
742 let some_outputs = some_outputs.to_list()?;
743 for output in some_outputs.into_iter() {
746 let output = output.to_str()?;
747 ctx_elements.insert(NixContextElement::Single {
748 derivation: context_key.to_string(),
749 name: output.to_string(),
750 });
751 }
752 }
753 }
754
755 if let Some(origin_ctx) = origin.context_mut() {
756 origin_ctx.extend(ctx_elements)
757 }
759
760 Ok(origin.into())
761 }
762
763 #[builtin("hashString")]
764 async fn builtin_hash_string(co: GenCo, algo: Value, s: Value) -> Result<Value, ErrorKind> {
765 hash_nix_string(algo.to_str()?, std::io::Cursor::new(s.to_str()?)).map(Value::from)
766 }
767
768 #[builtin("head")]
769 async fn builtin_head(co: GenCo, list: Value) -> Result<Value, ErrorKind> {
770 if list.is_catchable() {
771 return Ok(list);
772 }
773
774 match list.to_list()?.get(0) {
775 Some(x) => Ok(x.clone()),
776 None => Err(ErrorKind::IndexOutOfBounds { index: 0 }),
777 }
778 }
779
780 #[builtin("intersectAttrs")]
781 async fn builtin_intersect_attrs(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
782 if x.is_catchable() {
783 return Ok(x);
784 }
785 if y.is_catchable() {
786 return Ok(y);
787 }
788 let left_set = x.to_attrs()?;
789 if left_set.is_empty() {
790 return Ok(Value::attrs(NixAttrs::empty()));
791 }
792
793 let right_set = y.to_attrs()?;
794
795 if right_set.is_empty() {
796 return Ok(Value::attrs(NixAttrs::empty()));
797 }
798
799 Ok(Value::attrs(left_set.intersect(&right_set)))
800 }
801
802 #[builtin("isAttrs")]
803 async fn builtin_is_attrs(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
804 if value.is_catchable() {
806 return Ok(value);
807 }
808
809 Ok(Value::Bool(matches!(value, Value::Attrs(_))))
810 }
811
812 #[builtin("isBool")]
813 async fn builtin_is_bool(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
814 if value.is_catchable() {
815 return Ok(value);
816 }
817
818 Ok(Value::Bool(matches!(value, Value::Bool(_))))
819 }
820
821 #[builtin("isFloat")]
822 async fn builtin_is_float(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
823 if value.is_catchable() {
824 return Ok(value);
825 }
826
827 Ok(Value::Bool(matches!(value, Value::Float(_))))
828 }
829
830 #[builtin("isFunction")]
831 async fn builtin_is_function(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
832 if value.is_catchable() {
833 return Ok(value);
834 }
835
836 Ok(Value::Bool(matches!(
837 value,
838 Value::Closure(_) | Value::Builtin(_)
839 )))
840 }
841
842 #[builtin("isInt")]
843 async fn builtin_is_int(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
844 if value.is_catchable() {
845 return Ok(value);
846 }
847
848 Ok(Value::Bool(matches!(value, Value::Integer(_))))
849 }
850
851 #[builtin("isList")]
852 async fn builtin_is_list(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
853 if value.is_catchable() {
854 return Ok(value);
855 }
856
857 Ok(Value::Bool(matches!(value, Value::List(_))))
858 }
859
860 #[builtin("isNull")]
861 async fn builtin_is_null(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
862 if value.is_catchable() {
863 return Ok(value);
864 }
865
866 Ok(Value::Bool(matches!(value, Value::Null)))
867 }
868
869 #[builtin("isPath")]
870 async fn builtin_is_path(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
871 if value.is_catchable() {
872 return Ok(value);
873 }
874
875 Ok(Value::Bool(matches!(value, Value::Path(_))))
876 }
877
878 #[builtin("isString")]
879 async fn builtin_is_string(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
880 if value.is_catchable() {
881 return Ok(value);
882 }
883
884 Ok(Value::Bool(matches!(value, Value::String(_))))
885 }
886
887 #[builtin("length")]
888 async fn builtin_length(co: GenCo, list: Value) -> Result<Value, ErrorKind> {
889 if list.is_catchable() {
890 return Ok(list);
891 }
892 Ok(Value::Integer(list.to_list()?.len() as i64))
893 }
894
895 #[builtin("lessThan")]
896 async fn builtin_less_than(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
897 let span = generators::request_span(&co).await;
898 match x.nix_cmp_ordering(y, co, span).await? {
899 Err(cek) => Ok(Value::from(cek)),
900 Ok(Ordering::Less) => Ok(Value::Bool(true)),
901 Ok(_) => Ok(Value::Bool(false)),
902 }
903 }
904
905 #[builtin("listToAttrs")]
906 async fn builtin_list_to_attrs(co: GenCo, list: Value) -> Result<Value, ErrorKind> {
907 let list = list.to_list()?;
908 let mut map = FxHashMap::default();
909 for val in list {
910 let attrs = try_value!(generators::request_force(&co, val).await).to_attrs()?;
911 let name = try_value!(
912 generators::request_force(&co, attrs.select_required("name")?.clone()).await
913 )
914 .to_str()?;
915 let value = attrs.select_required("value")?.clone();
916 map.entry(name).or_insert(value);
918 }
919 Ok(Value::attrs(NixAttrs::from(map)))
920 }
921
922 #[builtin("map")]
923 async fn builtin_map(co: GenCo, #[lazy] f: Value, list_val: Value) -> Result<Value, ErrorKind> {
924 let list = list_val.to_list()?;
925 let mut out = Vec::with_capacity(list.len());
926
927 let span = generators::request_span(&co).await;
929
930 for val in list {
931 let result = Value::Thunk(Thunk::new_suspended_call(f.clone(), val, span));
932 out.push(result)
933 }
934
935 Ok(Value::List(out.into()))
936 }
937
938 #[builtin("mapAttrs")]
939 async fn builtin_map_attrs(
940 co: GenCo,
941 #[lazy] f: Value,
942 attrs: Value,
943 ) -> Result<Value, ErrorKind> {
944 let attrs = attrs.to_attrs()?;
945 let mut out = FxHashMap::default();
946
947 let span = generators::request_span(&co).await;
949
950 for (key, value) in attrs.into_iter() {
951 let result = Value::Thunk(Thunk::new_suspended_call(
952 f.clone(),
953 key.clone().into(),
954 span,
955 ));
956 let result = Value::Thunk(Thunk::new_suspended_call(result, value, span));
957
958 out.insert(key, result);
959 }
960
961 Ok(Value::attrs(out.into()))
962 }
963
964 #[builtin("match")]
965 async fn builtin_match(co: GenCo, regex: Value, str: Value) -> Result<Value, ErrorKind> {
966 let s = str;
967 if s.is_catchable() {
968 return Ok(s);
969 }
970 let s = s.to_contextful_str()?;
971 let re = regex;
972 if re.is_catchable() {
973 return Ok(re);
974 }
975 let re = re.to_str()?;
976 let re: Regex = Regex::new(&format!("^{}$", re.to_str()?)).unwrap();
977 match re.captures(s.to_str()?) {
978 Some(caps) => Ok(Value::List(
979 caps.iter()
980 .skip(1)
981 .map(|grp| {
982 grp.map(|g| Value::from(g.as_str())).unwrap_or(Value::Null)
990 })
991 .collect::<Vec<Value>>()
992 .into(),
993 )),
994 None => Ok(Value::Null),
995 }
996 }
997
998 #[builtin("mul")]
999 async fn builtin_mul(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
1000 arithmetic_op!(&x, &y, *)
1001 }
1002
1003 #[builtin("parseDrvName")]
1004 async fn builtin_parse_drv_name(co: GenCo, s: Value) -> Result<Value, ErrorKind> {
1005 if s.is_catchable() {
1006 return Ok(s);
1007 }
1008
1009 let s = s.to_str()?;
1012 let slice: &[u8] = s.as_ref();
1013 let (name, dash_and_version) = slice.split_at(
1014 slice
1015 .windows(2)
1016 .enumerate()
1017 .find_map(|x| match x {
1018 (idx, [b'-', c1]) if !c1.is_ascii_alphabetic() => Some(idx),
1019 _ => None,
1020 })
1021 .unwrap_or(slice.len()),
1022 );
1023 let version = dash_and_version
1024 .split_first()
1025 .map(|x| core::str::from_utf8(x.1))
1026 .unwrap_or(Ok(""))?;
1027 Ok(Value::attrs(NixAttrs::from_iter(
1028 [("name", core::str::from_utf8(name)?), ("version", version)].into_iter(),
1029 )))
1030 }
1031
1032 #[builtin("partition")]
1033 async fn builtin_partition(co: GenCo, pred: Value, list: Value) -> Result<Value, ErrorKind> {
1034 let mut right: Vec<Value> = Default::default();
1035 let mut wrong: Vec<Value> = Default::default();
1036
1037 let list: NixList = list.to_list()?;
1038 for elem in list {
1039 let result = generators::request_call_with(&co, pred.clone(), [elem.clone()]).await;
1040
1041 if try_value!(generators::request_force(&co, result).await).as_bool()? {
1042 right.push(elem);
1043 } else {
1044 wrong.push(elem);
1045 };
1046 }
1047
1048 let res = [
1049 ("right", Value::List(NixList::from(right))),
1050 ("wrong", Value::List(NixList::from(wrong))),
1051 ];
1052
1053 Ok(Value::attrs(NixAttrs::from_iter(res.into_iter())))
1054 }
1055
1056 #[builtin("removeAttrs")]
1057 async fn builtin_remove_attrs(
1058 co: GenCo,
1059 attrs: Value,
1060 keys: Value,
1061 ) -> Result<Value, ErrorKind> {
1062 let attrs = attrs.to_attrs()?;
1063 let keys = keys
1064 .to_list()?
1065 .into_iter()
1066 .map(|v| v.to_str())
1067 .collect::<Result<FxHashSet<_>, _>>()?;
1068 let res = attrs.iter().filter_map(|(k, v)| {
1069 if !keys.contains(k) {
1070 Some((k.clone(), v.clone()))
1071 } else {
1072 None
1073 }
1074 });
1075 Ok(Value::attrs(NixAttrs::from_iter(res)))
1076 }
1077
1078 #[builtin("replaceStrings")]
1079 async fn builtin_replace_strings(
1080 co: GenCo,
1081 from: Value,
1082 to: Value,
1083 s: Value,
1084 ) -> Result<Value, ErrorKind> {
1085 let from = from.to_list()?;
1086 for val in &from {
1087 try_value!(generators::request_force(&co, val.clone()).await);
1088 }
1089
1090 let to = to.to_list()?;
1091 for val in &to {
1092 try_value!(generators::request_force(&co, val.clone()).await);
1093 }
1094
1095 let mut string = s.to_contextful_str()?;
1096
1097 let mut res = BString::default();
1098
1099 let mut i: usize = 0;
1100 let mut empty_string_replace = false;
1101 let mut context = NixContext::new();
1102
1103 if let Some(string_context) = string.take_context() {
1104 context.extend(string_context.into_iter());
1105 }
1106
1107 'outer: while i < string.len() {
1114 for elem in std::iter::zip(from.iter(), to.iter()) {
1116 let from = elem.0.to_contextful_str()?;
1117 let mut to = elem.1.to_contextful_str()?;
1118
1119 if i + from.len() > string.len() {
1120 continue;
1121 }
1122
1123 if empty_string_replace && from.is_empty() {
1127 continue;
1128 }
1129
1130 if string[i..i + from.len()] == *from {
1132 res.push_str(&to);
1133 i += from.len();
1134 if let Some(to_ctx) = to.take_context() {
1135 context.extend(to_ctx.into_iter());
1136 }
1137
1138 empty_string_replace = from.is_empty();
1140
1141 continue 'outer;
1142 }
1143 }
1144
1145 res.push_str(&string[i..i + 1]);
1147 i += 1;
1148
1149 empty_string_replace = false;
1152 }
1153
1154 for elem in std::iter::zip(from.iter(), to.iter()) {
1157 let from = elem.0.to_contextful_str()?;
1158 let mut to = elem.1.to_contextful_str()?;
1163
1164 if from.is_empty() {
1165 res.push_str(&to);
1166 if let Some(to_ctx) = to.take_context() {
1167 context.extend(to_ctx.into_iter());
1168 }
1169 break;
1170 }
1171 }
1172
1173 Ok(Value::from(NixString::new_context_from(context, res)))
1174 }
1175
1176 #[builtin("seq")]
1177 async fn builtin_seq(co: GenCo, _x: Value, y: Value) -> Result<Value, ErrorKind> {
1178 Ok(y)
1181 }
1182
1183 #[builtin("split")]
1184 async fn builtin_split(co: GenCo, regex: Value, str: Value) -> Result<Value, ErrorKind> {
1185 if str.is_catchable() {
1186 return Ok(str);
1187 }
1188
1189 if regex.is_catchable() {
1190 return Ok(regex);
1191 }
1192
1193 let s = str.to_contextful_str()?;
1194 let text = s.to_str()?;
1195 let re = regex.to_str()?;
1196 let re = Regex::new(re.to_str()?).unwrap();
1197 let mut capture_locations = re.capture_locations();
1198 let num_captures = capture_locations.len();
1199 let mut ret = Vec::new();
1200 let mut pos = 0;
1201
1202 while let Some(thematch) = re.captures_read_at(&mut capture_locations, text, pos) {
1203 ret.push(Value::from(NixString::new_inherit_context_from(
1205 &s,
1206 &text[pos..thematch.start()],
1207 )));
1208
1209 let v: Vec<Value> = (1..num_captures)
1214 .map(|i| capture_locations.get(i))
1215 .map(|o| {
1216 o.map(|(start, end)| {
1217 Value::from(&text[start..end])
1220 })
1221 .unwrap_or(Value::Null)
1222 })
1223 .collect();
1224 ret.push(Value::List(NixList::from(v)));
1225 if pos == text.len() {
1226 break;
1227 }
1228 pos = thematch.end();
1229 }
1230
1231 ret.push(Value::from(&text[pos..]));
1235
1236 Ok(Value::List(NixList::from(ret)))
1237 }
1238
1239 #[builtin("sort")]
1240 async fn builtin_sort(co: GenCo, comparator: Value, list: Value) -> Result<Value, ErrorKind> {
1241 let list = list.to_list()?;
1242 let mut len = list.len();
1243 let mut data = list.into_inner();
1244
1245 loop {
1256 let mut new_len = 0;
1257 for i in 1..len {
1258 if try_value!(
1259 generators::request_force(
1260 &co,
1261 generators::request_call_with(
1262 &co,
1263 comparator.clone(),
1264 [data[i].clone(), data[i - 1].clone()],
1265 )
1266 .await,
1267 )
1268 .await
1269 )
1270 .as_bool()
1271 .context("evaluating comparator in `builtins.sort`")?
1272 {
1273 data.swap(i, i - 1);
1274 new_len = i;
1275 }
1276 }
1277
1278 if new_len == 0 {
1279 break;
1280 }
1281
1282 len = new_len;
1283 }
1284
1285 Ok(Value::List(data.into()))
1286 }
1287
1288 #[builtin("splitVersion")]
1289 async fn builtin_split_version(co: GenCo, s: Value) -> Result<Value, ErrorKind> {
1290 if s.is_catchable() {
1291 return Ok(s);
1292 }
1293 let s = s.to_str()?;
1294 let s = VersionPartsIter::new((&s).into());
1295
1296 let parts = s
1297 .map(|s| {
1298 Value::from(match s {
1299 VersionPart::Number(n) => n,
1300 VersionPart::Word(w) => w,
1301 })
1302 })
1303 .collect::<Vec<Value>>();
1304 Ok(Value::List(NixList::construct(parts.len(), parts)))
1305 }
1306
1307 #[builtin("stringLength")]
1308 async fn builtin_string_length(co: GenCo, #[lazy] s: Value) -> Result<Value, ErrorKind> {
1309 let span = generators::request_span(&co).await;
1311 let s = s
1312 .coerce_to_string(
1313 co,
1314 CoercionKind {
1315 strong: false,
1316 import_paths: true,
1317 },
1318 span,
1319 )
1320 .await?;
1321
1322 if s.is_catchable() {
1323 return Ok(s);
1324 }
1325
1326 Ok(Value::Integer(s.to_contextful_str()?.len() as i64))
1327 }
1328
1329 #[builtin("sub")]
1330 async fn builtin_sub(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
1331 arithmetic_op!(&x, &y, -)
1332 }
1333
1334 #[builtin("substring")]
1335 async fn builtin_substring(
1336 co: GenCo,
1337 start: Value,
1338 len: Value,
1339 s: Value,
1340 ) -> Result<Value, ErrorKind> {
1341 let beg = start.as_int()?;
1342 let len = len.as_int()?;
1343 let span = generators::request_span(&co).await;
1344 let x = s
1345 .coerce_to_string(
1346 co,
1347 CoercionKind {
1348 strong: false,
1349 import_paths: true,
1350 },
1351 span,
1352 )
1353 .await?;
1354 if x.is_catchable() {
1355 return Ok(x);
1356 }
1357 let x = x.to_contextful_str()?;
1358
1359 if beg < 0 {
1360 return Err(ErrorKind::IndexOutOfBounds { index: beg });
1361 }
1362 let beg = beg as usize;
1363
1364 if beg >= x.len() {
1368 return Ok(Value::from(NixString::new_inherit_context_from(
1369 &x,
1370 BString::default(),
1371 )));
1372 }
1373
1374 let end = if len < 0 {
1375 x.len()
1376 } else {
1377 cmp::min(beg + (len as usize), x.len())
1378 };
1379
1380 Ok(Value::from(NixString::new_inherit_context_from(
1381 &x,
1382 &x[beg..end],
1383 )))
1384 }
1385
1386 #[builtin("tail")]
1387 async fn builtin_tail(co: GenCo, list: Value) -> Result<Value, ErrorKind> {
1388 if list.is_catchable() {
1389 return Ok(list);
1390 }
1391
1392 let xs = list.to_list()?;
1393
1394 if xs.is_empty() {
1395 Err(ErrorKind::TailEmptyList)
1396 } else {
1397 let output = xs.into_iter().skip(1).collect::<Vec<_>>();
1398 Ok(Value::List(NixList::construct(output.len(), output)))
1399 }
1400 }
1401
1402 #[builtin("throw")]
1403 async fn builtin_throw(co: GenCo, message: Value) -> Result<Value, ErrorKind> {
1404 if message.is_catchable() {
1406 return Ok(message);
1407 }
1408
1409 Ok(Value::from(CatchableErrorKind::Throw(message.to_str()?)))
1412 }
1413
1414 #[builtin("toString")]
1415 async fn builtin_to_string(co: GenCo, #[lazy] x: Value) -> Result<Value, ErrorKind> {
1416 let span = generators::request_span(&co).await;
1421 x.coerce_to_string(
1422 co,
1423 CoercionKind {
1424 strong: true,
1425 import_paths: false,
1426 },
1427 span,
1428 )
1429 .await
1430 }
1431
1432 #[builtin("toXML")]
1433 async fn builtin_to_xml(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
1434 let value = generators::request_deep_force(&co, value).await;
1435 if value.is_catchable() {
1436 return Ok(value);
1437 }
1438
1439 let mut buf: Vec<u8> = vec![];
1440 let context = to_xml::value_to_xml(&mut buf, &value)?;
1441
1442 Ok(NixString::new_context_from(context, buf).into())
1443 }
1444
1445 #[builtin("trace")]
1446 async fn builtin_trace(co: GenCo, message: Value, value: Value) -> Result<Value, ErrorKind> {
1447 eprintln!("trace: {} :: {}", message, message.type_of());
1450 Ok(value)
1451 }
1452
1453 #[builtin("toPath")]
1454 async fn builtin_to_path(co: GenCo, s: Value) -> Result<Value, ErrorKind> {
1455 if s.is_catchable() {
1456 return Ok(s);
1457 }
1458
1459 match coerce_value_to_path(&co, s).await? {
1460 Err(cek) => Ok(Value::from(cek)),
1461 Ok(path) => {
1462 let path: Value = crate::value::canon_path(path).into();
1463 let span = generators::request_span(&co).await;
1464 Ok(path
1465 .coerce_to_string(
1466 co,
1467 CoercionKind {
1468 strong: false,
1469 import_paths: false,
1470 },
1471 span,
1472 )
1473 .await?)
1474 }
1475 }
1476 }
1477
1478 #[builtin("tryEval")]
1479 async fn builtin_try_eval(co: GenCo, #[lazy] e: Value) -> Result<Value, ErrorKind> {
1480 let res = match generators::request_try_force(&co, e).await {
1481 Value::Catchable(_) => [("value", false.into()), ("success", false.into())],
1482 value => [("value", value), ("success", true.into())],
1483 };
1484
1485 Ok(Value::attrs(NixAttrs::from_iter(res.into_iter())))
1486 }
1487
1488 #[builtin("typeOf")]
1489 async fn builtin_type_of(co: GenCo, x: Value) -> Result<Value, ErrorKind> {
1490 if x.is_catchable() {
1491 return Ok(x);
1492 }
1493
1494 Ok(Value::from(x.type_of()))
1495 }
1496}
1497
1498async fn bgc_insert_key(
1501 co: &GenCo,
1502 key: Value,
1503 done: &mut Vec<Value>,
1504) -> Result<Result<bool, CatchableErrorKind>, ErrorKind> {
1505 for existing in done.iter() {
1506 match generators::check_equality(
1507 co,
1508 existing.clone(),
1509 key.clone(),
1510 PointerEquality::ForbidAll,
1512 )
1513 .await?
1514 {
1515 Ok(true) => return Ok(Ok(false)),
1516 Ok(false) => (),
1517 Err(cek) => return Ok(Err(cek)),
1518 }
1519 }
1520
1521 done.push(key);
1522 Ok(Ok(true))
1523}
1524
1525pub fn pure_builtins() -> Vec<(&'static str, Value)> {
1528 let mut result = pure_builtins::builtins();
1529
1530 result.push(("nixVersion", Value::from("2.3.17-compat-snix-0.1")));
1532 result.push(("langVersion", Value::Integer(6)));
1533 result.push(("null", Value::Null));
1534 result.push(("true", Value::Bool(true)));
1535 result.push(("false", Value::Bool(false)));
1536
1537 result.push((
1538 "currentSystem",
1539 crate::systems::llvm_triple_to_nix_double(CURRENT_PLATFORM).into(),
1540 ));
1541
1542 result.push((
1543 "__curPos",
1544 Value::Thunk(Thunk::new_suspended_native(Box::new(move || {
1545 Ok(Value::attrs(NixAttrs::from_iter([
1547 ("line", 42.into()),
1548 ("column", 42.into()),
1549 ("file", Value::String("/deep/thought".into())),
1550 ])))
1551 }))),
1552 ));
1553
1554 result
1555}
1556
1557#[builtins]
1558mod placeholder_builtins {
1559 use crate::NixContext;
1560
1561 use super::*;
1562
1563 #[builtin("unsafeDiscardStringContext")]
1564 async fn builtin_unsafe_discard_string_context(
1565 co: GenCo,
1566 s: Value,
1567 ) -> Result<Value, ErrorKind> {
1568 let span = generators::request_span(&co).await;
1569 let mut v = s
1570 .coerce_to_string(
1571 co,
1572 CoercionKind {
1576 strong: false,
1577 import_paths: true,
1578 },
1579 span,
1580 )
1581 .await?
1582 .to_contextful_str()?;
1583 v.clear_context();
1584 Ok(Value::from(v))
1585 }
1586
1587 #[builtin("unsafeDiscardOutputDependency")]
1588 async fn builtin_unsafe_discard_output_dependency(
1589 co: GenCo,
1590 s: Value,
1591 ) -> Result<Value, ErrorKind> {
1592 let span = generators::request_span(&co).await;
1593 let mut v = s
1594 .coerce_to_string(
1595 co,
1596 CoercionKind {
1600 strong: false,
1601 import_paths: true,
1602 },
1603 span,
1604 )
1605 .await?
1606 .to_contextful_str()?;
1607
1608 if let Some(c) = v.take_context() {
1610 let mut context = NixContext::new();
1611 context.extend(c.into_iter().map(|elem| match elem {
1612 crate::NixContextElement::Derivation(drv_path) => {
1613 crate::NixContextElement::Plain(drv_path.to_string())
1614 }
1615 elem => elem.clone(),
1616 }));
1617
1618 return Ok(Value::String(NixString::new_context_from(context, v)));
1619 }
1620 Ok(Value::from(v))
1621 }
1622
1623 #[builtin("addErrorContext")]
1624 async fn builtin_add_error_context(
1625 co: GenCo,
1626 #[lazy] _context: Value,
1627 #[lazy] val: Value,
1628 ) -> Result<Value, ErrorKind> {
1629 generators::emit_warning_kind(&co, WarningKind::NotImplemented("builtins.addErrorContext"))
1630 .await;
1631 Ok(val)
1632 }
1633
1634 #[builtin("unsafeGetAttrPos")]
1635 async fn builtin_unsafe_get_attr_pos(
1636 co: GenCo,
1637 _name: Value,
1638 _attrset: Value,
1639 ) -> Result<Value, ErrorKind> {
1640 generators::emit_warning_kind(
1642 &co,
1643 WarningKind::NotImplemented("builtins.unsafeGetAttrsPos"),
1644 )
1645 .await;
1646 let res = [
1647 ("line", 42.into()),
1648 ("column", 42.into()),
1649 ("file", Value::String("/deep/thought".into())),
1650 ];
1651 Ok(Value::attrs(NixAttrs::from_iter(res.into_iter())))
1652 }
1653}
1654
1655pub fn placeholders() -> Vec<(&'static str, Value)> {
1656 placeholder_builtins::builtins()
1657}