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