1use crate::{ast::*, language::NginxLanguage, lexer::token_type::NginxTokenType, parser::element_type::NginxElementType};
2use oak_core::{
3 Builder, BuilderCache, GreenNode, Lexer, OakDiagnostics, OakError, Parser, RedNode, RedTree, TextEdit,
4 source::{Source, SourceText},
5};
6
7pub struct NginxBuilder<'config> {
9 config: &'config NginxLanguage,
10}
11
12impl<'config> NginxBuilder<'config> {
13 pub fn new(config: &'config NginxLanguage) -> Self {
15 Self { config }
16 }
17}
18
19impl<'config> Builder<NginxLanguage> for NginxBuilder<'config> {
20 fn build<'a, S: Source + ?Sized>(&self, source: &S, edits: &[TextEdit], _cache: &'a mut impl BuilderCache<NginxLanguage>) -> OakDiagnostics<NginxRoot> {
21 let parser = crate::parser::NginxParser::new(self.config);
22 let lexer = crate::lexer::NginxLexer::new(&self.config);
23
24 let mut session = oak_core::parser::session::ParseSession::<NginxLanguage>::default();
25 lexer.lex(source, edits, &mut session);
26 let parse_result = parser.parse(source, edits, &mut session);
27
28 match parse_result.result {
29 Ok(green_tree) => {
30 let source_text = SourceText::new(source.get_text_in((0..source.length()).into()).into_owned());
31 match self.build_root(green_tree, &source_text) {
32 Ok(ast_root) => OakDiagnostics { result: Ok(ast_root), diagnostics: parse_result.diagnostics },
33 Err(build_error) => {
34 let mut diagnostics = parse_result.diagnostics;
35 diagnostics.push(build_error.clone());
36 OakDiagnostics { result: Err(build_error), diagnostics }
37 }
38 }
39 }
40 Err(parse_error) => OakDiagnostics { result: Err(parse_error), diagnostics: parse_result.diagnostics },
41 }
42 }
43}
44
45impl<'config> NginxBuilder<'config> {
46 pub(crate) fn build_root(&self, green_tree: &GreenNode<NginxLanguage>, source: &SourceText) -> Result<NginxRoot, OakError> {
47 let red_root = RedNode::new(green_tree, 0);
48 let mut items = Vec::new();
49
50 for child in red_root.children() {
51 if let RedTree::Node(node) = child {
52 match node.green.kind {
53 NginxElementType::Directive => {
54 items.push(NginxItem::Directive(self.build_directive(node, source)?));
55 }
56 NginxElementType::Block => {
57 items.push(NginxItem::Block(self.build_block(node, source)?));
58 }
59 NginxElementType::Comment => {
60 items.push(NginxItem::Comment(self.build_comment(node, source)?));
61 }
62 _ => {}
63 }
64 }
65 }
66
67 Ok(NginxRoot { range: red_root.span(), items })
68 }
69
70 fn build_directive(&self, node: RedNode<NginxLanguage>, source: &SourceText) -> Result<Directive, OakError> {
71 let mut name = String::new();
72 let mut parameters = Vec::new();
73 let mut children = node.children();
74
75 while let Some(child) = children.next() {
77 match child {
78 RedTree::Leaf(t) => {
79 if t.kind != NginxTokenType::Whitespace && t.kind != NginxTokenType::CommentToken {
80 name = source.get_text_in(t.span()).as_ref().to_string();
81 break;
82 }
83 }
84 _ => {}
85 }
86 }
87
88 for child in children {
90 match child {
91 RedTree::Node(n) if n.green.kind == NginxElementType::Parameter => {
92 parameters.push(source.get_text_in(n.span()).as_ref().trim().to_string());
93 }
94 RedTree::Leaf(t) if t.kind != NginxTokenType::Whitespace && t.kind != NginxTokenType::Semicolon && t.kind != NginxTokenType::CommentToken => {
95 parameters.push(source.get_text_in(t.span()).as_ref().to_string());
96 }
97 _ => {}
98 }
99 }
100
101 Ok(Directive { name, parameters, range: node.span() })
102 }
103
104 fn build_block(&self, node: RedNode<NginxLanguage>, source: &SourceText) -> Result<Block, OakError> {
105 let mut name = String::new();
106 let mut parameters = Vec::new();
107 let mut items = Vec::new();
108 let mut children = node.children();
109
110 while let Some(child) = children.next() {
112 match child {
113 RedTree::Leaf(t) => {
114 if t.kind != NginxTokenType::Whitespace && t.kind != NginxTokenType::CommentToken {
115 name = source.get_text_in(t.span()).as_ref().to_string();
116 break;
117 }
118 }
119 _ => {}
120 }
121 }
122
123 for child in children {
125 match child {
126 RedTree::Node(n) => match n.green.kind {
127 NginxElementType::Parameter => {
128 parameters.push(source.get_text_in(n.span()).as_ref().trim().to_string());
129 }
130 NginxElementType::Directive => {
131 items.push(NginxItem::Directive(self.build_directive(n, source)?));
132 }
133 NginxElementType::Block => {
134 items.push(NginxItem::Block(self.build_block(n, source)?));
135 }
136 NginxElementType::Comment => {
137 items.push(NginxItem::Comment(self.build_comment(n, source)?));
138 }
139 _ => {}
140 },
141 RedTree::Leaf(t) if t.kind != NginxTokenType::Whitespace && t.kind != NginxTokenType::LeftBrace && t.kind != NginxTokenType::RightBrace && t.kind != NginxTokenType::CommentToken => {
142 parameters.push(source.get_text_in(t.span()).as_ref().to_string());
143 }
144 _ => {}
145 }
146 }
147
148 Ok(Block { name, parameters, items, range: node.span() })
149 }
150
151 fn build_comment(&self, node: RedNode<NginxLanguage>, source: &SourceText) -> Result<Comment, OakError> {
152 Ok(Comment { text: source.get_text_in(node.span()).as_ref().to_string(), range: node.span() })
153 }
154}