biscuit_quote/
lib.rs

1/*
2 * Copyright (c) 2019 Geoffroy Couprie <contact@geoffroycouprie.com> and Contributors to the Eclipse Foundation.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5//! Procedural macros to build biscuit-auth tokens and authorizers
6
7use biscuit_parser::{
8    builder::{Check, Fact, Policy, Rule},
9    error,
10    parser::{parse_block_source, parse_source},
11};
12use proc_macro2::{Span, TokenStream};
13use proc_macro_error2::{abort_call_site, proc_macro_error};
14use quote::{quote, ToTokens};
15use std::collections::{HashMap, HashSet};
16use syn::{
17    parse::{self, Parse, ParseStream},
18    Expr, Ident, LitStr, Token, TypePath,
19};
20
21// parses ", foo = bar, baz = quux", including the leading comma
22struct ParsedParameters {
23    parameters: HashMap<String, Expr>,
24}
25
26impl Parse for ParsedParameters {
27    fn parse(input: ParseStream) -> parse::Result<Self> {
28        let mut parameters = HashMap::new();
29
30        while input.peek(Token![,]) {
31            let _: Token![,] = input.parse()?;
32            if input.is_empty() {
33                break;
34            }
35
36            let key: Ident = input.parse()?;
37            let _: Token![=] = input.parse()?;
38            let value: Expr = input.parse()?;
39
40            parameters.insert(key.to_string(), value);
41        }
42
43        Ok(Self { parameters })
44    }
45}
46
47// parses "\"...\", foo = bar, baz = quux"
48struct ParsedCreateNew {
49    datalog: String,
50    parameters: HashMap<String, Expr>,
51}
52
53impl Parse for ParsedCreateNew {
54    fn parse(input: ParseStream) -> parse::Result<Self> {
55        let datalog = input.parse::<LitStr>()?.value();
56        let parameters = input.parse::<ParsedParameters>()?;
57
58        Ok(Self {
59            datalog,
60            parameters: parameters.parameters,
61        })
62    }
63}
64
65// parses "&mut b, \"...\", foo = bar, baz = quux"
66struct ParsedMerge {
67    target: Expr,
68    datalog: String,
69    parameters: HashMap<String, Expr>,
70}
71
72impl Parse for ParsedMerge {
73    fn parse(input: ParseStream) -> parse::Result<Self> {
74        let target = input.parse::<Expr>()?;
75        let _: Token![,] = input.parse()?;
76
77        let datalog = input.parse::<LitStr>()?.value();
78        let parameters = input.parse::<ParsedParameters>()?;
79
80        Ok(Self {
81            target,
82            datalog,
83            parameters: parameters.parameters,
84        })
85    }
86}
87
88/// Create a `BlockBuilder` from a datalog string and optional parameters.
89/// The datalog string is parsed at compile time and replaced by manual
90/// block building.
91#[proc_macro]
92#[proc_macro_error]
93pub fn block(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
94    let ParsedCreateNew {
95        datalog,
96        parameters,
97    } = syn::parse_macro_input!(input as ParsedCreateNew);
98
99    let ty = syn::parse_quote!(::biscuit_auth::builder::BlockBuilder);
100    let builder = Builder::block_source(ty, None, datalog, parameters)
101        .unwrap_or_else(|e| abort_call_site!(e.to_string()));
102
103    builder.into_token_stream().into()
104}
105
106/// Merge facts, rules, and checks into a `BlockBuilder` from a datalog
107/// string and optional parameters. The datalog string is parsed at compile time
108/// and replaced by manual block building.
109#[proc_macro]
110#[proc_macro_error]
111pub fn block_merge(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
112    let ParsedMerge {
113        target,
114        datalog,
115        parameters,
116    } = syn::parse_macro_input!(input as ParsedMerge);
117
118    let ty = syn::parse_quote!(::biscuit_auth::builder::BlockBuilder);
119    let builder = Builder::block_source(ty, Some(target), datalog, parameters)
120        .unwrap_or_else(|e| abort_call_site!(e.to_string()));
121
122    builder.into_token_stream().into()
123}
124
125/// Create an `Authorizer` from a datalog string and optional parameters.
126/// The datalog string is parsed at compile time and replaced by manual
127/// block building.
128#[proc_macro]
129#[proc_macro_error]
130pub fn authorizer(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
131    let ParsedCreateNew {
132        datalog,
133        parameters,
134    } = syn::parse_macro_input!(input as ParsedCreateNew);
135
136    let ty = syn::parse_quote!(::biscuit_auth::builder::AuthorizerBuilder);
137    let builder = Builder::source(ty, None, datalog, parameters)
138        .unwrap_or_else(|e| abort_call_site!(e.to_string()));
139
140    builder.into_token_stream().into()
141}
142
143/// Merge facts, rules, checks, and policies into an `Authorizer` from a datalog
144/// string and optional parameters. The datalog string is parsed at compile time
145/// and replaced by manual block building.
146#[proc_macro]
147#[proc_macro_error]
148pub fn authorizer_merge(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
149    let ParsedMerge {
150        target,
151        datalog,
152        parameters,
153    } = syn::parse_macro_input!(input as ParsedMerge);
154
155    let ty = syn::parse_quote!(::biscuit_auth::builder::AuthorizerBuilder);
156    let builder = Builder::source(ty, Some(target), datalog, parameters)
157        .unwrap_or_else(|e| abort_call_site!(e.to_string()));
158
159    builder.into_token_stream().into()
160}
161
162/// Create an `BiscuitBuilder` from a datalog string and optional parameters.
163/// The datalog string is parsed at compile time and replaced by manual
164/// block building.
165#[proc_macro]
166#[proc_macro_error]
167pub fn biscuit(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
168    let ParsedCreateNew {
169        datalog,
170        parameters,
171    } = syn::parse_macro_input!(input as ParsedCreateNew);
172
173    let ty = syn::parse_quote!(::biscuit_auth::builder::BiscuitBuilder);
174    let builder = Builder::block_source(ty, None, datalog, parameters)
175        .unwrap_or_else(|e| abort_call_site!(e.to_string()));
176
177    builder.into_token_stream().into()
178}
179
180/// Merge facts, rules, and checks into a `BiscuitBuilder` from a datalog
181/// string and optional parameters. The datalog string is parsed at compile time
182/// and replaced by manual block building.
183#[proc_macro]
184#[proc_macro_error]
185pub fn biscuit_merge(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
186    let ParsedMerge {
187        target,
188        datalog,
189        parameters,
190    } = syn::parse_macro_input!(input as ParsedMerge);
191
192    let ty = syn::parse_quote!(::biscuit_auth::builder::BiscuitBuilder);
193    let builder = Builder::block_source(ty, Some(target), datalog, parameters)
194        .unwrap_or_else(|e| abort_call_site!(e.to_string()));
195
196    builder.into_token_stream().into()
197}
198
199#[derive(Clone, Debug)]
200struct Builder {
201    pub builder_type: TypePath,
202    pub target: Option<Expr>,
203    pub parameters: HashMap<String, Expr>,
204
205    // parameters used in the datalog source
206    pub datalog_parameters: HashSet<String>,
207    // parameters provided to the macro
208    pub macro_parameters: HashSet<String>,
209
210    pub facts: Vec<Fact>,
211    pub rules: Vec<Rule>,
212    pub checks: Vec<Check>,
213    pub policies: Vec<Policy>,
214}
215
216impl Builder {
217    fn new(
218        builder_type: TypePath,
219        target: Option<Expr>,
220        parameters: HashMap<String, Expr>,
221    ) -> Self {
222        let macro_parameters = parameters.keys().cloned().collect();
223
224        Self {
225            builder_type,
226            target,
227            parameters,
228
229            datalog_parameters: HashSet::new(),
230            macro_parameters,
231
232            facts: Vec::new(),
233            rules: Vec::new(),
234            checks: Vec::new(),
235            policies: Vec::new(),
236        }
237    }
238
239    fn block_source<T: AsRef<str>>(
240        builder_type: TypePath,
241        target: Option<Expr>,
242        source: T,
243        parameters: HashMap<String, Expr>,
244    ) -> Result<Builder, error::LanguageError> {
245        let mut builder = Builder::new(builder_type, target, parameters);
246        let source = parse_block_source(source.as_ref())?;
247
248        builder.facts(source.facts.into_iter().map(|(_name, fact)| fact));
249        builder.rules(source.rules.into_iter().map(|(_name, rule)| rule));
250        builder.checks(source.checks.into_iter().map(|(_name, check)| check));
251
252        builder.validate()?;
253        Ok(builder)
254    }
255
256    fn source<T: AsRef<str>>(
257        builder_type: TypePath,
258        target: Option<Expr>,
259        source: T,
260        parameters: HashMap<String, Expr>,
261    ) -> Result<Builder, error::LanguageError> {
262        let mut builder = Builder::new(builder_type, target, parameters);
263        let source = parse_source(source.as_ref())?;
264
265        builder.facts(source.facts.into_iter().map(|(_name, fact)| fact));
266        builder.rules(source.rules.into_iter().map(|(_name, rule)| rule));
267        builder.checks(source.checks.into_iter().map(|(_name, check)| check));
268        builder.policies(source.policies.into_iter().map(|(_name, policy)| policy));
269
270        builder.validate()?;
271        Ok(builder)
272    }
273
274    fn facts(&mut self, facts: impl Iterator<Item = Fact>) {
275        for fact in facts {
276            if let Some(parameters) = &fact.parameters {
277                self.datalog_parameters.extend(parameters.keys().cloned());
278            }
279            self.facts.push(fact);
280        }
281    }
282
283    fn rule_parameters(&mut self, rule: &Rule) {
284        if let Some(parameters) = &rule.parameters {
285            self.datalog_parameters.extend(parameters.keys().cloned());
286        }
287
288        if let Some(parameters) = &rule.scope_parameters {
289            self.datalog_parameters.extend(parameters.keys().cloned());
290        }
291    }
292
293    fn rules(&mut self, rules: impl Iterator<Item = Rule>) {
294        for rule in rules {
295            self.rule_parameters(&rule);
296            self.rules.push(rule);
297        }
298    }
299
300    fn checks(&mut self, checks: impl Iterator<Item = Check>) {
301        for check in checks {
302            for rule in check.queries.iter() {
303                self.rule_parameters(rule);
304            }
305            self.checks.push(check);
306        }
307    }
308
309    fn policies(&mut self, policies: impl Iterator<Item = Policy>) {
310        for policy in policies {
311            for rule in policy.queries.iter() {
312                self.rule_parameters(rule);
313            }
314            self.policies.push(policy);
315        }
316    }
317
318    fn validate(&self) -> Result<(), error::LanguageError> {
319        if self.macro_parameters.is_subset(&self.datalog_parameters) {
320            Ok(())
321        } else {
322            let unused_parameters: Vec<String> = self
323                .macro_parameters
324                .difference(&self.datalog_parameters)
325                .cloned()
326                .collect();
327            Err(error::LanguageError::Parameters {
328                missing_parameters: Vec::new(),
329                unused_parameters,
330            })
331        }
332    }
333}
334
335struct Item {
336    parameters: HashSet<String>,
337    start: TokenStream,
338    middle: TokenStream,
339    end: TokenStream,
340}
341
342impl Item {
343    fn fact(fact: &Fact) -> Self {
344        Self {
345            parameters: fact
346                .parameters
347                .iter()
348                .flatten()
349                .map(|(name, _)| name.to_owned())
350                .collect(),
351            start: quote! {
352                let mut __biscuit_auth_item = #fact;
353            },
354            middle: TokenStream::new(),
355            end: quote! {
356                __biscuit_auth_builder = __biscuit_auth_builder.fact(__biscuit_auth_item).unwrap();
357            },
358        }
359    }
360    fn rule(rule: &Rule) -> Self {
361        Self {
362            parameters: Item::rule_params(rule).collect(),
363            start: quote! {
364                let mut __biscuit_auth_item = #rule;
365            },
366            middle: TokenStream::new(),
367            end: quote! {
368                __biscuit_auth_builder = __biscuit_auth_builder.rule(__biscuit_auth_item).unwrap();
369            },
370        }
371    }
372
373    fn check(check: &Check) -> Self {
374        Self {
375            parameters: check.queries.iter().flat_map(Item::rule_params).collect(),
376            start: quote! {
377                let mut __biscuit_auth_item = #check;
378            },
379            middle: TokenStream::new(),
380            end: quote! {
381                __biscuit_auth_builder =__biscuit_auth_builder.check(__biscuit_auth_item).unwrap();
382            },
383        }
384    }
385
386    fn policy(policy: &Policy) -> Self {
387        Self {
388            parameters: policy.queries.iter().flat_map(Item::rule_params).collect(),
389            start: quote! {
390                let mut __biscuit_auth_item = #policy;
391            },
392            middle: TokenStream::new(),
393            end: quote! {
394                __biscuit_auth_builder = __biscuit_auth_builder.policy(__biscuit_auth_item).unwrap();
395            },
396        }
397    }
398
399    fn rule_params(rule: &Rule) -> impl Iterator<Item = String> + '_ {
400        rule.parameters
401            .iter()
402            .flatten()
403            .map(|(name, _)| name.as_ref())
404            .chain(
405                rule.scope_parameters
406                    .iter()
407                    .flatten()
408                    .map(|(name, _)| name.as_ref()),
409            )
410            .map(str::to_owned)
411    }
412
413    fn needs_param(&self, name: &str) -> bool {
414        self.parameters.contains(name)
415    }
416
417    fn add_param(&mut self, name: &str, clone: bool) {
418        let ident = Ident::new(name, Span::call_site());
419
420        let expr = if clone {
421            quote! { ::core::clone::Clone::clone(&#ident) }
422        } else {
423            quote! { #ident }
424        };
425
426        self.middle.extend(quote! {
427            __biscuit_auth_item.set_macro_param(#name, #expr).unwrap();
428        });
429    }
430}
431
432impl ToTokens for Item {
433    fn to_tokens(&self, tokens: &mut TokenStream) {
434        tokens.extend(self.start.clone());
435        tokens.extend(self.middle.clone());
436        tokens.extend(self.end.clone());
437    }
438}
439
440impl ToTokens for Builder {
441    fn to_tokens(&self, tokens: &mut TokenStream) {
442        let params_quote = {
443            let (ident, expr): (Vec<_>, Vec<_>) = self
444                .parameters
445                .iter()
446                .map(|(name, expr)| {
447                    let ident = Ident::new(name, Span::call_site());
448                    (ident, expr)
449                })
450                .unzip();
451
452            // Bind all parameters "in parallel". If this were a sequence of let bindings,
453            // earlier bindings would affect the scope of later bindings.
454            quote! {
455                let (#(#ident),*) = (#(#expr),*);
456            }
457        };
458
459        let mut items = self
460            .facts
461            .iter()
462            .map(Item::fact)
463            .chain(self.rules.iter().map(Item::rule))
464            .chain(self.checks.iter().map(Item::check))
465            .chain(self.policies.iter().map(Item::policy))
466            .collect::<Vec<_>>();
467
468        for param in &self.datalog_parameters {
469            let mut items = items.iter_mut().filter(|i| i.needs_param(param)).peekable();
470
471            loop {
472                match (items.next(), items.peek()) {
473                    (Some(cur), Some(_next)) => cur.add_param(param, true),
474                    (Some(cur), None) => cur.add_param(param, false),
475                    (None, _) => break,
476                }
477            }
478        }
479
480        let builder_type = &self.builder_type;
481        let builder_quote = if let Some(target) = &self.target {
482            quote! {
483                let mut __biscuit_auth_builder: #builder_type = #target;
484            }
485        } else {
486            quote! {
487                let mut __biscuit_auth_builder = <#builder_type>::new();
488            }
489        };
490
491        tokens.extend(quote! {
492            {
493                #builder_quote
494                #params_quote
495                #(#items)*
496                __biscuit_auth_builder
497            }
498        });
499    }
500}
501
502/// Create a `Rule` from a datalog string and optional parameters.
503/// The datalog string is parsed at compile time and replaced by manual
504/// builder calls.
505#[proc_macro]
506#[proc_macro_error]
507pub fn rule(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
508    let ParsedCreateNew {
509        datalog,
510        parameters,
511    } = syn::parse_macro_input!(input as ParsedCreateNew);
512
513    // here we reuse the machinery made for managing parameter substitution
514    // for whole blocks. Of course, we're only interested in a single rule
515    // here. The block management happens only at compile-time, so it won't
516    // affect runtime performance.
517    let ty = syn::parse_quote!(::biscuit_auth::builder::BlockBuilder);
518    let builder = Builder::block_source(ty, None, datalog, parameters)
519        .unwrap_or_else(|e| abort_call_site!(e.to_string()));
520
521    let mut rule_item = if let Some(r) = builder.rules.first() {
522        if builder.rules.len() == 1 && builder.facts.is_empty() && builder.checks.is_empty() {
523            Item::rule(r)
524        } else {
525            abort_call_site!("The rule macro only accepts a single rule as input")
526        }
527    } else {
528        abort_call_site!("The rule macro only accepts a single rule as input")
529    };
530
531    // here we are only interested in returning the rule, not adding it to a
532    // builder, so we override the default behaviour and just return the rule
533    // instead of calling `add_rule`
534    rule_item.end = quote! {
535      __biscuit_auth_item
536    };
537
538    let params_quote = {
539        let (ident, expr): (Vec<_>, Vec<_>) = builder
540            .parameters
541            .iter()
542            .map(|(name, expr)| {
543                let ident = Ident::new(name, Span::call_site());
544                (ident, expr)
545            })
546            .unzip();
547
548        // Bind all parameters "in parallel". If this were a sequence of let bindings,
549        // earlier bindings would affect the scope of later bindings.
550        quote! {
551            let (#(#ident),*) = (#(#expr),*);
552        }
553    };
554
555    for param in &builder.datalog_parameters {
556        if rule_item.needs_param(param) {
557            rule_item.add_param(param, false);
558        }
559    }
560
561    (quote! {
562        {
563            #params_quote
564            #rule_item
565        }
566    })
567    .into()
568}
569
570/// Create a `Fact` from a datalog string and optional parameters.
571/// The datalog string is parsed at compile time and replaced by manual
572/// builder calls.
573#[proc_macro]
574#[proc_macro_error]
575pub fn fact(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
576    let ParsedCreateNew {
577        datalog,
578        parameters,
579    } = syn::parse_macro_input!(input as ParsedCreateNew);
580
581    // here we reuse the machinery made for managing parameter substitution
582    // for whole blocks. Of course, we're only interested in a single fact
583    // here. The block management happens only at compile-time, so it won't
584    // affect runtime performance.
585    let ty = syn::parse_quote!(::biscuit_auth::builder::BlockBuilder);
586    let builder = Builder::block_source(ty, None, datalog, parameters)
587        .unwrap_or_else(|e| abort_call_site!(e.to_string()));
588
589    let mut fact_item = if let Some(f) = builder.facts.first() {
590        if builder.facts.len() == 1 && builder.rules.is_empty() && builder.checks.is_empty() {
591            Item::fact(f)
592        } else {
593            abort_call_site!("The fact macro only accepts a single fact as input")
594        }
595    } else {
596        abort_call_site!("The fact macro only accepts a single fact as input")
597    };
598
599    // here we are only interested in returning the fact, not adding it to a
600    // builder, so we override the default behaviour and just return the fact
601    // instead of calling `add_fact`
602    fact_item.end = quote! {
603      __biscuit_auth_item
604    };
605
606    let params_quote = {
607        let (ident, expr): (Vec<_>, Vec<_>) = builder
608            .parameters
609            .iter()
610            .map(|(name, expr)| {
611                let ident = Ident::new(name, Span::call_site());
612                (ident, expr)
613            })
614            .unzip();
615
616        // Bind all parameters "in parallel". If this were a sequence of let bindings,
617        // earlier bindings would affect the scope of later bindings.
618        quote! {
619            let (#(#ident),*) = (#(#expr),*);
620        }
621    };
622
623    for param in &builder.datalog_parameters {
624        if fact_item.needs_param(param) {
625            fact_item.add_param(param, false);
626        }
627    }
628
629    (quote! {
630        {
631            #params_quote
632            #fact_item
633        }
634    })
635    .into()
636}
637
638/// Create a `Check` from a datalog string and optional parameters.
639/// The datalog string is parsed at compile time and replaced by manual
640/// builder calls.
641#[proc_macro]
642#[proc_macro_error]
643pub fn check(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
644    let ParsedCreateNew {
645        datalog,
646        parameters,
647    } = syn::parse_macro_input!(input as ParsedCreateNew);
648
649    // here we reuse the machinery made for managing parameter substitution
650    // for whole blocks. Of course, we're only interested in a single check
651    // here. The block management happens only at compile-time, so it won't
652    // affect runtime performance.
653    let ty = syn::parse_quote!(::biscuit_auth::builder::BlockBuilder);
654    let builder = Builder::block_source(ty, None, datalog, parameters)
655        .unwrap_or_else(|e| abort_call_site!(e.to_string()));
656
657    let mut check_item = if let Some(c) = builder.checks.first() {
658        if builder.checks.len() == 1 && builder.facts.is_empty() && builder.rules.is_empty() {
659            Item::check(c)
660        } else {
661            abort_call_site!("The check macro only accepts a single check as input")
662        }
663    } else {
664        abort_call_site!("The check macro only accepts a single check as input")
665    };
666
667    // here we are only interested in returning the check, not adding it to a
668    // builder, so we override the default behaviour and just return the check
669    // instead of calling `add_check`
670    check_item.end = quote! {
671      __biscuit_auth_item
672    };
673
674    let params_quote = {
675        let (ident, expr): (Vec<_>, Vec<_>) = builder
676            .parameters
677            .iter()
678            .map(|(name, expr)| {
679                let ident = Ident::new(name, Span::call_site());
680                (ident, expr)
681            })
682            .unzip();
683
684        // Bind all parameters "in parallel". If this were a sequence of let bindings,
685        // earlier bindings would affect the scope of later bindings.
686        quote! {
687            let (#(#ident),*) = (#(#expr),*);
688        }
689    };
690
691    for param in &builder.datalog_parameters {
692        if check_item.needs_param(param) {
693            check_item.add_param(param, false);
694        }
695    }
696
697    (quote! {
698        {
699            #params_quote
700            #check_item
701        }
702    })
703    .into()
704}
705
706/// Create a `Policy` from a datalog string and optional parameters.
707/// The datalog string is parsed at compile time and replaced by manual
708/// builder calls.
709#[proc_macro]
710#[proc_macro_error]
711pub fn policy(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
712    let ParsedCreateNew {
713        datalog,
714        parameters,
715    } = syn::parse_macro_input!(input as ParsedCreateNew);
716
717    // here we reuse the machinery made for managing parameter substitution
718    // for whole blocks. Of course, we're only interested in a single policy
719    // here. The block management happens only at compile-time, so it won't
720    // affect runtime performance.
721    let ty = syn::parse_quote!(::biscuit_auth::Authorizer);
722    let builder = Builder::source(ty, None, datalog, parameters)
723        .unwrap_or_else(|e| abort_call_site!(e.to_string()));
724
725    let mut policy_item = if let Some(p) = builder.policies.first() {
726        if builder.policies.len() == 1
727            && builder.facts.is_empty()
728            && builder.rules.is_empty()
729            && builder.checks.is_empty()
730        {
731            Item::policy(p)
732        } else {
733            abort_call_site!("The policy macro only accepts a single policy as input")
734        }
735    } else {
736        abort_call_site!("The policy macro only accepts a single policy as input")
737    };
738
739    // here we are only interested in returning the policy, not adding it to a
740    // builder, so we override the default behaviour and just return the policy
741    // instead of calling `add_policy`
742    policy_item.end = quote! {
743      __biscuit_auth_item
744    };
745
746    let params_quote = {
747        let (ident, expr): (Vec<_>, Vec<_>) = builder
748            .parameters
749            .iter()
750            .map(|(name, expr)| {
751                let ident = Ident::new(name, Span::call_site());
752                (ident, expr)
753            })
754            .unzip();
755
756        // Bind all parameters "in parallel". If this were a sequence of let bindings,
757        // earlier bindings would affect the scope of later bindings.
758        quote! {
759            let (#(#ident),*) = (#(#expr),*);
760        }
761    };
762
763    for param in &builder.datalog_parameters {
764        if policy_item.needs_param(param) {
765            policy_item.add_param(param, false);
766        }
767    }
768
769    (quote! {
770        {
771            #params_quote
772            #policy_item
773        }
774    })
775    .into()
776}