Skip to main content

rust_ef/query/builder/
core.rs

1//! `QueryBuilder<T>` struct and core constructors.
2
3use std::collections::HashMap;
4use std::marker::PhantomData;
5use std::sync::Arc;
6
7use crate::entity::IEntityType;
8use crate::provider::IDatabaseProvider;
9
10use super::super::ast::{BoolExpr, CompiledFilter};
11use super::super::compile::collect_bool_expr_values;
12use super::super::state::QueryState;
13
14/// A chainable query builder for entity type `T`.
15///
16/// Corresponds to EFCore's `IQueryable<T>`.
17///
18/// `Clone` is derived so that builders can be forked for compositional reuse
19/// (e.g. applying additional filters on a base query without losing the
20/// original). Note that `single`/`single_or_default` still use the `take(2)`
21/// approach rather than `clone().count()` to avoid a double round-trip.
22#[derive(Clone)]
23pub struct QueryBuilder<T: IEntityType> {
24    pub(super) state: QueryState,
25    pub(super) provider: Option<Arc<dyn IDatabaseProvider>>,
26    pub(super) filter_map: Option<Arc<HashMap<String, CompiledFilter>>>,
27    pub(super) lazy_loading_enabled: bool,
28    pub(super) _phantom: PhantomData<T>,
29}
30
31impl<T: IEntityType> QueryBuilder<T> {
32    /// Creates a new QueryBuilder for a given table (without provider — SQL-only).
33    pub fn new(table_name: impl Into<String>) -> Self {
34        Self {
35            state: QueryState::new(table_name),
36            provider: None,
37            filter_map: None,
38            lazy_loading_enabled: false,
39            _phantom: PhantomData,
40        }
41    }
42
43    /// Creates a new QueryBuilder for a given table with a provider for execution.
44    pub fn with_provider(
45        table_name: impl Into<String>,
46        provider: Arc<dyn IDatabaseProvider>,
47    ) -> Self {
48        Self {
49            state: QueryState::new(table_name),
50            provider: Some(provider),
51            filter_map: None,
52            lazy_loading_enabled: false,
53            _phantom: PhantomData,
54        }
55    }
56
57    /// Attaches a global filter map (table_name → BoolExpr) for NavigationLoader.
58    pub(crate) fn with_filter_map(
59        mut self,
60        map: Option<Arc<HashMap<String, CompiledFilter>>>,
61    ) -> Self {
62        self.filter_map = map;
63        self
64    }
65
66    /// Sets whether lazy loading is enabled for materialized entities.
67    pub(crate) fn with_lazy_loading(mut self, enabled: bool) -> Self {
68        self.lazy_loading_enabled = enabled;
69        self
70    }
71
72    /// Returns a reference to the accumulated query state.
73    pub fn state(&self) -> &QueryState {
74        &self.state
75    }
76
77    /// Applies a compile-time LINQ expression tree from `linq!(?)`.
78    pub fn filter(self, f: impl FnOnce(Self) -> Self) -> Self {
79        f(self)
80    }
81
82    /// Applies a global query filter `BoolExpr` (produced by `linq!(filter |b: T| ...)`).
83    /// Inline values carried by the expression are collected and appended to
84    /// the query parameters in the correct position.
85    pub(crate) fn apply_query_filter(mut self, filter: BoolExpr) -> Self {
86        let values = collect_bool_expr_values(&filter);
87        self.state.parameters.extend(values);
88        self.state.append_bool_expr(filter);
89        self
90    }
91}