//! Defines the public-facing data structures for Nix values. //! //! These types are used to represent the final result of an evaluation and are //! designed to be user-friendly and serializable. They are distinct from the //! internal `Value` types used during evaluation in `nixjit_eval`. use core::fmt::{Debug, Display, Formatter, Result as FmtResult}; use core::hash::Hash; use core::ops::Deref; use std::borrow::Cow; use std::collections::BTreeMap; use std::sync::LazyLock; use derive_more::{Constructor, IsVariant, Unwrap}; use regex::Regex; /// Represents a constant, primitive value in Nix. #[derive(Debug, Clone, Copy, PartialEq, IsVariant, Unwrap)] pub enum Const { /// A boolean value (`true` or `false`). Bool(bool), /// A 64-bit signed integer. Int(i64), /// A 64-bit floating-point number. Float(f64), /// The `null` value. Null, } impl Display for Const { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { use Const::*; match self { Int(x) => write!(f, "{x}"), Float(x) => write!(f, "{x}"), Bool(x) => write!(f, "{x}"), Null => write!(f, "null"), } } } impl From for Const { fn from(value: bool) -> Self { Const::Bool(value) } } impl From for Const { fn from(value: i64) -> Self { Const::Int(value) } } impl From for Const { fn from(value: f64) -> Self { Const::Float(value) } } /// Represents a Nix symbol, which is used as a key in attribute sets. #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Constructor)] pub struct Symbol(String); impl> From for Symbol { fn from(value: T) -> Self { Symbol(value.into()) } } /// Formats a string slice as a Nix symbol, quoting it if necessary. pub fn format_symbol<'a>(sym: impl Into>) -> Cow<'a, str> { let sym = sym.into(); if REGEX.is_match(&sym) { sym } else { Cow::Owned(format!(r#""{sym}""#)) } } impl Display for Symbol { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { if self.normal() { write!(f, "{}", self.0) } else { write!(f, r#""{}""#, self.0) } } } static REGEX: LazyLock = LazyLock::new(|| Regex::new(r"^[a-zA-Z_][a-zA-Z0-9_'-]*$").unwrap()); impl Symbol { /// Checks if the symbol is a "normal" identifier that doesn't require quotes. fn normal(&self) -> bool { REGEX.is_match(self) } } impl Deref for Symbol { type Target = str; fn deref(&self) -> &Self::Target { &self.0 } } impl Symbol { /// Consumes the `Symbol`, returning its inner `String`. pub fn into_inner(self) -> String { self.0 } /// Returns a reference to the inner `String`. pub fn as_inner(&self) -> &String { &self.0 } } /// Represents a Nix attribute set, which is a map from symbols to values. #[derive(Constructor, Clone, PartialEq)] pub struct AttrSet { data: BTreeMap, } impl Debug for AttrSet { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { use Value::*; write!(f, "{{")?; for (k, v) in self.data.iter() { write!(f, " {k:?} = ")?; match v { List(_) => write!(f, "[ ... ];")?, AttrSet(_) => write!(f, "{{ ... }};")?, v => write!(f, "{v:?};")?, } } write!(f, " }}") } } impl Display for AttrSet { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { use Value::*; write!(f, "{{ ")?; let mut first = true; for (k, v) in self.data.iter() { if !first { write!(f, "; ")?; } write!(f, "{k} = ")?; match v { AttrSet(_) => write!(f, "{{ ... }}"), List(_) => write!(f, "[ ... ]"), v => write!(f, "{v}"), }?; first = false; } write!(f, " }}") } } /// Represents a Nix list, which is a vector of values. #[derive(Constructor, Clone, Debug, PartialEq)] pub struct List { data: Vec, } impl Display for List { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { write!(f, "[ ")?; for v in self.data.iter() { write!(f, "{v} ")?; } write!(f, "]") } } /// Represents any possible Nix value that can be returned from an evaluation. #[derive(IsVariant, Unwrap, Clone, Debug, PartialEq)] pub enum Value { /// A constant value (int, float, bool, null). Const(Const), /// A string value. String(String), /// An attribute set. AttrSet(AttrSet), /// A list. List(List), /// A thunk, representing a delayed computation. Thunk, /// A function (lambda). Func, /// A primitive (built-in) operation. PrimOp, /// A partially applied primitive operation. PrimOpApp, /// A marker for a value that has been seen before during serialization, to break cycles. /// This is used to prevent infinite recursion when printing or serializing cyclic data structures. Repeated, } impl Display for Value { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { use Value::*; match self { Const(x) => write!(f, "{x}"), String(x) => write!(f, r#""{x}""#), AttrSet(x) => write!(f, "{x}"), List(x) => write!(f, "{x}"), Thunk => write!(f, ""), Func => write!(f, ""), PrimOp => write!(f, ""), PrimOpApp => write!(f, ""), Repeated => write!(f, ""), } } }