git.delta.rocks / jrsonnet / refs/commits / a70ed4a71e82

difftreelog

refactor cleaner toplevel value handling

Yaroslav Bolyukin2024-12-09parent: #c0e7b60.patch.diff
in: trunk

1 file changed

modifiedcrates/nix-eval/src/value.rsdiffbeforeafterboth
71struct PathDisplay<'i>(&'i [Index]);71struct PathDisplay<'i>(&'i [Index]);
72impl fmt::Display for PathDisplay<'_> {72impl fmt::Display for PathDisplay<'_> {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 if !matches!(self.0.first(), Some(Index::Var(_))) {
75 write!(f, "<unknown>")?;
76 }
74 for i in self.0 {77 for i in self.0 {
75 write!(f, "{i}")?;78 write!(f, "{i}")?;
76 }79 }
77 Ok(())80 Ok(())
78 }81 }
79}82}
80struct ValueInner {83struct ValueInner {
81 full_path: Option<Vec<Index>>,84 full_path: Vec<Index>,
82 session: NixSession,85 session: NixSession,
83 value: Option<u32>,86 value: u32,
84}87}
85#[derive(Clone)]88#[derive(Clone)]
86pub struct Value(Arc<ValueInner>);89pub struct Value(Arc<ValueInner>);
87impl Value {90impl Value {
88 fn root(session: NixSession) -> Self {91 async fn new(session: NixSession, query: &str) -> Result<Self> {
92 let vid = session.0.lock().await.execute_assign(query).await?;
89 Self(Arc::new(ValueInner {93 Ok(Self(Arc::new(ValueInner {
90 full_path: Some(vec![]),94 full_path: vec![],
91 session,95 session,
92 value: None,96 value: vid,
93 }))97 })))
94 }98 }
99 /// Get a top-level binding.
100 ///
101 /// In flake repl session, every output is exposed as top-level binding.
95 async fn new(session: NixSession, query: &str) -> Result<Self> {102 pub async fn binding(session: NixSession, query: &str) -> Result<Self> {
103 // TODO: Verify that query is a valid variable name
96 let vid = session.0.lock().await.execute_assign(query).await?;104 let vid = session.0.lock().await.execute_assign(query).await?;
97 Ok(Self(Arc::new(ValueInner {105 Ok(Self(Arc::new(ValueInner {
98 full_path: None,106 full_path: vec![Index::Var(query.to_owned())],
99 session,107 session,
100 value: Some(vid),108 value: vid,
101 })))109 })))
102 }110 }
103 /// Get a top-level binding.
104 ///
105 /// In flake repl session, every output is exposed as top-level binding.
106 pub async fn binding(session: NixSession, field: &str) -> Result<Self> {
107 Self::root(session).select([Index::var(field)]).await
108 }
109 pub async fn select<'a>(&self, name: impl IntoIterator<Item = Index>) -> Result<Self> {111 pub async fn select(&self, name: impl IntoIterator<Item = Index>) -> Result<Self> {
110 let mut used_fields = Vec::new();112 let mut used_fields = Vec::new();
111 let mut name = name.into_iter();113 let name = name.into_iter();
112114
113 let mut full_path = self.0.full_path.clone();115 let mut full_path = self.0.full_path.clone();
114 let mut query = if let Some(id) = self.0.value {116 let mut query = self.sess_field_name();
115 format!("sess_field_{id}")
116 } else {
117 let first = name.next();
118 if let Some(Index::Var(i)) = first {
119 if let Some(full_path) = &mut full_path {
120 full_path.push(Index::Var(i.clone()));
121 }
122 i.clone()
123 } else {
124 panic!("first path item should be variable, got {first:?}")
125 }
126 };
127 for v in name {117 for v in name {
128 if let Some(full_path) = &mut full_path {
129 full_path.push(v.clone());118 full_path.push(v.clone());
130 }
131 match v {119 match v {
132 Index::Var(_) => panic!("var item may only be first"),120 Index::Var(_) => panic!("var item may only be first"),
133 Index::String(s) => {121 Index::String(s) => {
144 let index = Value::new(self.0.session.clone(), &e.out).await?;132 let index = Value::new(self.0.session.clone(), &e.out).await?;
145 used_fields.push(index.clone());133 used_fields.push(index.clone());
146 query.push('.');134 query.push('.');
147 let index = format!("${{sess_field_{}}}", index.0.value.expect("value"));135 let index = format!("${{sess_field_{}}}", index.0.value);
148 query.push_str(&index);136 query.push_str(&index);
149 }137 }
150 Index::ExprApply(e) => {138 Index::ExprApply(e) => {
151 let index = Value::new(self.0.session.clone(), &e.out).await?;139 let index = Value::new(self.0.session.clone(), &e.out).await?;
152 used_fields.push(index.clone());140 used_fields.push(index.clone());
153 query.push(' ');141 query.push(' ');
154 let index = format!("sess_field_{}", index.0.value.expect("value"));142 let index = format!("sess_field_{}", index.0.value);
155 query.push_str(&index);143 query.push_str(&index);
156 query = format!("({query})");144 query = format!("({query})");
157 }145 }
158 Index::Pipe(v) => {146 Index::Pipe(v) => {
159 let index = Value::new(self.0.session.clone(), &v.out).await?;147 let index = Value::new(self.0.session.clone(), &v.out).await?;
160 used_fields.push(index.clone());148 used_fields.push(index.clone());
161 let index = format!("sess_field_{}", index.0.value.expect("value"));149 let index = format!("sess_field_{}", index.0.value);
162 query = format!("({index} {query})");150 query = format!("({index} {query})");
163 }151 }
164 Index::Merge(v) => {152 Index::Merge(v) => {
165 let index = Value::new(self.0.session.clone(), &v.out).await?;153 let index = Value::new(self.0.session.clone(), &v.out).await?;
166 used_fields.push(index.clone());154 used_fields.push(index.clone());
167 let index = format!("sess_field_{}", index.0.value.expect("value"));155 let index = format!("sess_field_{}", index.0.value);
168 query = format!("({query} // {index})");156 query = format!("({query} // {index})");
169 }157 }
170 }158 }
182 Ok(Self(Arc::new(ValueInner {170 Ok(Self(Arc::new(ValueInner {
183 full_path,171 full_path,
184 session: self.0.session.clone(),172 session: self.0.session.clone(),
185 value: Some(vid),173 value: vid,
186 })))174 })))
187 }175 }
188 pub async fn as_json<V: DeserializeOwned>(&self) -> Result<V> {176 pub async fn as_json<V: DeserializeOwned>(&self) -> Result<V> {
189 let id = self.0.value.expect("can't serialize root field");177 let query = self.sess_field_name();
190 let query = format!("sess_field_{id}");
191 self.0178 self.0
192 .session179 .session
193 .0180 .0
199 }186 }
200 #[allow(dead_code)]187 #[allow(dead_code)]
201 pub async fn has_field(&self, name: &str) -> Result<bool> {188 pub async fn has_field(&self, name: &str) -> Result<bool> {
202 let id = self.0.value.expect("can't list root fields");
203 let key = nixlike::escape_string(name);189 let key = nixlike::escape_string(name);
204 let query = format!("sess_field_{id} ? {key}");190 let query = format!("{} ? {key}", self.sess_field_name());
205 self.0191 self.0
206 .session192 .session
207 .0193 .0
212 .map_err(|e| e.context(self.attribute()))198 .map_err(|e| e.context(self.attribute()))
213 }199 }
214 pub async fn list_fields(&self) -> Result<Vec<String>> {200 pub async fn list_fields(&self) -> Result<Vec<String>> {
215 let id = self.0.value.expect("can't list root fields");
216 let query = format!("builtins.attrNames sess_field_{id}");201 let query = format!("builtins.attrNames {}", self.sess_field_name());
217 self.0202 self.0
218 .session203 .session
219 .0204 .0
224 .map_err(|e| e.context(self.attribute()))209 .map_err(|e| e.context(self.attribute()))
225 }210 }
226 pub async fn type_of(&self) -> Result<String> {211 pub async fn type_of(&self) -> Result<String> {
227 let id = self.0.value.expect("can't list root fields");
228 let query = format!("builtins.typeOf sess_field_{id}");212 let query = format!("builtins.typeOf {}", self.sess_field_name());
229 self.0213 self.0
230 .session214 .session
231 .0215 .0
240 let import = Self::new(self.0.session.clone(), "import").await?;224 let import = Self::new(self.0.session.clone(), "import").await?;
241 Ok(nix_go!(self | import))225 Ok(nix_go!(self | import))
242 }226 }
227 fn sess_field_name(&self) -> String {
228 format!("sess_field_{}", self.0.value)
229 }
243 pub async fn build_maybe_batch(230 pub async fn build_maybe_batch(
244 &self,231 &self,
245 batch: Option<NixBuildBatch>,232 batch: Option<NixBuildBatch>,
251 }238 }
252 }239 }
253 pub async fn build(&self) -> Result<HashMap<String, PathBuf>> {240 pub async fn build(&self) -> Result<HashMap<String, PathBuf>> {
254 let id = self.0.value.expect("can't use build on not-value");
255 let query = format!(":b sess_field_{id}");241 let query = format!(":b {}", self.sess_field_name());
256 let vid = self242 let vid = self
257 .0243 .0
258 .session244 .session
284 }270 }
285 /// Weakly convert string-like types (derivation/path/string) to string271 /// Weakly convert string-like types (derivation/path/string) to string
286 pub async fn to_string_weak(&self) -> Result<String> {272 pub async fn to_string_weak(&self) -> Result<String> {
287 let id = self.0.value.expect("can't use build on not-value");
288 let query = format!("\"${{sess_field_{id}}}\"");273 let query = format!("\"${{{}}}\"", self.sess_field_name());
289 let vid: String = self274 let vid: String = self
290 .0275 .0
291 .session276 .session
298 }283 }
299284
300 fn attribute(&self) -> String {285 fn attribute(&self) -> String {
301 if let Some(full_path) = &self.0.full_path {286 PathDisplay(&self.0.full_path).to_string()
302 PathDisplay(full_path).to_string()
303 } else {
304 "<root>".to_owned()
305 }
306 }287 }
307288
308 pub(crate) fn session(&self) -> NixSession {289 pub(crate) fn session(&self) -> NixSession {
309 self.0.session.clone()290 self.0.session.clone()
310 }291 }
311292
312 pub(crate) fn session_field_id(&self) -> u32 {293 pub(crate) fn session_field_id(&self) -> u32 {
313 self.0.value.expect("not root")294 self.0.value
314 }295 }
315}296}
316impl Drop for ValueInner {297impl Drop for ValueInner {
317 fn drop(&mut self) {298 fn drop(&mut self) {
318 if let Some(id) = self.value {
319 if let Ok(mut lock) = self.session.0.try_lock() {299 if let Ok(mut lock) = self.session.0.try_lock() {
320 lock.free_list.push(id)300 lock.free_list.push(self.value)
321 }301 }
322 // Leaked302 // Leaked
323 }
324 }303 }
325}304}
326305