Skip to main content

reddb_server/storage/query/parser/
dml.rs

1//! DML SQL Parser: INSERT, UPDATE, DELETE
2
3use super::super::ast::{
4    AskQuery, DeleteQuery, Expr, Filter, InsertEntityType, InsertQuery, QueryExpr, ReturningItem,
5    UpdateQuery,
6};
7use super::super::lexer::Token;
8use super::error::ParseError;
9use super::Parser;
10use crate::storage::query::sql_lowering::{filter_to_expr, fold_expr_to_value};
11use crate::storage::schema::Value;
12
13/// DoS guard: maximum JSON nesting depth accepted by the parser.
14/// Mirrors typical web-server JSON limits and bails out before stack
15/// usage gets dangerous in downstream traversals.
16pub(crate) const JSON_LITERAL_MAX_DEPTH: u32 = 128;
17
18/// Walk a parsed `JsonValue` tree and bail out if nesting exceeds
19/// `JSON_LITERAL_MAX_DEPTH`. Iterative to avoid the very stack
20/// overflow we're trying to prevent.
21pub(crate) fn json_literal_depth_check(
22    value: &crate::utils::json::JsonValue,
23) -> Result<(), String> {
24    use crate::utils::json::JsonValue;
25    let mut stack: Vec<(&JsonValue, u32)> = vec![(value, 1)];
26    while let Some((node, depth)) = stack.pop() {
27        if depth > JSON_LITERAL_MAX_DEPTH {
28            return Err(format!(
29                "JSON object literal exceeds JSON_LITERAL_MAX_DEPTH ({})",
30                JSON_LITERAL_MAX_DEPTH
31            ));
32        }
33        match node {
34            JsonValue::Object(entries) => {
35                for (_, v) in entries {
36                    stack.push((v, depth + 1));
37                }
38            }
39            JsonValue::Array(items) => {
40                for v in items {
41                    stack.push((v, depth + 1));
42                }
43            }
44            _ => {}
45        }
46    }
47    Ok(())
48}
49
50impl<'a> Parser<'a> {
51    /// Parse: INSERT INTO table [NODE|EDGE|VECTOR|DOCUMENT|KV] (col1, col2) VALUES (val1, val2), (val3, val4) [RETURNING]
52    pub fn parse_insert_query(&mut self) -> Result<QueryExpr, ParseError> {
53        self.expect(Token::Insert)?;
54        self.expect(Token::Into)?;
55        let table = self.expect_ident()?;
56
57        // Check for entity type keyword
58        let entity_type = match self.peek().clone() {
59            Token::Node => {
60                self.advance()?;
61                InsertEntityType::Node
62            }
63            Token::Edge => {
64                self.advance()?;
65                InsertEntityType::Edge
66            }
67            Token::Vector => {
68                self.advance()?;
69                InsertEntityType::Vector
70            }
71            Token::Document => {
72                self.advance()?;
73                InsertEntityType::Document
74            }
75            Token::Kv => {
76                self.advance()?;
77                InsertEntityType::Kv
78            }
79            _ => InsertEntityType::Row,
80        };
81
82        // Parse column list
83        self.expect(Token::LParen)?;
84        let columns = self.parse_ident_list()?;
85        self.expect(Token::RParen)?;
86
87        // Parse VALUES
88        self.expect(Token::Values)?;
89        let mut all_values = Vec::new();
90        let mut all_value_exprs = Vec::new();
91        loop {
92            self.expect(Token::LParen)?;
93            let row_exprs = self.parse_dml_expr_list()?;
94            self.expect(Token::RParen)?;
95            let row_values = row_exprs
96                .iter()
97                .cloned()
98                .map(fold_expr_to_value)
99                .collect::<Result<Vec<_>, _>>()
100                .map_err(|msg| ParseError::new(msg, self.position()))?;
101            all_value_exprs.push(row_exprs);
102            all_values.push(row_values);
103            if !self.consume(&Token::Comma)? {
104                break;
105            }
106        }
107
108        // Parse optional WITH clauses
109        let (ttl_ms, expires_at_ms, with_metadata, auto_embed) = self.parse_with_clauses()?;
110
111        let returning = self.parse_returning_clause()?;
112
113        let suppress_events = if self.consume_ident_ci("SUPPRESS")? {
114            self.expect_ident_ci("EVENTS")?;
115            true
116        } else {
117            false
118        };
119
120        Ok(QueryExpr::Insert(InsertQuery {
121            table,
122            entity_type,
123            columns,
124            value_exprs: all_value_exprs,
125            values: all_values,
126            returning,
127            ttl_ms,
128            expires_at_ms,
129            with_metadata,
130            auto_embed,
131            suppress_events,
132        }))
133    }
134
135    /// Parse TTL duration value using the same logic as CREATE TABLE ... WITH TTL.
136    fn parse_ttl_duration(&mut self) -> Result<u64, ParseError> {
137        // Reuse the DDL TTL parser: expects a number followed by optional unit
138        let ttl_value = self.parse_float()?;
139        let ttl_unit = match self.peek() {
140            Token::Ident(unit) => {
141                let unit = unit.clone();
142                self.advance()?;
143                unit
144            }
145            _ => "s".to_string(),
146        };
147
148        let multiplier_ms = match ttl_unit.to_ascii_lowercase().as_str() {
149            "ms" | "msec" | "millisecond" | "milliseconds" => 1.0,
150            "s" | "sec" | "secs" | "second" | "seconds" => 1_000.0,
151            "m" | "min" | "mins" | "minute" | "minutes" => 60_000.0,
152            "h" | "hr" | "hrs" | "hour" | "hours" => 3_600_000.0,
153            "d" | "day" | "days" => 86_400_000.0,
154            other => {
155                return Err(ParseError::new(
156                    // F-05: render `other` via `{:?}` so caller-controlled
157                    // bytes (CR / LF / NUL / quotes) are escaped before
158                    // landing in the JSON/audit/log/gRPC error sinks.
159                    format!("unsupported TTL unit {other:?}"),
160                    self.position(),
161                ));
162            }
163        };
164
165        Ok((ttl_value * multiplier_ms) as u64)
166    }
167
168    /// Parse WITH clauses: WITH TTL | EXPIRES AT | METADATA | AUTO EMBED
169    /// Returns (ttl_ms, expires_at_ms, metadata, auto_embed)
170    pub fn parse_with_clauses(
171        &mut self,
172    ) -> Result<
173        (
174            Option<u64>,
175            Option<u64>,
176            Vec<(String, Value)>,
177            Option<crate::storage::query::ast::AutoEmbedConfig>,
178        ),
179        ParseError,
180    > {
181        let mut ttl_ms = None;
182        let mut expires_at_ms = None;
183        let mut with_metadata = Vec::new();
184        let mut auto_embed = None;
185
186        while self.consume(&Token::With)? {
187            if self.consume_ident_ci("TTL")? {
188                ttl_ms = Some(self.parse_ttl_duration()?);
189            } else if self.consume_ident_ci("EXPIRES")? {
190                self.expect_ident_ci("AT")?;
191                let ts = self.parse_expires_at_value()?;
192                expires_at_ms = Some(ts);
193            } else if self.consume(&Token::Metadata)? || self.consume_ident_ci("METADATA")? {
194                with_metadata = self.parse_with_metadata_pairs()?;
195            } else if self.consume_ident_ci("AUTO")? {
196                // WITH AUTO EMBED (field1, field2) [USING provider] [MODEL 'model']
197                self.consume_ident_ci("EMBED")?;
198                self.expect(Token::LParen)?;
199                let mut fields = Vec::new();
200                loop {
201                    fields.push(self.expect_ident()?);
202                    if !self.consume(&Token::Comma)? {
203                        break;
204                    }
205                }
206                self.expect(Token::RParen)?;
207                // `USING` is a reserved keyword (`Token::Using`), so
208                // `consume_ident_ci` would never match. Use the typed
209                // consumer instead. See bug #108 (mirrors the #92 fix
210                // for migration `DEPENDS ON`).
211                let provider = if self.consume(&Token::Using)? {
212                    self.expect_ident()?
213                } else {
214                    "openai".to_string()
215                };
216                let model = if self.consume_ident_ci("MODEL")? {
217                    Some(self.parse_string()?)
218                } else {
219                    None
220                };
221                auto_embed = Some(crate::storage::query::ast::AutoEmbedConfig {
222                    fields,
223                    provider,
224                    model,
225                });
226            } else {
227                return Err(ParseError::expected(
228                    vec!["TTL", "EXPIRES AT", "METADATA", "AUTO EMBED"],
229                    self.peek(),
230                    self.position(),
231                ));
232            }
233        }
234
235        Ok((ttl_ms, expires_at_ms, with_metadata, auto_embed))
236    }
237
238    /// Expect a case-insensitive identifier (error if not found)
239    fn expect_ident_ci(&mut self, expected: &str) -> Result<(), ParseError> {
240        if self.consume_ident_ci(expected)? {
241            Ok(())
242        } else {
243            Err(ParseError::expected(
244                vec![expected],
245                self.peek(),
246                self.position(),
247            ))
248        }
249    }
250
251    /// Parse an absolute expiration timestamp (unix ms or string date)
252    fn parse_expires_at_value(&mut self) -> Result<u64, ParseError> {
253        // Try integer (unix timestamp in ms)
254        if let Ok(value) = self.parse_integer() {
255            return Ok(value as u64);
256        }
257        // Try string like '2026-12-31' — convert to unix ms
258        if let Ok(text) = self.parse_string() {
259            // Simple ISO date parsing: YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS
260            let trimmed = text.trim();
261            if let Ok(ts) = trimmed.parse::<u64>() {
262                return Ok(ts);
263            }
264            // Basic date parsing — delegate to chrono if available, or simple heuristic
265            return Err(ParseError::new(
266                // F-05: `trimmed` is caller-controlled string-literal bytes.
267                // Render via `{:?}` so CR/LF/NUL/quotes are escaped before
268                // the message reaches the JSON / audit / log / gRPC sinks.
269                format!("EXPIRES AT requires a unix timestamp in milliseconds, got {trimmed:?}"),
270                self.position(),
271            ));
272        }
273        Err(ParseError::expected(
274            vec!["timestamp (unix ms) or 'YYYY-MM-DD'"],
275            self.peek(),
276            self.position(),
277        ))
278    }
279
280    /// Parse WITH METADATA (key1 = 'value1', key2 = 42)
281    fn parse_with_metadata_pairs(&mut self) -> Result<Vec<(String, Value)>, ParseError> {
282        self.expect(Token::LParen)?;
283        let mut pairs = Vec::new();
284        if !self.check(&Token::RParen) {
285            loop {
286                let key = self.expect_ident_or_keyword()?.to_ascii_lowercase();
287                self.expect(Token::Eq)?;
288                let value = self.parse_literal_value()?;
289                pairs.push((key, value));
290                if !self.consume(&Token::Comma)? {
291                    break;
292                }
293            }
294        }
295        self.expect(Token::RParen)?;
296        Ok(pairs)
297    }
298
299    /// Parse: UPDATE table SET col1=val1, col2=val2 [WHERE filter] [WITH TTL|EXPIRES AT|METADATA]
300    pub fn parse_update_query(&mut self) -> Result<QueryExpr, ParseError> {
301        self.expect(Token::Update)?;
302        let table = self.expect_ident()?;
303        self.expect(Token::Set)?;
304
305        let mut assignments = Vec::new();
306        let mut assignment_exprs = Vec::new();
307        loop {
308            let col = self.expect_ident()?;
309            self.expect(Token::Eq)?;
310            let expr = self.parse_expr()?;
311            let folded = fold_expr_to_value(expr.clone()).ok();
312            assignment_exprs.push((col.clone(), expr));
313            if let Some(val) = folded {
314                assignments.push((col.clone(), val));
315            }
316            if !self.consume(&Token::Comma)? {
317                break;
318            }
319        }
320
321        let filter = if self.consume(&Token::Where)? {
322            Some(self.parse_filter()?)
323        } else {
324            None
325        };
326        let where_expr = filter.as_ref().map(filter_to_expr);
327
328        let (ttl_ms, expires_at_ms, with_metadata, _auto_embed) = self.parse_with_clauses()?;
329
330        // Optional `LIMIT N` — used by `BATCH N ROWS` data migrations
331        // to cap a single batch. Must come after WHERE / WITH because
332        // those have their own keyword tokens that the LIMIT branch
333        // would otherwise mis-consume.
334        let limit = if self.consume(&Token::Limit)? {
335            Some(self.parse_integer()? as u64)
336        } else {
337            None
338        };
339
340        let returning = self.parse_returning_clause()?;
341
342        let suppress_events = if self.consume_ident_ci("SUPPRESS")? {
343            self.expect_ident_ci("EVENTS")?;
344            true
345        } else {
346            false
347        };
348
349        Ok(QueryExpr::Update(UpdateQuery {
350            table,
351            assignment_exprs,
352            assignments,
353            where_expr,
354            filter,
355            ttl_ms,
356            expires_at_ms,
357            with_metadata,
358            returning,
359            limit,
360            suppress_events,
361        }))
362    }
363
364    /// Parse: DELETE FROM table [WHERE filter]
365    pub fn parse_delete_query(&mut self) -> Result<QueryExpr, ParseError> {
366        self.expect(Token::Delete)?;
367        self.expect(Token::From)?;
368        let table = self.expect_ident()?;
369
370        let filter = if self.consume(&Token::Where)? {
371            Some(self.parse_filter()?)
372        } else {
373            None
374        };
375
376        let where_expr = filter.as_ref().map(filter_to_expr);
377
378        let returning = self.parse_returning_clause()?;
379
380        let suppress_events = if self.consume_ident_ci("SUPPRESS")? {
381            self.expect_ident_ci("EVENTS")?;
382            true
383        } else {
384            false
385        };
386
387        Ok(QueryExpr::Delete(DeleteQuery {
388            table,
389            where_expr,
390            filter,
391            returning,
392            suppress_events,
393        }))
394    }
395
396    /// Parse optional `RETURNING (* | col [, col ...])` clause.
397    /// Returns `None` if no RETURNING token, errors if RETURNING is present
398    /// but not followed by `*` or a non-empty column list.
399    fn parse_returning_clause(&mut self) -> Result<Option<Vec<ReturningItem>>, ParseError> {
400        if !self.consume(&Token::Returning)? {
401            return Ok(None);
402        }
403        if self.consume(&Token::Star)? {
404            return Ok(Some(vec![ReturningItem::All]));
405        }
406        let mut items = Vec::new();
407        loop {
408            let col = self.expect_ident_or_keyword()?;
409            items.push(ReturningItem::Column(col));
410            if !self.consume(&Token::Comma)? {
411                break;
412            }
413        }
414        if items.is_empty() {
415            return Err(ParseError::expected(
416                vec!["*", "column name"],
417                self.peek(),
418                self.position(),
419            ));
420        }
421        Ok(Some(items))
422    }
423
424    /// Parse: ASK 'question' [USING provider] [MODEL 'model'] [DEPTH n] [LIMIT n] [COLLECTION col]
425    pub fn parse_ask_query(&mut self) -> Result<QueryExpr, ParseError> {
426        self.advance()?; // consume ASK
427
428        let question = self.parse_string()?;
429
430        let mut provider = None;
431        let mut model = None;
432        let mut depth = None;
433        let mut limit = None;
434        let mut collection = None;
435
436        // Parse optional clauses in any order
437        for _ in 0..5 {
438            if self.consume(&Token::Using)? {
439                provider = Some(self.expect_ident()?);
440            } else if self.consume_ident_ci("MODEL")? {
441                model = Some(self.parse_string()?);
442            } else if self.consume(&Token::Depth)? {
443                depth = Some(self.parse_integer()? as usize);
444            } else if self.consume(&Token::Limit)? {
445                limit = Some(self.parse_integer()? as usize);
446            } else if self.consume(&Token::Collection)? {
447                collection = Some(self.expect_ident()?);
448            } else {
449                break;
450            }
451        }
452
453        Ok(QueryExpr::Ask(AskQuery {
454            question,
455            provider,
456            model,
457            depth,
458            limit,
459            collection,
460        }))
461    }
462
463    /// Parse comma-separated identifiers (accepts keywords as column names in DML context)
464    fn parse_ident_list(&mut self) -> Result<Vec<String>, ParseError> {
465        let mut idents = Vec::new();
466        loop {
467            idents.push(self.expect_ident_or_keyword()?);
468            if !self.consume(&Token::Comma)? {
469                break;
470            }
471        }
472        Ok(idents)
473    }
474
475    /// Parse comma-separated literal values for DML statements
476    fn parse_dml_value_list(&mut self) -> Result<Vec<Value>, ParseError> {
477        self.parse_dml_expr_list()?
478            .into_iter()
479            .map(fold_expr_to_value)
480            .collect::<Result<Vec<_>, _>>()
481            .map_err(|msg| ParseError::new(msg, self.position()))
482    }
483
484    fn parse_dml_expr_list(&mut self) -> Result<Vec<Expr>, ParseError> {
485        let mut values = Vec::new();
486        loop {
487            values.push(self.parse_expr()?);
488            if !self.consume(&Token::Comma)? {
489                break;
490            }
491        }
492        Ok(values)
493    }
494
495    /// Parse a single literal value (string, number, true, false, null, array)
496    pub(crate) fn parse_literal_value(&mut self) -> Result<Value, ParseError> {
497        // Depth guard: this function recurses for nested array `[…]`
498        // and object `{…}` literals (see the LBracket / LBrace arms
499        // below). Without entering the depth counter, an adversarial
500        // payload like `[[[[…(10k×)…]]]]` would overflow the Rust
501        // stack BEFORE `ParserLimits::max_depth` fires. The
502        // `JsonLiteral` token path uses `json_literal_depth_check`
503        // (iterative) — the bare `[`/`{` path needs the recursion
504        // counter explicitly.
505        self.enter_depth()?;
506        let result = self.parse_literal_value_inner();
507        self.exit_depth();
508        result
509    }
510
511    fn parse_literal_value_inner(&mut self) -> Result<Value, ParseError> {
512        // Recognize PASSWORD('plaintext') and SECRET('plaintext') as
513        // typed literal constructors. The parser stores them as
514        // sentinel-prefixed values so that the INSERT executor can
515        // apply the crypto transform (argon2id hash / AES-256-GCM
516        // encrypt) without the parser depending on auth or crypto
517        // subsystems.
518        if let Token::Ident(name) = self.peek().clone() {
519            let upper = name.to_uppercase();
520            if upper == "PASSWORD" || upper == "SECRET" {
521                self.advance()?; // consume ident
522                self.expect(Token::LParen)?;
523                let plaintext = self.parse_string()?;
524                self.expect(Token::RParen)?;
525                return Ok(match upper.as_str() {
526                    "PASSWORD" => Value::Password(format!("@@plain@@{plaintext}")),
527                    "SECRET" => Value::Secret(format!("@@plain@@{plaintext}").into_bytes()),
528                    _ => unreachable!(),
529                });
530            }
531            if upper == "SECRET_REF" {
532                self.advance()?; // consume ident
533                self.expect(Token::LParen)?;
534                let store = self.expect_ident_or_keyword()?.to_ascii_lowercase();
535                if store != "vault" {
536                    return Err(ParseError::expected(
537                        vec!["vault"],
538                        self.peek(),
539                        self.position(),
540                    ));
541                }
542                self.expect(Token::Comma)?;
543                let (collection, key) =
544                    self.parse_kv_key(crate::catalog::CollectionModel::Vault)?;
545                self.expect(Token::RParen)?;
546                return Ok(secret_ref_value(&store, &collection, &key));
547            }
548        }
549
550        match self.peek().clone() {
551            Token::String(s) => {
552                let s = s.clone();
553                self.advance()?;
554                Ok(Value::text(s))
555            }
556            Token::JsonLiteral(raw) => {
557                // The lexer already validated brace balance and the
558                // 16 MiB payload ceiling. Parse the raw text into a
559                // canonical JsonValue then re-encode via `to_vec` so
560                // the on-disk bytes match the quoted form.
561                self.advance()?;
562                let json_value = crate::utils::json::parse_json(&raw).map_err(|err| {
563                    ParseError::new(
564                        // F-05: render the underlying parse-error string
565                        // via `{:?}` so any user fragment serde echoed
566                        // back (unexpected character, key text, …) is
567                        // Debug-escaped before reaching the downstream
568                        // JSON / audit / log / gRPC sinks.
569                        format!("invalid JSON object literal: {:?}", err.to_string()),
570                        self.position(),
571                    )
572                })?;
573                json_literal_depth_check(&json_value)
574                    .map_err(|err| ParseError::new(err, self.position()))?;
575                let canonical = crate::serde_json::Value::from(json_value);
576                let bytes = crate::json::to_vec(&canonical).map_err(|err| {
577                    ParseError::new(
578                        // F-05: escape the encoder error via `{:?}` so any
579                        // user fragment it carries cannot smuggle control
580                        // bytes through downstream serialization sinks.
581                        format!("failed to encode JSON literal: {:?}", err.to_string()),
582                        self.position(),
583                    )
584                })?;
585                Ok(Value::Json(bytes))
586            }
587            Token::Integer(n) => {
588                self.advance()?;
589                Ok(Value::Integer(n))
590            }
591            Token::Float(n) => {
592                self.advance()?;
593                Ok(Value::Float(n))
594            }
595            Token::True => {
596                self.advance()?;
597                Ok(Value::Boolean(true))
598            }
599            Token::False => {
600                self.advance()?;
601                Ok(Value::Boolean(false))
602            }
603            Token::Null => {
604                self.advance()?;
605                Ok(Value::Null)
606            }
607            Token::LBracket => {
608                // Parse array literal [val1, val2, ...]
609                // For numeric arrays, produce Value::Vector; for others, produce Value::Json
610                self.advance()?; // consume '['
611                let mut items = Vec::new();
612                if !self.check(&Token::RBracket) {
613                    loop {
614                        items.push(self.parse_literal_value()?);
615                        if !self.consume(&Token::Comma)? {
616                            break;
617                        }
618                    }
619                }
620                self.expect(Token::RBracket)?;
621
622                // Check if all items are numeric (Integer or Float) -> Value::Vector
623                let all_numeric = items
624                    .iter()
625                    .all(|v| matches!(v, Value::Integer(_) | Value::Float(_)));
626                if all_numeric && !items.is_empty() {
627                    let floats: Vec<f32> = items
628                        .iter()
629                        .map(|v| match v {
630                            Value::Float(f) => *f as f32,
631                            Value::Integer(i) => *i as f32,
632                            _ => 0.0,
633                        })
634                        .collect();
635                    Ok(Value::Vector(floats))
636                } else {
637                    // Encode as JSON bytes
638                    let json_arr: Vec<crate::json::Value> = items
639                        .iter()
640                        .map(|v| match v {
641                            Value::Null => crate::json::Value::Null,
642                            Value::Boolean(b) => crate::json::Value::Bool(*b),
643                            Value::Integer(i) => crate::json::Value::Number(*i as f64),
644                            Value::Float(f) => crate::json::Value::Number(*f),
645                            Value::Text(s) => crate::json::Value::String(s.to_string()),
646                            _ => crate::json::Value::Null,
647                        })
648                        .collect();
649                    let json_val = crate::json::Value::Array(json_arr);
650                    let bytes = crate::json::to_vec(&json_val).unwrap_or_default();
651                    Ok(Value::Json(bytes))
652                }
653            }
654            Token::LBrace => {
655                // Parse JSON object literal {key: value, ...}
656                self.advance()?; // consume '{'
657                let mut map = crate::json::Map::new();
658                if !self.check(&Token::RBrace) {
659                    loop {
660                        // Key: string or identifier. Reserved-word
661                        // keys (`level`, `msg`, `type`, …) fall through
662                        // to `expect_ident_or_keyword`, which returns
663                        // the canonical UPPERCASE spelling; lowercase
664                        // that path so the JSON object preserves the
665                        // source casing.
666                        let key = match self.peek().clone() {
667                            Token::String(s) => {
668                                self.advance()?;
669                                s
670                            }
671                            Token::Ident(s) => {
672                                self.advance()?;
673                                s
674                            }
675                            _ => self.expect_ident_or_keyword()?.to_ascii_lowercase(),
676                        };
677                        // Separator: ':' or '='
678                        if !self.consume(&Token::Colon)? {
679                            self.expect(Token::Eq)?;
680                        }
681                        // Value: recursive
682                        let val = self.parse_literal_value()?;
683                        let json_val = match val {
684                            Value::Null => crate::json::Value::Null,
685                            Value::Boolean(b) => crate::json::Value::Bool(b),
686                            Value::Integer(i) => crate::json::Value::Number(i as f64),
687                            Value::Float(f) => crate::json::Value::Number(f),
688                            Value::Text(s) => crate::json::Value::String(s.to_string()),
689                            Value::Json(ref bytes) => {
690                                crate::json::from_slice(bytes).unwrap_or(crate::json::Value::Null)
691                            }
692                            _ => crate::json::Value::Null,
693                        };
694                        map.insert(key, json_val);
695                        if !self.consume(&Token::Comma)? {
696                            break;
697                        }
698                    }
699                }
700                self.expect(Token::RBrace)?;
701                let json_val = crate::json::Value::Object(map);
702                let bytes = crate::json::to_vec(&json_val).unwrap_or_default();
703                Ok(Value::Json(bytes))
704            }
705            ref other => Err(ParseError::expected(
706                vec!["string", "number", "true", "false", "null", "[", "{"],
707                other,
708                self.position(),
709            )),
710        }
711    }
712}
713
714fn secret_ref_value(store: &str, collection: &str, key: &str) -> Value {
715    let mut map = crate::json::Map::new();
716    map.insert(
717        "type".to_string(),
718        crate::json::Value::String("secret_ref".to_string()),
719    );
720    map.insert(
721        "store".to_string(),
722        crate::json::Value::String(store.to_string()),
723    );
724    map.insert(
725        "collection".to_string(),
726        crate::json::Value::String(collection.to_string()),
727    );
728    map.insert(
729        "key".to_string(),
730        crate::json::Value::String(key.to_string()),
731    );
732    Value::Json(crate::json::to_vec(&crate::json::Value::Object(map)).unwrap_or_default())
733}