quick_oxibooks_sql/
query.rs

1use quickbooks_types::QBItem;
2
3use crate::{Limit, Order, OrderClause, WhereClause};
4
5/// Struct representing a SQL-like query for `QuickBooks` entities
6#[derive(Debug, PartialEq, Clone)]
7pub struct Query<QB> {
8    pub(crate) condition: Vec<WhereClause>,
9    pub(crate) order: Vec<OrderClause>,
10    pub(crate) limit: Option<Limit>,
11    _phantom: std::marker::PhantomData<QB>,
12}
13
14impl<QB: QBItem> Default for Query<QB> {
15    fn default() -> Self {
16        Self::new()
17    }
18}
19
20impl<QB: QBItem> Query<QB> {
21    /// Create a new empty query
22    #[must_use]
23    pub fn new() -> Self {
24        Query {
25            condition: Vec::new(),
26            order: Vec::new(),
27            limit: None,
28            _phantom: std::marker::PhantomData,
29        }
30    }
31
32    /// Add a condition to the query
33    ///
34    /// # Safety
35    /// This function is unsafe because it accepts a raw `WhereClause`.
36    /// The caller must ensure that the `WhereClause` is valid and corresponds to the `QuickBooks` entity.
37    #[must_use]
38    pub unsafe fn condition(mut self, condition: WhereClause) -> Self {
39        self.condition.push(condition);
40        self
41    }
42
43    /// Add a typed condition to the query
44    ///
45    /// This is safe because the typed where clause ensures that the field and values are valid for the `QuickBooks` entity.
46    #[must_use]
47    pub fn typed_condition(mut self, condition: crate::TypedWhereClause<QB>) -> Self {
48        self.condition.push(condition.into());
49        self
50    }
51
52    /// Add an order clause to the query
53    ///
54    /// # Safety
55    /// This function is unsafe because it accepts a raw string slice as the field name.
56    /// The caller must ensure that the field name is valid and corresponds to a field in the `QuickBooks` entity.
57    #[must_use]
58    pub unsafe fn order(mut self, field: &'static str, order: Order) -> Self {
59        self.order.push(OrderClause { field, order });
60        self
61    }
62
63    /// Set a limit on the number of results returned by the query
64    #[must_use]
65    pub fn limit(mut self, number: u32, offset: Option<u32>) -> Self {
66        self.limit = Some(Limit { number, offset });
67        self
68    }
69
70    /// Generate the query string
71    #[must_use]
72    pub fn query_string(&self) -> String {
73        let mut query = format!("select * from {}", QB::name());
74
75        if !self.condition.is_empty() {
76            query.push_str(" where");
77            for (i, cond) in self.condition.iter().enumerate() {
78                if i > 0 {
79                    query.push_str(" and");
80                }
81                cond.extend_query(&mut query);
82            }
83        }
84
85        if !self.order.is_empty() {
86            query.push_str(" order by");
87            for (i, ord) in self.order.iter().enumerate() {
88                if i > 0 {
89                    query.push(',');
90                }
91                ord.extend_query(&mut query);
92            }
93        }
94
95        if let Some(limit) = &self.limit {
96            limit.extend_query(&mut query);
97        }
98
99        query
100    }
101
102    #[cfg(feature = "api")]
103    /// Execute the query against the `QuickBooks` API, returning a vector of results or an error
104    ///
105    /// # Errors
106    /// This function will return an error if the API request fails or if the response cannot be parsed.
107    pub fn execute(
108        &self,
109        qb: &quick_oxibooks::QBContext,
110        client: &ureq::Agent,
111    ) -> Result<Vec<QB>, quick_oxibooks::error::APIError> {
112        // Safety: The query has been constructed using the provided methods,
113        // ensuring that it is valid for the QuickBooks entity QB.
114        unsafe { quick_oxibooks::functions::query::qb_query_raw::<QB>(self, qb, client) }
115    }
116}
117
118impl<QB: QBItem> std::fmt::Display for Query<QB> {
119    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120        write!(f, "{}", self.query_string())
121    }
122}