molframe_query/execution/
eval_query.rs1use super::{Evaluation, Groups, Query};
4use crate::builder::Builder;
5use crate::execution::QueryFingerprint;
6use crate::plan::{LogicalPlan, PhysicalQuery};
7use crate::spatial::SpatialResolver;
8use molframe_core::contract::AnalysisPolicy;
9use molframe_core::diagnostic::Diagnostic;
10use molframe_core::selection::AtomSelection;
11use molframe_core::structure::Structure;
12use std::ops::{BitAnd, BitOr, Not};
13
14impl Query {
15 pub fn compile(source: &str) -> Result<Self, Vec<Diagnostic>> {
21 let parsed = crate::parser::parse(source)?;
22 Ok(Self {
23 expr: parsed.expr,
24 warnings: parsed.warnings,
25 source: source.into(),
26 })
27 }
28
29 #[must_use]
31 pub fn from_builder(builder: Builder) -> Self {
32 let (expr, source) = builder.into_parts();
33 Self {
34 expr,
35 warnings: Vec::new(),
36 source,
37 }
38 }
39
40 #[must_use]
42 pub fn source(&self) -> &str {
43 &self.source
44 }
45
46 #[must_use]
48 pub fn fingerprint(&self) -> QueryFingerprint {
49 QueryFingerprint::of(&self.expr)
50 }
51
52 #[must_use]
54 pub fn within(radius: f32, target: Self) -> Self {
55 let source = format!("within {radius} of ({})", target.source);
56 Self {
57 expr: crate::ast::Expr::Geometric(crate::ast::GeometricExpr::Within {
58 radius,
59 target: Box::new(target.expr),
60 }),
61 warnings: target.warnings,
62 source: source.into(),
63 }
64 }
65
66 #[must_use]
68 pub fn by_residue(target: Self) -> Self {
69 let source = format!("byres ({})", target.source);
70 Self {
71 expr: crate::ast::Expr::ByResidue(Box::new(target.expr)),
72 warnings: target.warnings,
73 source: source.into(),
74 }
75 }
76
77 #[must_use]
79 pub fn residues_within(radius: f32, target: Self) -> Self {
80 Self::by_residue(Self::within(radius, target))
81 }
82
83 #[must_use]
85 pub fn logical_plan(&self) -> LogicalPlan {
86 LogicalPlan(self.expr.clone())
87 }
88
89 #[must_use]
91 pub fn plan(&self, structure: &Structure, policy: &AnalysisPolicy) -> PhysicalQuery {
92 crate::plan::bind_expr(&self.expr, structure, policy, self.warnings.clone())
93 }
94
95 pub fn evaluate(
101 &self,
102 structure: &Structure,
103 policy: &AnalysisPolicy,
104 groups: &Groups,
105 spatial: Option<&dyn SpatialResolver>,
106 ) -> Result<Evaluation, Vec<Diagnostic>> {
107 self.evaluate_in(
108 structure,
109 &AtomSelection::All(structure.atom_count()),
110 policy,
111 groups,
112 spatial,
113 )
114 }
115
116 pub fn evaluate_in(
122 &self,
123 structure: &Structure,
124 universe: &AtomSelection,
125 policy: &AnalysisPolicy,
126 groups: &Groups,
127 spatial: Option<&dyn SpatialResolver>,
128 ) -> Result<Evaluation, Vec<Diagnostic>> {
129 self.plan(structure, policy)
130 .evaluate_in(structure, universe, policy, groups, spatial)
131 }
132}
133
134impl From<Builder> for Query {
135 fn from(builder: Builder) -> Self {
136 Self::from_builder(builder)
137 }
138}
139
140impl BitAnd for Query {
141 type Output = Self;
142
143 fn bitand(self, right: Self) -> Self::Output {
144 let source = format!("({}) and ({})", self.source, right.source);
145 Self {
146 expr: crate::ast::Expr::And(Box::new(self.expr), Box::new(right.expr)),
147 warnings: merged_warnings(self.warnings, right.warnings),
148 source: source.into(),
149 }
150 }
151}
152
153impl BitOr for Query {
154 type Output = Self;
155
156 fn bitor(self, right: Self) -> Self::Output {
157 let source = format!("({}) or ({})", self.source, right.source);
158 Self {
159 expr: crate::ast::Expr::Or(Box::new(self.expr), Box::new(right.expr)),
160 warnings: merged_warnings(self.warnings, right.warnings),
161 source: source.into(),
162 }
163 }
164}
165
166impl Not for Query {
167 type Output = Self;
168
169 fn not(self) -> Self::Output {
170 let source = format!("not ({})", self.source);
171 Self {
172 expr: crate::ast::Expr::Not(Box::new(self.expr)),
173 warnings: self.warnings,
174 source: source.into(),
175 }
176 }
177}
178
179fn merged_warnings(mut left: Vec<Diagnostic>, right: Vec<Diagnostic>) -> Vec<Diagnostic> {
180 left.extend(right);
181 left.sort_by_key(Diagnostic::code);
182 left
183}