1#![warn(missing_docs)]
49#![forbid(unsafe_code)]
50
51use ast::Schema;
52use ir::SchemaIr;
53use std::path::{Path, PathBuf};
54
55pub mod analysis;
56pub mod ast;
57pub mod bool_expr;
58pub mod diagnostic;
59mod error;
60pub mod formatter;
61pub mod ir;
62mod lexer;
63pub mod parser;
64mod span;
65pub mod sql_expr;
66mod token;
67mod validator;
68pub mod visitor;
69
70pub use analysis::{
71 analyze, completion, completion_with_analysis, goto_definition, goto_definition_with_analysis,
72 hover, hover_with_analysis, semantic_tokens, AnalysisResult, CompletionItem, CompletionKind,
73 HoverInfo, SemanticKind, SemanticToken,
74};
75pub use ast::ComputedKind;
76pub use diagnostic::{Diagnostic, Severity};
77pub use error::{Result, SchemaError};
78pub use formatter::format_schema;
79pub use lexer::Lexer;
80pub use parser::Parser;
81pub use span::{Position, Span};
82pub use token::{Token, TokenKind};
83pub use validator::validate_schema;
84
85#[derive(Debug, Clone)]
87pub struct ParsedSchema {
88 pub ast: Schema,
90 pub recovered_errors: Vec<SchemaError>,
92}
93
94#[derive(Debug, Clone)]
96pub struct ValidatedSchema {
97 pub ast: Schema,
99 pub ir: SchemaIr,
101}
102
103fn lex_source(source: &str) -> Result<Vec<Token>> {
104 let mut lexer = Lexer::new(source);
105 let mut tokens = Vec::new();
106 loop {
107 let token = lexer.next_token()?;
108 let is_eof = matches!(token.kind, TokenKind::Eof);
109 tokens.push(token);
110 if is_eof {
111 break;
112 }
113 }
114 Ok(tokens)
115}
116
117pub fn parse_schema_source_with_recovery(source: &str) -> Result<ParsedSchema> {
119 let tokens = lex_source(source)?;
120 let mut parser = Parser::new(&tokens, source);
121 let ast = parser.parse_schema()?;
122 let recovered_errors = parser.take_errors();
123 Ok(ParsedSchema {
124 ast,
125 recovered_errors,
126 })
127}
128
129pub fn parse_schema_source(source: &str) -> Result<Schema> {
131 let parsed = parse_schema_source_with_recovery(source)?;
132 if let Some(error) = parsed.recovered_errors.into_iter().next() {
133 return Err(error);
134 }
135 Ok(parsed.ast)
136}
137
138pub fn validate_schema_source(source: &str) -> Result<ValidatedSchema> {
140 let ast = parse_schema_source(source)?;
141 let ir = validate_schema(ast.clone())?;
142 Ok(ValidatedSchema { ast, ir })
143}
144
145pub fn discover_schema_paths(dir: &Path) -> std::io::Result<Vec<PathBuf>> {
147 let mut paths: Vec<PathBuf> = std::fs::read_dir(dir)?
148 .filter_map(|entry| entry.ok())
149 .map(|entry| entry.path())
150 .filter(|path| {
151 path.is_file() && path.extension().and_then(|ext| ext.to_str()) == Some("nautilus")
152 })
153 .collect();
154 paths.sort();
155 Ok(paths)
156}
157
158pub fn discover_schema_paths_in_current_dir() -> std::io::Result<Vec<PathBuf>> {
160 let current_dir = std::env::current_dir()?;
161 discover_schema_paths(¤t_dir)
162}
163
164pub fn resolve_env_url(raw: &str) -> std::result::Result<String, String> {
169 if raw.starts_with("env(") && raw.ends_with(')') {
170 let var = &raw[4..raw.len() - 1];
171 std::env::var(var).map_err(|_| format!("environment variable '{}' is not set", var))
172 } else {
173 Ok(raw.to_string())
174 }
175}