1use serde::Serialize;23use crate::{NixSession, Value};45#[derive(Clone)]6pub struct NixExprBuilder {7 pub(crate) out: String,8 used_fields: Vec<Value>,9}10impl NixExprBuilder {11 pub fn object() -> Self {12 NixExprBuilder {13 out: "{ ".to_owned(),14 used_fields: Vec::new(),15 }16 }17 pub fn string(s: &str) -> Self {18 NixExprBuilder {19 out: nixlike::serialize(s)20 .expect("no problems with serializing_string")21 .trim_end()22 .to_owned(),23 used_fields: Vec::new(),24 }25 }26 pub fn serialized(v: impl Serialize) -> Self {27 let serialized = nixlike::serialize(v).expect("invalid value for apply");28 Self {29 out: serialized.trim_end().to_owned(),30 used_fields: Vec::new(),31 }32 }33 pub fn value(f: Value) -> Self {34 Self {35 out: format!("sess_field_{}", f.session_field_id()),36 used_fields: vec![f],37 }38 }39 pub fn end_obj(&mut self) {40 self.out.push('}');41 }42 pub fn obj_key(&mut self, name: Self, value: Self) {43 self.out.push_str(r#""${"#);44 self.extend(name);45 self.out.push_str(r#"}" = "#);46 self.extend(value);47 self.out.push_str("; ");48 }4950 pub fn extend(&mut self, e: Self) {51 self.out.push_str(&e.out);52 self.used_fields.extend(e.used_fields);53 }5455 #[allow(dead_code)]56 pub fn session(&self) -> NixSession {57 let mut session = None;58 for ele in &self.used_fields {59 if session.is_none() {60 session = Some(ele.session());61 continue;62 }63 let session = session.as_ref().expect("checked");64 let ele_sess = ele.session();65 assert!(66 NixSession::ptr_eq(session, &ele_sess),67 "can't mix fields from different session"68 );69 }70 session.expect("expr without fields used")71 }72 #[allow(dead_code)]73 pub fn index_attr(&mut self, s: &str) {74 let escaped = nixlike::serialize(s).expect("string");75 self.out.push('.');76 self.out.push_str(escaped.trim_end());77 }78}7980#[macro_export]81macro_rules! nix_expr_inner {82 83 (@obj($o:ident) $field:ident, $($tt:tt)*) => {{84 $o.obj_key(85 NixExprBuilder::string(stringify!($field)),86 NixExprBuilder::value($field),87 );88 nix_expr_inner!(@obj($o) $($tt)*);89 }};90 (@obj($o:ident) $field:ident: $v:block, $($tt:tt)*) => {{91 $o.obj_key(92 NixExprBuilder::string(stringify!($field)),93 NixExprBuilder::serialized(&$v),94 );95 nix_expr_inner!(@obj($o) $($tt)*);96 }};97 (@obj($o:ident)) => {{}};98 (Obj { $($tt:tt)* }) => {{99 use $crate::{macros::NixExprBuilder, nix_expr_inner};100 let mut out = NixExprBuilder::object();101 nix_expr_inner!(@obj(out) $($tt)*);102 out.end_obj();103 out104 }};105 (@field($o:ident) . $var:ident $($tt:tt)*) => {{106 $o.index_attr(stringify!($var));107 nix_expr_inner!(@field($o) $($tt)*);108 }};109 (@field($o:ident) [{ $v:expr }] $($tt:tt)*) => {{110 $o.push(Index::attr(&$v));111 nix_expr_inner!(@o($o) $($tt)*);112 }};113 (@field($o:ident) [ $($var:tt)+ ] $($tt:tt)*) => {{114 $o.push(Index::Expr($crate::nix_expr_inner!($($var)+)));115 nix_expr_inner!(@o($o) $($tt)*);116 }};117 (@field($o:ident) ($($var:tt)*) $($tt:tt)*) => {118 $o.push(Index::ExprApply($crate::nix_expr_inner!($($var)+)));119 nix_expr_inner!(@o($o) $($tt)*);120 };121 (@field($o:ident)) => {};122 ($field:ident $($tt:tt)*) => {{123 use $crate::{macros::NixExprBuilder, nix_expr_inner};124 125 #[allow(unused_mut)]126 let mut out = NixExprBuilder::value($field.clone());127 nix_expr_inner!(@field(out) $($tt)*);128 out129 }};130 ($v:literal) => {{131 use $crate::macros::NixExprBuilder;132 NixExprBuilder::string($v)133 }};134 ({$v:expr}) => {{135 use $crate::macros::NixExprBuilder;136 NixExprBuilder::serialized(&$v)137 }}138}139#[macro_export]140macro_rules! nix_expr {141 ($($tt:tt)+) => {{142 use $crate::{macros::{NixExprBuilder}, Value, nix_expr_inner};143 let expr = nix_expr_inner!($($tt)+);144 Field::new(expr.session(), expr.out)145 }};146}147148#[macro_export]149macro_rules! nix_go {150 (@o($o:ident) . $var:ident $($tt:tt)*) => {{151 $o.push(Index::attr(stringify!($var)));152 nix_go!(@o($o) $($tt)*);153 }};154 (@o($o:ident) [{ $v:expr }] $($tt:tt)*) => {{155 $o.push(Index::attr(&$v));156 nix_go!(@o($o) $($tt)*);157 }};158 (@o($o:ident) [ $($var:tt)+ ] $($tt:tt)*) => {{159 $o.push(Index::Expr($crate::nix_expr_inner!($($var)+)));160 nix_go!(@o($o) $($tt)*);161 }};162 (@o($o:ident) ($($var:tt)*) $($tt:tt)*) => {163 $o.push(Index::ExprApply($crate::nix_expr_inner!($($var)+)));164 nix_go!(@o($o) $($tt)*);165 };166 (@o($o:ident) | $($var:tt)*) => {167 $o.push(Index::Pipe($crate::nix_expr_inner!($($var)+)));168 };169 (@o($o:ident)) => {};170 ($field:ident $($tt:tt)+) => {{171 use $crate::{nix_go, Index};172 let field = $field.clone();173 let mut out = vec![];174 nix_go!(@o(out) $($tt)*);175 field.select(out).await?176 }}177}178#[macro_export]179macro_rules! nix_go_json {180 ($($tt:tt)*) => {{181 $crate::nix_go!($($tt)*).as_json().await?182 }};183}