qail_core/ast/builders/
json.rs

1//! JSON access builders for PostgreSQL JSONB operations.
2
3use crate::ast::Expr;
4
5/// JSON text access (column->>'key')
6/// # Example
7/// ```ignore
8/// json("contact_info", "phone")  // contact_info->>'phone'
9/// ```
10pub fn json(column: &str, key: &str) -> JsonBuilder {
11    JsonBuilder {
12        column: column.to_string(),
13        path_segments: vec![(key.to_string(), true)], // true = as text (->>)
14        alias: None,
15    }
16}
17
18/// JSON path access with multiple keys (column->'a'->'b'->>'c')
19/// The last key extracts as text.
20/// # Example
21/// ```ignore
22/// json_path("metadata", ["vessel_bookings", "0", "departure"])
23/// // metadata->'vessel_bookings'->0->>'departure'
24/// ```
25pub fn json_path<S: AsRef<str>>(column: &str, keys: impl IntoIterator<Item = S>) -> JsonBuilder {
26    let keys_vec: Vec<_> = keys.into_iter().collect();
27    let len = keys_vec.len();
28    let path_segments: Vec<(String, bool)> = keys_vec
29        .into_iter()
30        .enumerate()
31        .map(|(i, k)| (k.as_ref().to_string(), i == len - 1)) // Last key as text
32        .collect();
33
34    JsonBuilder {
35        column: column.to_string(),
36        path_segments,
37        alias: None,
38    }
39}
40
41/// JSON object access (column->'key') - keeps as JSON, not text
42pub fn json_obj(column: &str, key: &str) -> JsonBuilder {
43    JsonBuilder {
44        column: column.to_string(),
45        path_segments: vec![(key.to_string(), false)], // false = as JSON (->)
46        alias: None,
47    }
48}
49
50/// Builder for JSON access expressions
51#[derive(Debug, Clone)]
52pub struct JsonBuilder {
53    pub(crate) column: String,
54    pub(crate) path_segments: Vec<(String, bool)>,
55    pub(crate) alias: Option<String>,
56}
57
58impl JsonBuilder {
59    /// Access another nested key as JSON (->)
60    pub fn get(mut self, key: &str) -> Self {
61        self.path_segments.push((key.to_string(), false));
62        self
63    }
64
65    /// Access another nested key as text (->>)
66    pub fn get_text(mut self, key: &str) -> Self {
67        self.path_segments.push((key.to_string(), true));
68        self
69    }
70
71    /// Add alias (AS name)
72    pub fn alias(mut self, name: &str) -> Expr {
73        self.alias = Some(name.to_string());
74        self.build()
75    }
76
77    /// Build the final Expr
78    pub fn build(self) -> Expr {
79        Expr::JsonAccess {
80            column: self.column,
81            path_segments: self.path_segments,
82            alias: self.alias,
83        }
84    }
85}
86
87impl From<JsonBuilder> for Expr {
88    fn from(builder: JsonBuilder) -> Self {
89        builder.build()
90    }
91}