1use proc_macro2::Span;
2use proc_macro2::TokenStream as TokenStream2;
3use quote::format_ident;
4use quote::quote;
5use quote::ToTokens;
6use syn::braced;
7use syn::bracketed;
8use syn::parse::Parse;
9use syn::parse::ParseStream;
10use syn::punctuated::Punctuated;
11use syn::Expr;
12use syn::Ident;
13use syn::Token;
14
15mod attributes;
16mod find;
17mod value_formatter;
18
19pub use attributes::attributes_impl;
20pub use find::find_impl;
21pub use value_formatter::value_formatter_impl;
22
23struct PatternInput {
24 set: Expr,
25 pattern: Vec<Entity>,
26}
27
28struct Entity {
29 id: Option<Inline>,
30 attributes: Vec<Attribute>,
31}
32
33#[derive(Clone, Copy, Debug, Eq, PartialEq)]
34enum AttributeMode {
35 Required,
36 Optional,
37 Repeated,
38}
39
40enum Inline {
41 Var(Ident),
42 LocalVar(Ident),
43 Expr(Expr),
44}
45
46struct Attribute {
47 name: Inline,
75 mode: AttributeMode,
76 value: Inline,
77}
78
79impl Parse for PatternInput {
80 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
81 let set: Expr = input.parse()?;
82 input.parse::<Token![,]>()?;
83 let content;
84 bracketed!(content in input);
85
86 let pattern = Punctuated::<_, Token![,]>::parse_terminated(&content)?
87 .into_iter()
88 .collect();
89
90 Ok(PatternInput { set, pattern })
91 }
92}
93
94impl Parse for Entity {
95 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
96 let content;
97 braced!(content in input);
98
99 let mut id: Option<Inline> = None;
100 {
101 let fork = content.fork();
102 if fork.peek(Token![_]) {
105 let fork2 = fork;
106 fork2.parse::<Token![_]>()?;
107 if fork2.peek(Token![@]) {
108 content.parse::<Token![_]>()?;
109 content.parse::<Token![@]>()?;
110 }
111 }
112 }
113 if id.is_none() {
114 let fork = content.fork();
115 if fork.parse::<Inline>().is_ok() && fork.peek(Token![@]) {
116 let pv: Inline = content.parse()?;
117 content.parse::<Token![@]>()?;
118 id = Some(pv);
119 }
120 }
121
122 let attributes = Punctuated::<_, Token![,]>::parse_terminated(&content)?
123 .into_iter()
124 .collect();
125
126 Ok(Entity { id, attributes })
127 }
128}
129
130impl Parse for Attribute {
131 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
132 let fork = input.fork();
143 if let Ok(expr_path) = fork.parse::<syn::ExprPath>() {
144 if fork.peek(Token![?]) {
145 let fork2 = fork.fork();
146 fork2.parse::<Token![?]>()?;
147 if fork2.peek(Token![:]) {
148 let name = Inline::Expr(Expr::Path(input.parse::<syn::ExprPath>()?));
149 input.parse::<Token![?]>()?;
150 input.parse::<Token![:]>()?;
151 let value: Inline = input.parse()?;
152 return Ok(Attribute {
153 name,
154 mode: AttributeMode::Optional,
155 value,
156 });
157 }
158 }
159
160 if fork.peek(Token![*]) {
161 let fork2 = fork.fork();
162 fork2.parse::<Token![*]>()?;
163 if fork2.peek(Token![:]) {
164 let name = Inline::Expr(Expr::Path(input.parse::<syn::ExprPath>()?));
165 input.parse::<Token![*]>()?;
166 input.parse::<Token![:]>()?;
167 let value: Inline = input.parse()?;
168 return Ok(Attribute {
169 name,
170 mode: AttributeMode::Repeated,
171 value,
172 });
173 }
174 }
175
176 let _ = expr_path;
180 }
181
182 let name: Inline = input.parse()?;
183 input.parse::<Token![:]>()?;
184 let value: Inline = input.parse()?;
185 Ok(Attribute {
186 name,
187 mode: AttributeMode::Required,
188 value,
189 })
190 }
191}
192
193impl Parse for Inline {
194 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
195 if input.peek(Token![_]) {
196 let fork = input.fork();
197 fork.parse::<Token![_]>()?;
198 if fork.peek(Token![?]) {
199 input.parse::<Token![_]>()?;
200 input.parse::<Token![?]>()?;
201 let var_ident: Ident = input.parse()?;
202 return Ok(Inline::LocalVar(var_ident));
203 }
204 }
205 if input.peek(Token![?]) {
206 input.parse::<Token![?]>()?;
207 let var_ident: Ident = input.parse()?;
208 Ok(Inline::Var(var_ident))
209 } else {
210 let expr: Expr = input.parse()?;
211 Ok(Inline::Expr(expr))
212 }
213 }
214}
215
216pub fn pattern_impl(input: TokenStream2, base_path: &TokenStream2) -> syn::Result<TokenStream2> {
217 let PatternInput { set, pattern } = syn::parse2(input)?;
218
219 let ctx_ident = format_ident!("__ctx", span = Span::mixed_site());
220 let set_ident = format_ident!("__set", span = Span::mixed_site());
221
222 let mut entity_tokens = TokenStream2::new();
223 let mut attr_tokens = TokenStream2::new();
224
225 use std::collections::HashMap;
226 let mut attr_idx = 0usize;
227 let mut val_idx = 0usize;
228 let mut attr_map: HashMap<String, (Ident, Option<Ident>)> = HashMap::new();
233 let mut local_tokens = TokenStream2::new();
234 let mut local_map: HashMap<String, Ident> = HashMap::new();
235 let mut local_idx = 0usize;
236 let mut get_local_var = |ident: &Ident| {
237 let key = format!("_?{}", ident);
238 local_map
239 .entry(key)
240 .or_insert_with(|| {
241 let ident = format_ident!("__local{}", local_idx, span = Span::mixed_site());
242 local_idx += 1;
243 local_tokens.extend(quote! {
244 let #ident = #ctx_ident.next_variable();
245 });
246 ident
247 })
248 .clone()
249 };
250
251 for (entity_idx, entity) in pattern.into_iter().enumerate() {
252 let e_ident = format_ident!("__e{}", entity_idx, span = Span::mixed_site());
253 let entity_var_key: Option<(bool, String)> = match entity.id {
257 Some(Inline::LocalVar(ref ident)) => Some((true, ident.to_string())),
258 Some(Inline::Var(ref ident)) => Some((false, ident.to_string())),
259 _ => None,
260 };
261 let init = if let Some(ref id_val) = entity.id {
262 match id_val {
263 Inline::Var(ref ident) => {
264 quote! { let #e_ident = #ident; }
265 }
266 Inline::LocalVar(ref ident) => {
267 let local_ident = get_local_var(ident);
268 quote! { let #e_ident = #local_ident; }
269 }
270 Inline::Expr(ref id_expr) => {
271 quote! {
275 let #e_ident: #base_path::inline::Inline<#base_path::inline::encodings::genid::GenId> = #base_path::inline::IntoInline::to_inline(#id_expr);
276 }
277 }
278 }
279 } else {
280 quote! {
281 let #e_ident: #base_path::query::Variable<#base_path::inline::encodings::genid::GenId> = #ctx_ident.next_variable();
282 }
283 };
284 entity_tokens.extend(init);
285
286 for Attribute { name, mode, value } in entity.attributes {
287 if mode != AttributeMode::Required {
288 let span_for_err = match &name {
289 Inline::Expr(e) => e.to_token_stream(),
290 Inline::Var(i) => i.to_token_stream(),
291 Inline::LocalVar(i) => i.to_token_stream(),
292 };
293 return Err(syn::Error::new_spanned(
294 span_for_err,
295 "`?:` and `*:` are not supported in pattern!; use `attr: value`",
296 ));
297 }
298
299 let key = match &name {
311 Inline::Var(ident) => format!("?{}", ident),
312 Inline::LocalVar(ident) => format!("_?{}", ident),
313 Inline::Expr(expr) => expr.to_token_stream().to_string(),
314 };
315 let (a_var_ident, af_ident_opt) = attr_map
316 .entry(key)
317 .or_insert_with(|| {
318 let a_ident =
319 format_ident!("__a{}", attr_idx, span = Span::mixed_site());
320 attr_idx += 1;
321 match &name {
322 Inline::Expr(expr) => {
323 let af_ident = format_ident!(
324 "__af{}",
325 attr_idx,
326 span = Span::mixed_site()
327 );
328 attr_tokens.extend(quote! {
329 let #af_ident = &#expr;
330 let #a_ident: #base_path::inline::Inline<#base_path::inline::encodings::genid::GenId> = #base_path::inline::IntoInline::to_inline(#af_ident.id());
331 });
332 (a_ident, Some(af_ident))
333 }
334 Inline::Var(user_ident) => {
335 attr_tokens.extend(quote! {
336 let #a_ident: #base_path::query::Variable<#base_path::inline::encodings::genid::GenId> = #user_ident;
337 });
338 (a_ident, None)
339 }
340 Inline::LocalVar(local_ident) => {
341 let local_var = get_local_var(local_ident);
342 attr_tokens.extend(quote! {
343 let #a_ident: #base_path::query::Variable<#base_path::inline::encodings::genid::GenId> = #local_var;
344 });
345 (a_ident, None)
346 }
347 }
348 })
349 .clone();
350
351 let val_id = {
352 let v = val_idx;
353 val_idx += 1;
354 v
355 };
356 let v_tmp_ident = format_ident!("__v{}", val_id, span = Span::mixed_site());
357
358 let value_var_key: Option<(bool, String)> = match value {
362 Inline::Var(ref ident) => Some((false, ident.to_string())),
363 Inline::LocalVar(ref ident) => Some((true, ident.to_string())),
364 _ => None,
365 };
366 let self_ref = entity_var_key.is_some() && entity_var_key == value_var_key;
367
368 let triple_tokens = match (value, af_ident_opt.as_ref()) {
373 (Inline::Var(ref var_ident), Some(af_ident)) if self_ref => {
375 let alias_ident = format_ident!("__alias{}", val_id, span = Span::mixed_site());
376 quote! {
377 {
378 #[allow(unused_imports)] use #base_path::query::TriblePattern;
379 let #alias_ident: #base_path::query::Variable<#base_path::inline::encodings::genid::GenId> = #ctx_ident.next_variable();
380 let v_var = #af_ident.as_variable(#alias_ident);
381 constraints.push(Box::new(#base_path::query::equalityconstraint::EqualityConstraint::new(#e_ident.index, #alias_ident.index)));
382 constraints.push(Box::new(#set_ident.pattern(#e_ident, #a_var_ident, v_var)));
383 }
384 }
385 }
386 (Inline::Var(ref var_ident), Some(af_ident)) => {
387 quote! {
388 {
389 #[allow(unused_imports)] use #base_path::query::TriblePattern;
390 let v_var = { #af_ident.as_variable(#var_ident) };
391 constraints.push(Box::new(#set_ident.pattern(#e_ident, #a_var_ident, v_var)));
392 }
393 }
394 }
395 (Inline::LocalVar(ref var_ident), Some(af_ident)) if self_ref => {
396 let _local_ident = get_local_var(var_ident);
397 let alias_ident = format_ident!("__alias{}", val_id, span = Span::mixed_site());
398 quote! {
399 {
400 #[allow(unused_imports)] use #base_path::query::TriblePattern;
401 let #alias_ident: #base_path::query::Variable<#base_path::inline::encodings::genid::GenId> = #ctx_ident.next_variable();
402 let v_var = #af_ident.as_variable(#alias_ident);
403 constraints.push(Box::new(#base_path::query::equalityconstraint::EqualityConstraint::new(#e_ident.index, #alias_ident.index)));
404 constraints.push(Box::new(#set_ident.pattern(#e_ident, #a_var_ident, v_var)));
405 }
406 }
407 }
408 (Inline::LocalVar(ref var_ident), Some(af_ident)) => {
409 let local_ident = get_local_var(var_ident);
410 quote! {
411 {
412 #[allow(unused_imports)] use #base_path::query::TriblePattern;
413 let v_var = { #af_ident.as_variable(#local_ident) };
414 constraints.push(Box::new(#set_ident.pattern(#e_ident, #a_var_ident, v_var)));
415 }
416 }
417 }
418 (Inline::Expr(ref expr), Some(af_ident)) => {
419 quote! {
424 {
425 #[allow(unused_imports)] use #base_path::query::TriblePattern;
426 let #v_tmp_ident = #af_ident.inline_from(#expr);
427 constraints.push(Box::new(#set_ident.pattern(#e_ident, #a_var_ident, #v_tmp_ident)));
428 }
429 }
430 }
431
432 (Inline::Var(ref var_ident), None) => {
449 quote! {
450 {
451 #[allow(unused_imports)] use #base_path::query::TriblePattern;
452 let _: &#base_path::query::Variable<#base_path::inline::encodings::UnknownInline> = &#var_ident;
458 constraints.push(Box::new(#set_ident.pattern(#e_ident, #a_var_ident, #var_ident)));
459 }
460 }
461 }
462 (Inline::LocalVar(ref var_ident), None) => {
463 return Err(syn::Error::new_spanned(
464 var_ident,
465 "local helper variables (_?ident) in the value position are not supported with a free attribute (?attr); use a `find!`-projected `Variable<UnknownInline>` (`?val`) instead",
466 ));
467 }
468 (Inline::Expr(ref expr), None) => {
469 return Err(syn::Error::new_spanned(
470 expr,
471 "free attribute (?ident) requires a `Variable<UnknownInline>` query variable in the value position; concrete value expressions need a known attribute so the macro can apply its schema",
472 ));
473 }
474 };
475 entity_tokens.extend(triple_tokens);
476 }
477 }
478
479 let output = quote! {
480 {
481 let mut constraints: ::std::vec::Vec<Box<dyn #base_path::query::Constraint + Send + Sync>> = ::std::vec::Vec::new();
482 let #ctx_ident = __local_find_context!();
483 let #set_ident = #set;
484 #local_tokens
485 #attr_tokens
486 #entity_tokens
487 ::std::sync::Arc::new(
488 #base_path::query::intersectionconstraint::IntersectionConstraint::new(constraints)
489 )
490 }
491 };
492
493 Ok(output)
494}
495
496pub fn entity_impl(input: TokenStream2, base_path: &TokenStream2) -> syn::Result<TokenStream2> {
497 entity_impl_with(input, base_path, true)
498}
499
500pub(crate) fn entity_impl_no_meta(
504 input: TokenStream2,
505 base_path: &TokenStream2,
506) -> syn::Result<TokenStream2> {
507 entity_impl_with(input, base_path, false)
508}
509
510fn entity_impl_with(
511 input: TokenStream2,
512 base_path: &TokenStream2,
513 emit_meta: bool,
514) -> syn::Result<TokenStream2> {
515 let wrapped = quote! { { #input } };
516
517 let Entity { id, attributes } = syn::parse2(wrapped)?;
518
519 let state_init = quote! {
520 #[allow(unused_mut)]
521 let mut __meta: #base_path::trible::TribleSet =
522 #base_path::trible::TribleSet::new();
523 let mut __blobs: #base_path::blob::MemoryBlobStore =
524 #base_path::blob::MemoryBlobStore::new();
525 };
526 let attr_count = attributes.len();
527
528 let mut attr_eval_tokens = TokenStream2::new();
529 let mut explicit_insert_tokens = TokenStream2::new();
530 let mut intrinsic_row_tokens = TokenStream2::new();
531 let mut extra_merge_tokens = TokenStream2::new();
532
533 for (i, attr) in attributes.into_iter().enumerate() {
534 let mode = attr.mode;
535 let field_expr = match attr.name {
539 Inline::Expr(e) => e,
540 Inline::Var(id) => {
541 return Err(syn::Error::new_spanned(
542 id,
543 "variable attribute bindings (?ident) are not allowed in entity!; use a literal attribute reference here",
544 ));
545 }
546 Inline::LocalVar(id) => {
547 return Err(syn::Error::new_spanned(
548 id,
549 "local variable attribute bindings (_?ident) are not allowed in entity!; use a literal attribute reference here",
550 ));
551 }
552 };
553 let value_expr = match attr.value {
554 Inline::Expr(e) => e,
555 Inline::Var(id) => {
556 return Err(syn::Error::new_spanned(
557 id,
558 "variable bindings (?ident) are not allowed in entity!; use a literal expression here",
559 ));
560 }
561 Inline::LocalVar(id) => {
562 return Err(syn::Error::new_spanned(
563 id,
564 "local variable bindings (_?ident) are not allowed in entity!; use a literal expression here",
565 ));
566 }
567 };
568
569 let af_ident = format_ident!("__af{}", i, span = Span::mixed_site());
570 let val_ident = format_ident!("__val{}", i, span = Span::mixed_site());
571 let aid_ident = format_ident!("__a_id{}", i, span = Span::mixed_site());
572 let extra_ident = format_ident!("__extra{}", i, span = Span::mixed_site());
573
574 attr_eval_tokens.extend(quote! { let #af_ident = &#field_expr; });
575
576 let meta_tokens = if emit_meta {
580 quote! {
581 {
582 let __attr_meta = #af_ident.meta();
583 __meta.union(__attr_meta.facts().clone());
584 __meta.union(__attr_meta.metafacts().clone());
585 __blobs.union(__attr_meta.blobs().clone());
586 }
587 }
588 } else {
589 TokenStream2::new()
590 };
591
592 match mode {
593 AttributeMode::Required => {
594 attr_eval_tokens.extend(quote! {
595 let (#val_ident, __maybe_blob) =
596 #af_ident.encoded_from(#value_expr).into_parts();
597 if let Some(__b) = __maybe_blob {
598 __blobs.insert(__b);
599 }
600 #meta_tokens
601 });
602 }
603 AttributeMode::Optional => {
604 attr_eval_tokens.extend(quote! {
605 let #val_ident = {
606 let __opt: ::std::option::Option<_> = #value_expr;
607 __opt.map(|__v| {
608 let (__val, __maybe_blob) =
609 #af_ident.encoded_from(__v).into_parts();
610 if let Some(__b) = __maybe_blob {
611 __blobs.insert(__b);
612 }
613 __val
614 })
615 };
616 if #val_ident.is_some() {
617 #meta_tokens
618 }
619 });
620 }
621 AttributeMode::Repeated => {
622 attr_eval_tokens.extend(quote! {
623 let (#val_ident, #extra_ident) = {
624 let (__spread_iter, __spread_facts) =
625 #base_path::trible::Spread::spread(#value_expr);
626 let __vals = ::std::iter::IntoIterator::into_iter(__spread_iter)
627 .map(|__v| {
628 let (__val, __maybe_blob) =
629 #af_ident.encoded_from(__v).into_parts();
630 if let Some(__b) = __maybe_blob {
631 __blobs.insert(__b);
632 }
633 __val
634 })
635 .collect::<::std::vec::Vec<_>>();
636 (__vals, __spread_facts)
637 };
638 if !#val_ident.is_empty() {
639 #meta_tokens
640 }
641 });
642 }
643 }
644 attr_eval_tokens.extend(quote! { let #aid_ident = #af_ident.id(); });
645
646 match mode {
647 AttributeMode::Required => {
648 intrinsic_row_tokens.extend(quote! {
649 __rows.push(#base_path::trible::IntrinsicEntityRow::new(
650 #aid_ident,
651 #val_ident.raw,
652 ));
653 });
654 explicit_insert_tokens.extend(quote! {
655 set.insert(&#base_path::trible::Trible::new(id_ref, &#aid_ident, &#val_ident));
656 });
657 }
658 AttributeMode::Optional => {
659 intrinsic_row_tokens.extend(quote! {
660 if let Some(ref __v) = #val_ident {
661 __rows.push(#base_path::trible::IntrinsicEntityRow::new(
662 #aid_ident,
663 __v.raw,
664 ));
665 }
666 });
667 explicit_insert_tokens.extend(quote! {
668 if let Some(ref __v) = #val_ident {
669 set.insert(&#base_path::trible::Trible::new(id_ref, &#aid_ident, __v));
670 }
671 });
672 }
673 AttributeMode::Repeated => {
674 intrinsic_row_tokens.extend(quote! {
675 for __v in #val_ident.iter() {
676 __rows.push(#base_path::trible::IntrinsicEntityRow::new(
677 #aid_ident,
678 __v.raw,
679 ));
680 }
681 });
682 explicit_insert_tokens.extend(quote! {
683 for __v in #val_ident.iter() {
684 set.insert(&#base_path::trible::Trible::new(id_ref, &#aid_ident, __v));
685 }
686 });
687 extra_merge_tokens.extend(quote! {
688 let (_, __extra_facts, __extra_metafacts, __extra_blobs) =
689 #extra_ident.into_parts();
690 set += __extra_facts;
691 __meta.union(__extra_metafacts);
692 __blobs.union(__extra_blobs);
693 });
694 }
695 }
696 }
697
698 let entity_init: TokenStream2 = if let Some(val) = id {
699 match val {
700 Inline::Expr(expr) => quote! {
701 let id_tmp = #expr;
702 let id_ref: &#base_path::id::ExclusiveId = id_tmp.as_ref();
703 let root_id = id_ref.id;
704 let mut set = #base_path::trible::TribleSet::new();
705 #explicit_insert_tokens
706 },
707 Inline::Var(ident) => {
708 return Err(syn::Error::new_spanned(
709 ident,
710 "variable bindings (?ident) are not allowed in entity!; use a literal expression here",
711 ));
712 }
713 Inline::LocalVar(ident) => {
714 return Err(syn::Error::new_spanned(
715 ident,
716 "local variable bindings (_?ident) are not allowed in entity!; use a literal expression here",
717 ));
718 }
719 }
720 } else {
721 quote! {
722 let mut __rows: ::std::vec::Vec<#base_path::trible::IntrinsicEntityRow> =
723 ::std::vec::Vec::with_capacity(#attr_count);
724 #intrinsic_row_tokens
725 let (root_id, mut set) = #base_path::trible::build_intrinsic_entity(__rows);
726 }
727 };
728
729 let output = quote! {
730 {
731 #state_init
732 #attr_eval_tokens
733 #entity_init
734 #extra_merge_tokens
735 #base_path::trible::Fragment::rooted_from_parts(root_id, set, __meta, __blobs)
736 }
737 };
738
739 Ok(output)
740}
741
742struct PatternChangesInput {
743 curr: Expr,
744 changes: Expr,
745 pattern: Vec<Entity>,
746}
747
748impl Parse for PatternChangesInput {
749 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
750 let curr: Expr = input.parse()?;
751 input.parse::<Token![,]>()?;
752 let changes: Expr = input.parse()?;
753 input.parse::<Token![,]>()?;
754 let content;
755 bracketed!(content in input);
756
757 let mut pattern = Vec::new();
758 while !content.is_empty() {
759 pattern.push(content.parse()?);
760 if content.peek(Token![,]) {
761 content.parse::<Token![,]>()?;
762 }
763 }
764
765 Ok(PatternChangesInput {
766 curr,
767 changes,
768 pattern,
769 })
770 }
771}
772
773pub fn pattern_changes_impl(
774 input: TokenStream2,
775 base_path: &TokenStream2,
776) -> syn::Result<TokenStream2> {
777 use std::collections::HashMap;
778
779 let PatternChangesInput {
780 curr,
781 changes,
782 pattern,
783 } = syn::parse2(input)?;
784
785 let ctx_ident = format_ident!("__ctx", span = Span::mixed_site());
786 let curr_ident = format_ident!("__curr", span = Span::mixed_site());
787 let delta_ident = format_ident!("__delta", span = Span::mixed_site());
788
789 let mut attr_decl_tokens = TokenStream2::new();
790 let mut entity_decl_tokens = TokenStream2::new();
791 let mut entity_const_tokens = TokenStream2::new();
792 let mut value_decl_tokens = TokenStream2::new();
793
794 let mut triples = Vec::<TripleInfo>::new();
795
796 let mut attr_map: HashMap<String, (Ident, Ident)> = HashMap::new();
797 let mut attr_idx = 0usize;
798 let mut value_idx = 0usize;
799 let mut local_decl_tokens = TokenStream2::new();
800 let mut local_map: HashMap<String, Ident> = HashMap::new();
801 let mut local_idx = 0usize;
802
803 let mut get_local_var = |ident: &Ident| {
804 let key = format!("_?{}", ident);
805 local_map
806 .entry(key)
807 .or_insert_with(|| {
808 let ident = format_ident!("__local{}", local_idx, span = Span::mixed_site());
809 local_idx += 1;
810 local_decl_tokens.extend(quote! {
811 let #ident = #ctx_ident.next_variable();
812 });
813 ident
814 })
815 .clone()
816 };
817
818 for (entity_idx, entity) in pattern.into_iter().enumerate() {
819 let e_ident = format_ident!("__e{}", entity_idx, span = Span::mixed_site());
820 let entity_var_key: Option<(bool, String)> = match entity.id {
821 Some(Inline::LocalVar(ref ident)) => Some((true, ident.to_string())),
822 Some(Inline::Var(ref ident)) => Some((false, ident.to_string())),
823 _ => None,
824 };
825 match entity.id {
826 Some(ref id_val) => match id_val {
827 Inline::Var(ref ident) => {
828 entity_decl_tokens.extend(quote! { let #e_ident = #ident; });
829 }
830 Inline::LocalVar(ref ident) => {
831 let local_ident = get_local_var(ident);
832 entity_decl_tokens.extend(quote! { let #e_ident = #local_ident; });
833 }
834 Inline::Expr(ref id_expr) => {
835 entity_decl_tokens.extend(quote! {
837 let #e_ident: #base_path::inline::Inline<#base_path::inline::encodings::genid::GenId> = #base_path::inline::IntoInline::to_inline(#id_expr);
838 });
839 }
840 },
841 None => {
842 entity_decl_tokens.extend(quote! {
843 let #e_ident: #base_path::query::Variable<#base_path::inline::encodings::genid::GenId> = #ctx_ident.next_variable();
844 });
845 }
846 }
847
848 for Attribute {
849 name: attr_name,
850 mode,
851 value,
852 } in entity.attributes
853 {
854 let attr_expr = match attr_name {
860 Inline::Expr(e) => e,
861 Inline::Var(ident) => {
862 return Err(syn::Error::new_spanned(
863 ident,
864 "free attribute (?ident) is not yet supported in pattern_changes!; use a concrete attribute constant",
865 ));
866 }
867 Inline::LocalVar(ident) => {
868 return Err(syn::Error::new_spanned(
869 ident,
870 "free attribute (_?ident) is not yet supported in pattern_changes!; use a concrete attribute constant",
871 ));
872 }
873 };
874 if mode != AttributeMode::Required {
875 return Err(syn::Error::new_spanned(
876 &attr_expr,
877 "`?:` and `*:` are not supported in pattern_changes!; use `attr: value`",
878 ));
879 }
880 let key = attr_expr.to_token_stream().to_string();
881 let (a_ident, af_ident) = attr_map
882 .entry(key)
883 .or_insert_with(|| {
884 let a_ident = format_ident!("__a{}", attr_idx, span = Span::mixed_site());
885 let af_ident = format_ident!("__af{}", attr_idx, span = Span::mixed_site());
886 attr_idx += 1;
887 attr_decl_tokens.extend(quote! {
890 let #af_ident = &#attr_expr;
891 let #a_ident: #base_path::inline::Inline<#base_path::inline::encodings::genid::GenId> = #base_path::inline::IntoInline::to_inline(#af_ident.id());
892 });
893 (a_ident, af_ident)
894 })
895 .clone();
896
897 let v_ident = format_ident!("__v{}", value_idx, span = Span::mixed_site());
898 value_idx += 1;
899
900 let value_var_key: Option<(bool, String)> = match value {
901 Inline::Var(ref ident) => Some((false, ident.to_string())),
902 Inline::LocalVar(ref ident) => Some((true, ident.to_string())),
903 _ => None,
904 };
905 let self_ref = entity_var_key.is_some() && entity_var_key == value_var_key;
906
907 match value {
908 Inline::Expr(expr) => {
909 value_decl_tokens.extend(quote! {
911 let #v_ident = #af_ident.inline_from(#expr);
912 });
913 }
914 Inline::Var(_) | Inline::LocalVar(_) if self_ref => {
915 let alias_ident =
917 format_ident!("__alias{}", value_idx, span = Span::mixed_site());
918 value_idx += 1;
919 value_decl_tokens.extend(quote! {
920 let #alias_ident: #base_path::query::Variable<#base_path::inline::encodings::genid::GenId> = #ctx_ident.next_variable();
921 let #v_ident = #af_ident.as_variable(#alias_ident);
922 });
923 entity_const_tokens.extend(quote! {
924 constraints.push(Box::new(#base_path::query::equalityconstraint::EqualityConstraint::new(#e_ident.index, #alias_ident.index)));
925 });
926 }
927 Inline::Var(var_ident) => {
928 value_decl_tokens.extend(quote! {
929 let #v_ident = #af_ident.as_variable(#var_ident);
930 });
931 }
932 Inline::LocalVar(ref var_ident) => {
933 let local_ident = get_local_var(var_ident);
934 value_decl_tokens.extend(quote! {
935 let #v_ident = #af_ident.as_variable(#local_ident);
936 });
937 }
938 }
939
940 triples.push(TripleInfo {
941 e_ident: e_ident.clone(),
942 a_ident: a_ident.clone(),
943 v_ident,
944 });
945 }
946 }
947
948 let mut case_exprs: Vec<TokenStream2> = Vec::new();
949 for delta_idx in 0..triples.len() {
950 let mut triple_tokens = TokenStream2::new();
951 for (
952 idx,
953 TripleInfo {
954 e_ident,
955 a_ident,
956 v_ident,
957 },
958 ) in triples.iter().enumerate()
959 {
960 let dataset = if idx == delta_idx {
961 &delta_ident
962 } else {
963 &curr_ident
964 };
965 triple_tokens.extend(quote! {
966 constraints.push(Box::new(#dataset.pattern(#e_ident, #a_ident, #v_ident)));
967 });
968 }
969
970 let case = quote! {
971 {
972 let mut constraints: ::std::vec::Vec<Box<dyn #base_path::query::Constraint + Send + Sync>> = ::std::vec::Vec::new();
973 #[allow(unused_imports)] use #base_path::query::TriblePattern;
974 #triple_tokens
975 ::std::sync::Arc::new(
976 #base_path::query::intersectionconstraint::IntersectionConstraint::new(constraints)
977 )
978 }
979 };
980 case_exprs.push(case);
981 }
982
983 let union_expr = quote! {
984 ::std::sync::Arc::new(
985 #base_path::query::unionconstraint::UnionConstraint::new(vec![
986 #(Box::new(#case_exprs) as Box<dyn #base_path::query::Constraint + Send + Sync>),*
987 ])
988 )
989 };
990
991 let output = quote! {
992 {
993 let #ctx_ident = __local_find_context!();
994 let #curr_ident = #curr;
995 let #delta_ident = #changes;
996 #attr_decl_tokens
997 #local_decl_tokens
998 #entity_decl_tokens
999 #value_decl_tokens
1000 let mut constraints: ::std::vec::Vec<Box<dyn #base_path::query::Constraint + Send + Sync>> = ::std::vec::Vec::new();
1001 #[allow(unused_imports)] use #base_path::query::TriblePattern;
1002 #entity_const_tokens
1003 constraints.push(Box::new(#union_expr));
1004 ::std::sync::Arc::new(
1005 #base_path::query::intersectionconstraint::IntersectionConstraint::new(constraints)
1006 )
1007 }
1008 };
1009
1010 Ok(output)
1011}
1012
1013struct TripleInfo {
1014 e_ident: Ident,
1015 a_ident: Ident,
1016 v_ident: Ident,
1017}