1use 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
21struct 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
47struct 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
65struct 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#[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#[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#[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#[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#[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#[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 pub datalog_parameters: HashSet<String>,
207 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 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#[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 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 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 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#[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 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 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 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#[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 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 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 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#[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 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 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 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}