1use serde::Serialize;23use crate::{NixSession, Value};45#[derive(Clone)]6pub struct NixExprBuilder {7 pub(crate) out: String,8 used_fields: Vec<Value>,9}10pub trait AttrSetValue {11 fn to_builder(self) -> NixExprBuilder;12}13trait Primitive {}1415macro_rules! impl_primitive {16 ($($t:ty),+) => {17 $(18 impl Primitive for $t {}19 )+20 };21}22impl_primitive!(String, bool, &'static str);2324impl<T> AttrSetValue for T25where26 27 T: Serialize + Primitive,28{29 fn to_builder(self) -> NixExprBuilder {30 let serialized = nixlike::serialize(self).expect("invalid value for apply");31 NixExprBuilder {32 out: serialized.trim_end().to_owned(),33 used_fields: Vec::new(),34 }35 }36}37impl AttrSetValue for Value {38 fn to_builder(self) -> NixExprBuilder {39 NixExprBuilder {40 out: format!("sess_field_{}", self.session_field_id()),41 used_fields: vec![self],42 }43 }44}45impl<T> AttrSetValue for Vec<T>46where47 T: AttrSetValue,48{49 fn to_builder(self) -> NixExprBuilder {50 let mut builder = NixExprBuilder::list();51 for v in self {52 builder.list_value(NixExprBuilder::attrset_value(v));53 }54 builder.list_end();55 builder56 }57}5859impl NixExprBuilder {60 pub fn object() -> Self {61 NixExprBuilder {62 out: "{ ".to_owned(),63 used_fields: Vec::new(),64 }65 }66 pub fn list() -> Self {67 NixExprBuilder {68 out: "[".to_owned(),69 used_fields: Vec::new(),70 }71 }72 pub fn string(s: &str) -> Self {73 NixExprBuilder {74 out: nixlike::serialize(s)75 .expect("no problems with serializing_string")76 .trim_end()77 .to_owned(),78 used_fields: Vec::new(),79 }80 }81 pub fn attrset_value(v: impl AttrSetValue) -> Self {82 v.to_builder()83 }84 pub fn serialized(v: impl Serialize) -> Self {85 let serialized = nixlike::serialize(v).expect("invalid value for apply");86 Self {87 out: serialized.trim_end().to_owned(),88 used_fields: Vec::new(),89 }90 }91 pub fn value(f: Value) -> Self {92 Self {93 out: format!("sess_field_{}", f.session_field_id()),94 used_fields: vec![f],95 }96 }97 pub fn end_obj(&mut self) {98 self.out.push('}');99 }100 pub fn obj_key(&mut self, name: Self, value: Self) {101 self.out.push_str(r#""${"#);102 self.extend(name);103 self.out.push_str(r#"}" = "#);104 self.extend(value);105 self.out.push_str("; ");106 }107 pub fn list_value(&mut self, value: Self) {108 self.extend(value);109 self.out.push(' ');110 }111 pub fn list_end(&mut self) {112 self.out.push(']');113 }114115 pub fn extend(&mut self, e: Self) {116 self.out.push_str(&e.out);117 self.used_fields.extend(e.used_fields);118 }119120 #[allow(dead_code)]121 pub fn session(&self) -> NixSession {122 let mut session = None;123 for ele in &self.used_fields {124 if session.is_none() {125 session = Some(ele.session());126 continue;127 }128 let session = session.as_ref().expect("checked");129 let ele_sess = ele.session();130 assert!(131 NixSession::ptr_eq(session, &ele_sess),132 "can't mix fields from different session"133 );134 }135 session.expect("expr without fields used")136 }137 #[allow(dead_code)]138 pub fn index_attr(&mut self, s: &str) {139 let escaped = nixlike::serialize(s).expect("string");140 self.out.push('.');141 self.out.push_str(escaped.trim_end());142 }143}144145#[macro_export]146macro_rules! nix_expr_inner {147 148 (@obj($o:ident) $field:ident$(, $($tt:tt)*)?) => {{149 $o.obj_key(150 NixExprBuilder::string(stringify!($field)),151 NixExprBuilder::attrset_value($field),152 );153 $(nix_expr_inner!(@obj($o) $($tt)*);)?154 }};155 (@obj($o:ident) $field:ident: $v:expr$(, $($tt:tt)*)?) => {{156 $o.obj_key(157 NixExprBuilder::string(stringify!($field)),158 NixExprBuilder::attrset_value($v),159 );160 $(nix_expr_inner!(@obj($o) $($tt)*);)?161 }};162 (@obj($o:ident)) => {{}};163 (Obj { $($tt:tt)* }) => {{164 use $crate::{macros::NixExprBuilder, nix_expr_inner};165 let mut out = NixExprBuilder::object();166 nix_expr_inner!(@obj(out) $($tt)*);167 out.end_obj();168 out169 }};170 (@field($o:ident) . $var:ident $($tt:tt)*) => {{171 $o.index_attr(stringify!($var));172 nix_expr_inner!(@field($o) $($tt)*);173 }};174 (@field($o:ident) [{ $v:expr }] $($tt:tt)*) => {{175 $o.push(Index::attr(&$v));176 nix_expr_inner!(@o($o) $($tt)*);177 }};178 (@field($o:ident) [ $($var:tt)+ ] $($tt:tt)*) => {{179 $o.push(Index::Expr($crate::nix_expr_inner!($($var)+)));180 nix_expr_inner!(@o($o) $($tt)*);181 }};182 (@field($o:ident) ($($var:tt)*) $($tt:tt)*) => {183 $o.push(Index::ExprApply($crate::nix_expr_inner!($($var)+)));184 nix_expr_inner!(@o($o) $($tt)*);185 };186 (@field($o:ident)) => {};187 ($field:ident $($tt:tt)*) => {{188 use $crate::{macros::NixExprBuilder, nix_expr_inner};189 190 #[allow(unused_mut)]191 let mut out = NixExprBuilder::value($field.clone());192 nix_expr_inner!(@field(out) $($tt)*);193 out194 }};195 ($v:literal) => {{196 use $crate::macros::NixExprBuilder;197 NixExprBuilder::string($v)198 }};199 ({$v:expr}) => {{200 use $crate::macros::NixExprBuilder;201 NixExprBuilder::serialized(&$v)202 }}203}204#[macro_export]205macro_rules! nix_expr {206 ($($tt:tt)+) => {{207 use $crate::{macros::{NixExprBuilder}, Value, nix_expr_inner};208 let expr = nix_expr_inner!($($tt)+);209 Field::new(expr.session(), expr.out)210 }};211}212213#[macro_export]214macro_rules! nix_go {215 (@o($o:ident) . $var:ident $($tt:tt)*) => {{216 $o.push(Index::attr(stringify!($var)));217 nix_go!(@o($o) $($tt)*);218 }};219 (@o($o:ident) [{ $v:expr }] $($tt:tt)*) => {{220 $o.push(Index::attr(&$v));221 nix_go!(@o($o) $($tt)*);222 }};223 (@o($o:ident) [ $($var:tt)+ ] $($tt:tt)*) => {{224 $o.push(Index::Expr($crate::nix_expr_inner!($($var)+)));225 nix_go!(@o($o) $($tt)*);226 }};227 (@o($o:ident) ($($var:tt)*) $($tt:tt)*) => {228 $o.push(Index::ExprApply($crate::nix_expr_inner!($($var)+)));229 nix_go!(@o($o) $($tt)*);230 };231 (@o($o:ident) | $($var:tt)*) => {232 $o.push(Index::Pipe($crate::nix_expr_inner!($($var)+)));233 };234 (@o($o:ident) + $($var:tt)*) => {235 $o.push(Index::Merge($crate::nix_expr_inner!($($var)+)));236 };237 (@o($o:ident)) => {};238 ($field:ident $($tt:tt)+) => {{239 use $crate::{nix_go, Index};240 let field = $field.clone();241 let mut out = vec![];242 nix_go!(@o(out) $($tt)*);243 field.select(out).await?244 }}245}246#[macro_export]247macro_rules! nix_go_json {248 ($($tt:tt)*) => {{249 $crate::nix_go!($($tt)*).as_json().await?250 }};251}