oak_pretty_print/rules/
mod.rs1use crate::{Document, FormatContext, FormatResult};
2use alloc::{boxed::Box, vec::Vec};
3use oak_core::{
4 errors::OakError,
5 language::Language,
6 tree::{RedLeaf, RedNode},
7};
8
9pub trait FormatRule<L: Language> {
11 fn name(&self) -> &str;
13
14 fn priority(&self) -> u8 {
16 0
17 }
18
19 fn applies_to_node(&self, _node: &RedNode<L>) -> bool {
21 false
22 }
23
24 fn applies_to_token(&self, _token: &RedLeaf<L>) -> bool {
26 false
27 }
28
29 fn apply_node<'a>(&self, node: &RedNode<L>, context: &FormatContext<L>, source: &'a str, format_children: &dyn Fn(&RedNode<L>) -> FormatResult<Document<'a>>) -> FormatResult<Option<Document<'a>>>;
31
32 fn apply_token<'a>(&self, token: &RedLeaf<L>, context: &FormatContext<L>, source: &'a str) -> FormatResult<Option<Document<'a>>>;
34
35 fn conflicts_with(&self, _other: &dyn FormatRule<L>) -> bool {
37 false
38 }
39}
40
41pub struct RuleSet<L: Language> {
43 rules: Vec<Box<dyn FormatRule<L>>>,
44}
45
46impl<L: Language> Default for RuleSet<L> {
47 fn default() -> Self {
48 Self { rules: Vec::new() }
49 }
50}
51
52impl<L: Language> RuleSet<L> {
53 pub fn new() -> Self {
55 Self::default()
56 }
57
58 pub fn add_rule(&mut self, rule: Box<dyn FormatRule<L>>) -> FormatResult<()> {
60 for existing_rule in &self.rules {
62 if rule.conflicts_with(existing_rule.as_ref()) || existing_rule.conflicts_with(rule.as_ref()) {
63 return Err(OakError::format_error(format!("Rule conflict between '{}' and '{}'", existing_rule.name(), rule.name())));
64 }
65 }
66
67 self.rules.push(rule);
68
69 self.rules.sort_by(|a, b| b.priority().cmp(&a.priority()));
71
72 Ok(())
73 }
74
75 pub fn add_rules(&mut self, rules: Vec<Box<dyn FormatRule<L>>>) -> FormatResult<()> {
77 for rule in rules {
78 self.add_rule(rule)?;
79 }
80 Ok(())
81 }
82
83 pub fn applicable_rules_for_node<'a>(&'a self, node: &'a RedNode<L>) -> impl Iterator<Item = &'a dyn FormatRule<L>> + 'a {
85 self.rules.iter().filter(move |rule| rule.applies_to_node(node)).map(|rule| rule.as_ref())
86 }
87
88 pub fn applicable_rules_for_token<'a>(&'a self, token: &'a RedLeaf<L>) -> impl Iterator<Item = &'a dyn FormatRule<L>> + 'a {
90 self.rules.iter().filter(move |rule| rule.applies_to_token(token)).map(|rule| rule.as_ref())
91 }
92
93 pub fn apply_node_rules<'a>(&self, node: &RedNode<L>, context: &FormatContext<L>, source: &'a str, format_children: &dyn Fn(&RedNode<L>) -> FormatResult<Document<'a>>) -> FormatResult<Option<Document<'a>>> {
95 for rule in self.applicable_rules_for_node(node) {
96 if let Some(doc) = rule.apply_node(node, context, source, format_children)? {
97 return Ok(Some(doc));
98 }
99 }
100 Ok(None)
101 }
102
103 pub fn apply_token_rules<'a>(&self, token: &RedLeaf<L>, context: &FormatContext<L>, source: &'a str) -> FormatResult<Option<Document<'a>>> {
105 for rule in self.applicable_rules_for_token(token) {
106 if let Some(doc) = rule.apply_token(token, context, source)? {
107 return Ok(Some(doc));
108 }
109 }
110 Ok(None)
111 }
112}