pub struct Query { /* private fields */ }Expand description
One fixed-stage query. Empty text selects and emits the original document.
Implementations§
Source§impl Query
impl Query
Sourcepub fn parse(expression: &str, limits: Limits) -> Result<Self, QueryError>
pub fn parse(expression: &str, limits: Limits) -> Result<Self, QueryError>
Examples found in repository?
examples/grow-wedding.rs (line 82)
63fn grow(bytes: Vec<u8>, factor: usize) -> Result<Vec<u8>> {
64 if !(1..=MAX_FACTOR).contains(&factor) {
65 return Err(format!("FACTOR must be in 1..={MAX_FACTOR}"));
66 }
67 if bytes.len() > MAX_INPUT {
68 return Err("input exceeds 1 MiB".into());
69 }
70 let input = Input::parse_named(bytes, FileMode::Data, "wedding.dzn")
71 .map_err(|error| format!("invalid data: {error:?}"))?;
72 if input
73 .syntax()
74 .tokens()
75 .len()
76 .checked_mul(factor)
77 .is_none_or(|n| n > MAX_TOKENS)
78 {
79 return Err("grown input exceeds the 200,000-token allowance".into());
80 }
81 // This example understands these eight assignments, not arbitrary dependencies.
82 let QueryResult::Selection(items) = Query::parse("items", Limits::default())
83 .map_err(|error| error.message)?
84 .evaluate(&input, Limits::default())
85 .map_err(|error| error.message)?
86 else {
87 unreachable!()
88 };
89 let mut names = BTreeSet::new();
90 for item in items.items() {
91 if item.kind != ItemKind::Assignment
92 || !item
93 .name
94 .is_some_and(|name| ASSIGNMENTS.contains(&name) && names.insert(name))
95 {
96 return Err("expected only the eight distinct wedding data assignments".into());
97 }
98 }
99 if names.len() != ASSIGNMENTS.len() {
100 return Err("all eight wedding data assignments are required".into());
101 }
102 // Refuse formatter directives: copying their text could change directive scopes.
103 for (index, token) in input.syntax().tokens().iter().enumerate() {
104 if matches!(token.kind, TokenKind::LineComment | TokenKind::BlockComment)
105 && input
106 .token_bytes(index)
107 .windows(b"zincite-fmt:".len())
108 .any(|w| w == b"zincite-fmt:")
109 {
110 return Err("comments containing zincite directives are unsupported".into());
111 }
112 }
113 let topics = literal(&input, "Topics")?;
114 let guests = literal(&input, "Guests")?;
115 let records = literal(&input, "guests")?;
116 let same = literal(&input, "same_table")?;
117 let different = literal(&input, "different_tables")?;
118 let capacities = literal(&input, "table_capacities")?;
119 let imbalance = literal(&input, "max_gender_imbalance_per_table")?;
120 let topic_members = members(&topics)?;
121 let guest_members = members(&guests)?;
122 if topic_members.is_empty() || guest_members.is_empty() {
123 return Err("Topics and Guests must be nonempty literal member sets".into());
124 }
125 let topic_names: BTreeSet<_> = topic_members.iter().map(|(name, _)| *name).collect();
126 let guest_names: BTreeSet<_> = guest_members.iter().map(|(name, _)| *name).collect();
127 if !topic_names.is_disjoint(&guest_names)
128 || topic_names
129 .iter()
130 .chain(guest_names.iter())
131 .any(|name| ASSIGNMENTS.contains(name) || ["Man", "Woman", "NA", "T"].contains(name))
132 {
133 return Err("enum members collide with the wedding model's identifiers".into());
134 }
135 validate_records(&records, &guest_names, topic_members.len())?;
136 let same_members = group_members(&same, &guest_names)?;
137 let different_members = group_members(&different, &guest_names)?;
138 let table_item = items
139 .items()
140 .iter()
141 .find(|item| item.name == Some("Tables"))
142 .unwrap();
143 let (table_count, table_range) = table_constructor(&input, &table_item.range)?;
144 let capacity_values = positional(&capacities)?;
145 if capacity_values.len() != table_count
146 || capacity_values
147 .iter()
148 .any(|value| !matches!(value.kind, LiteralKind::Integer))
149 || !matches!(imbalance.kind, LiteralKind::Integer)
150 {
151 return Err("table_capacities must contain one literal integer per table; imbalance must be a literal integer".into());
152 }
153 let grown_tables = table_count
154 .checked_mul(factor)
155 .filter(|count| i64::try_from(*count).is_ok())
156 .ok_or("table count overflow")?;
157 if factor == 1 {
158 return Ok(input.source_bytes().to_vec());
159 }
160 let mut occupied: BTreeSet<String> = input
161 .syntax()
162 .tokens()
163 .iter()
164 .enumerate()
165 .filter(|(_, token)| {
166 matches!(
167 token.kind,
168 TokenKind::Identifier | TokenKind::QuotedIdentifier
169 )
170 })
171 .map(|(index, _)| {
172 String::from_utf8_lossy(input.token_bytes(index))
173 .trim_matches('\'')
174 .to_owned()
175 })
176 .collect();
177 let mut mappings = Vec::new();
178 for copy in 1..factor {
179 let mut mapping = BTreeMap::new();
180 for (index, (name, _)) in guest_members.iter().enumerate() {
181 let generated = format!("GrownGuest_{copy}_{}", index + 1);
182 if !occupied.insert(generated.clone()) {
183 return Err(format!("generated guest identifier collides: {generated}"));
184 }
185 mapping.insert(*name, generated);
186 }
187 mappings.push(mapping);
188 }
189 let LiteralKind::Array { entries, .. } = &records.kind else {
190 unreachable!()
191 };
192 let record_keys: Vec<_> = entries
193 .iter()
194 .map(|entry| {
195 let key = entry.key.as_ref().unwrap();
196 let LiteralKind::Member(name) = key.kind else {
197 unreachable!()
198 };
199 (name, key.range.clone())
200 })
201 .collect();
202 let mut edits = Vec::new();
203 for (value, replacements) in [
204 (&guests, guest_members),
205 (&records, record_keys),
206 (&same, same_members),
207 (&different, different_members),
208 (&capacities, Vec::new()),
209 ] {
210 edits.push(copy_collection(&input, value, &replacements, &mappings)?);
211 }
212 edits.push(TextEdit {
213 range: table_range,
214 replacement: vec![EditPart::Text(grown_tables.to_string())],
215 });
216 // Charge the exact final byte count before the public edit API allocates it.
217 let mut output_len = input.source_bytes().len();
218 for edit in &edits {
219 output_len -= edit.range.len();
220 for part in &edit.replacement {
221 let len = match part {
222 EditPart::Text(text) => text.len(),
223 EditPart::Original(range) => range.len(),
224 };
225 output_len = output_len
226 .checked_add(len)
227 .filter(|n| *n <= MAX_OUTPUT)
228 .ok_or("candidate exceeds 16 MiB")?;
229 }
230 }
231 let snapshot = SourceSnapshot::new("wedding.dzn", input.source_bytes());
232 let candidate = prepare_text_edits(&snapshot, input.source_bytes(), &edits)
233 .map_err(|error| error.to_string())?;
234 Input::parse_named(candidate.clone(), FileMode::Data, "grown-wedding.dzn")
235 .map_err(|error| format!("invalid candidate: {error:?}"))?;
236 Ok(candidate)
237}
238
239fn literal<'a>(input: &'a Input, name: &str) -> Result<LiteralValue<'a>> {
240 let query = Query::parse(
241 &format!("filter(name(\"{name}\")) | values"),
242 Limits::default(),
243 )
244 .map_err(|error| error.message)?;
245 let QueryResult::Literals(values) = query
246 .evaluate(input, Limits::default())
247 .map_err(|error| error.message)?
248 else {
249 unreachable!()
250 };
251 match values.values() {
252 [value] => Ok(value.clone()),
253 _ => Err(format!("expected one {name} assignment")),
254 }
255}pub fn is_editing(&self) -> bool
pub fn requires_model_facts(&self) -> bool
Sourcepub fn evaluate<'a>(
&self,
input: &'a Input,
limits: Limits,
) -> Result<QueryResult<'a>, QueryError>
pub fn evaluate<'a>( &self, input: &'a Input, limits: Limits, ) -> Result<QueryResult<'a>, QueryError>
Examples found in repository?
examples/grow-wedding.rs (line 84)
63fn grow(bytes: Vec<u8>, factor: usize) -> Result<Vec<u8>> {
64 if !(1..=MAX_FACTOR).contains(&factor) {
65 return Err(format!("FACTOR must be in 1..={MAX_FACTOR}"));
66 }
67 if bytes.len() > MAX_INPUT {
68 return Err("input exceeds 1 MiB".into());
69 }
70 let input = Input::parse_named(bytes, FileMode::Data, "wedding.dzn")
71 .map_err(|error| format!("invalid data: {error:?}"))?;
72 if input
73 .syntax()
74 .tokens()
75 .len()
76 .checked_mul(factor)
77 .is_none_or(|n| n > MAX_TOKENS)
78 {
79 return Err("grown input exceeds the 200,000-token allowance".into());
80 }
81 // This example understands these eight assignments, not arbitrary dependencies.
82 let QueryResult::Selection(items) = Query::parse("items", Limits::default())
83 .map_err(|error| error.message)?
84 .evaluate(&input, Limits::default())
85 .map_err(|error| error.message)?
86 else {
87 unreachable!()
88 };
89 let mut names = BTreeSet::new();
90 for item in items.items() {
91 if item.kind != ItemKind::Assignment
92 || !item
93 .name
94 .is_some_and(|name| ASSIGNMENTS.contains(&name) && names.insert(name))
95 {
96 return Err("expected only the eight distinct wedding data assignments".into());
97 }
98 }
99 if names.len() != ASSIGNMENTS.len() {
100 return Err("all eight wedding data assignments are required".into());
101 }
102 // Refuse formatter directives: copying their text could change directive scopes.
103 for (index, token) in input.syntax().tokens().iter().enumerate() {
104 if matches!(token.kind, TokenKind::LineComment | TokenKind::BlockComment)
105 && input
106 .token_bytes(index)
107 .windows(b"zincite-fmt:".len())
108 .any(|w| w == b"zincite-fmt:")
109 {
110 return Err("comments containing zincite directives are unsupported".into());
111 }
112 }
113 let topics = literal(&input, "Topics")?;
114 let guests = literal(&input, "Guests")?;
115 let records = literal(&input, "guests")?;
116 let same = literal(&input, "same_table")?;
117 let different = literal(&input, "different_tables")?;
118 let capacities = literal(&input, "table_capacities")?;
119 let imbalance = literal(&input, "max_gender_imbalance_per_table")?;
120 let topic_members = members(&topics)?;
121 let guest_members = members(&guests)?;
122 if topic_members.is_empty() || guest_members.is_empty() {
123 return Err("Topics and Guests must be nonempty literal member sets".into());
124 }
125 let topic_names: BTreeSet<_> = topic_members.iter().map(|(name, _)| *name).collect();
126 let guest_names: BTreeSet<_> = guest_members.iter().map(|(name, _)| *name).collect();
127 if !topic_names.is_disjoint(&guest_names)
128 || topic_names
129 .iter()
130 .chain(guest_names.iter())
131 .any(|name| ASSIGNMENTS.contains(name) || ["Man", "Woman", "NA", "T"].contains(name))
132 {
133 return Err("enum members collide with the wedding model's identifiers".into());
134 }
135 validate_records(&records, &guest_names, topic_members.len())?;
136 let same_members = group_members(&same, &guest_names)?;
137 let different_members = group_members(&different, &guest_names)?;
138 let table_item = items
139 .items()
140 .iter()
141 .find(|item| item.name == Some("Tables"))
142 .unwrap();
143 let (table_count, table_range) = table_constructor(&input, &table_item.range)?;
144 let capacity_values = positional(&capacities)?;
145 if capacity_values.len() != table_count
146 || capacity_values
147 .iter()
148 .any(|value| !matches!(value.kind, LiteralKind::Integer))
149 || !matches!(imbalance.kind, LiteralKind::Integer)
150 {
151 return Err("table_capacities must contain one literal integer per table; imbalance must be a literal integer".into());
152 }
153 let grown_tables = table_count
154 .checked_mul(factor)
155 .filter(|count| i64::try_from(*count).is_ok())
156 .ok_or("table count overflow")?;
157 if factor == 1 {
158 return Ok(input.source_bytes().to_vec());
159 }
160 let mut occupied: BTreeSet<String> = input
161 .syntax()
162 .tokens()
163 .iter()
164 .enumerate()
165 .filter(|(_, token)| {
166 matches!(
167 token.kind,
168 TokenKind::Identifier | TokenKind::QuotedIdentifier
169 )
170 })
171 .map(|(index, _)| {
172 String::from_utf8_lossy(input.token_bytes(index))
173 .trim_matches('\'')
174 .to_owned()
175 })
176 .collect();
177 let mut mappings = Vec::new();
178 for copy in 1..factor {
179 let mut mapping = BTreeMap::new();
180 for (index, (name, _)) in guest_members.iter().enumerate() {
181 let generated = format!("GrownGuest_{copy}_{}", index + 1);
182 if !occupied.insert(generated.clone()) {
183 return Err(format!("generated guest identifier collides: {generated}"));
184 }
185 mapping.insert(*name, generated);
186 }
187 mappings.push(mapping);
188 }
189 let LiteralKind::Array { entries, .. } = &records.kind else {
190 unreachable!()
191 };
192 let record_keys: Vec<_> = entries
193 .iter()
194 .map(|entry| {
195 let key = entry.key.as_ref().unwrap();
196 let LiteralKind::Member(name) = key.kind else {
197 unreachable!()
198 };
199 (name, key.range.clone())
200 })
201 .collect();
202 let mut edits = Vec::new();
203 for (value, replacements) in [
204 (&guests, guest_members),
205 (&records, record_keys),
206 (&same, same_members),
207 (&different, different_members),
208 (&capacities, Vec::new()),
209 ] {
210 edits.push(copy_collection(&input, value, &replacements, &mappings)?);
211 }
212 edits.push(TextEdit {
213 range: table_range,
214 replacement: vec![EditPart::Text(grown_tables.to_string())],
215 });
216 // Charge the exact final byte count before the public edit API allocates it.
217 let mut output_len = input.source_bytes().len();
218 for edit in &edits {
219 output_len -= edit.range.len();
220 for part in &edit.replacement {
221 let len = match part {
222 EditPart::Text(text) => text.len(),
223 EditPart::Original(range) => range.len(),
224 };
225 output_len = output_len
226 .checked_add(len)
227 .filter(|n| *n <= MAX_OUTPUT)
228 .ok_or("candidate exceeds 16 MiB")?;
229 }
230 }
231 let snapshot = SourceSnapshot::new("wedding.dzn", input.source_bytes());
232 let candidate = prepare_text_edits(&snapshot, input.source_bytes(), &edits)
233 .map_err(|error| error.to_string())?;
234 Input::parse_named(candidate.clone(), FileMode::Data, "grown-wedding.dzn")
235 .map_err(|error| format!("invalid candidate: {error:?}"))?;
236 Ok(candidate)
237}
238
239fn literal<'a>(input: &'a Input, name: &str) -> Result<LiteralValue<'a>> {
240 let query = Query::parse(
241 &format!("filter(name(\"{name}\")) | values"),
242 Limits::default(),
243 )
244 .map_err(|error| error.message)?;
245 let QueryResult::Literals(values) = query
246 .evaluate(input, Limits::default())
247 .map_err(|error| error.message)?
248 else {
249 unreachable!()
250 };
251 match values.values() {
252 [value] => Ok(value.clone()),
253 _ => Err(format!("expected one {name} assignment")),
254 }
255}Sourcepub fn evaluate_with_facts<'a>(
&self,
input: &'a Input,
facts: Rc<ModelFacts<'a>>,
limits: Limits,
) -> Result<QueryResult<'a>, QueryError>
pub fn evaluate_with_facts<'a>( &self, input: &'a Input, facts: Rc<ModelFacts<'a>>, limits: Limits, ) -> Result<QueryResult<'a>, QueryError>
Evaluate against reusable root-local facts. The returned semantic result retains the owner; each evaluation still charges its own scan and output.
Sourcepub fn evaluate_with_model<'a>(
&self,
input: &'a Input,
model: Option<&'a ModelContext>,
limits: Limits,
) -> Result<QueryResult<'a>, QueryError>
pub fn evaluate_with_model<'a>( &self, input: &'a Input, model: Option<&'a ModelContext>, limits: Limits, ) -> Result<QueryResult<'a>, QueryError>
Semantic inspection borrows the supplied model and requires exact root bytes. Enum reduction keeps its separate optional data/model interpretation contract.
Trait Implementations§
Auto Trait Implementations§
impl !Send for Query
impl !Sync for Query
impl Freeze for Query
impl RefUnwindSafe for Query
impl Unpin for Query
impl UnsafeUnpin for Query
impl UnwindSafe for Query
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more