cqlc_core/lib.rs
1//! Pure CQL front end: lexer, parser, schema resolver, type checker and
2//! typed IR. No I/O and no database access.
3//!
4//! # Stability
5//!
6//! The supported API is [`compile`] with its [`Input`] and [`Compilation`],
7//! and the data it returns: [`ir`], [`schema`], [`types`], [`diagnostic`] and
8//! [`span`]. Open enums and structs of those modules are `#[non_exhaustive]`,
9//! so adding variants or fields is not a breaking change.
10//!
11//! The modules `ast`, `lexer`, `parser`, `resolver` and `checker` are the
12//! compiler's internals. They are public for testing and tooling, hidden from
13//! the documentation and **not covered by semver**.
14//!
15//! ```
16//! use cqlc_core::{compile, Input};
17//!
18//! let c = compile(
19//! &[Input::new("schema.cql", "CREATE TABLE t (k uuid PRIMARY KEY, v text);")],
20//! &[Input::new("q.cql", "-- name: Get :one\nSELECT v FROM t WHERE k = ?;")],
21//! );
22//! assert!(c.diagnostics.is_empty());
23//! assert_eq!(c.ir.queries[0].name, "Get");
24//! ```
25
26#![warn(missing_docs)]
27
28#[doc(hidden)]
29pub mod ast;
30#[doc(hidden)]
31pub mod checker;
32pub mod diagnostic;
33pub mod ir;
34#[doc(hidden)]
35pub mod lexer;
36#[doc(hidden)]
37pub mod parser;
38#[doc(hidden)]
39pub mod resolver;
40pub mod schema;
41pub mod span;
42pub mod types;
43
44use diagnostic::Diagnostic;
45use span::SourceMap;
46
47/// One input file for [`compile`].
48#[derive(Clone, Debug)]
49#[non_exhaustive]
50pub struct Input {
51 /// Name shown in diagnostics, usually the file path.
52 pub name: String,
53 /// The file contents.
54 pub text: String,
55}
56
57impl Input {
58 /// Creates an input from a display name and the file contents.
59 pub fn new(name: impl Into<String>, text: impl Into<String>) -> Self {
60 Input {
61 name: name.into(),
62 text: text.into(),
63 }
64 }
65}
66
67/// Result of a full front-end run.
68#[derive(Clone, Debug)]
69#[non_exhaustive]
70pub struct Compilation {
71 /// All source files, needed to render diagnostics.
72 pub sources: SourceMap,
73 /// The resolved schema.
74 pub schema: schema::Schema,
75 /// Typed queries; queries with errors are not included.
76 pub ir: ir::Ir,
77 /// Errors and warnings, in file order.
78 pub diagnostics: Vec<Diagnostic>,
79}
80
81impl Compilation {
82 /// Whether any diagnostic is an error.
83 pub fn has_errors(&self) -> bool {
84 self.diagnostics.iter().any(Diagnostic::is_error)
85 }
86}
87
88/// Runs lexer, parser, resolver and checker over schema and query files.
89/// File order determines output order; callers should sort for determinism.
90pub fn compile(schema_files: &[Input], query_files: &[Input]) -> Compilation {
91 let mut sources = SourceMap::default();
92 let mut diagnostics = Vec::new();
93 let mut ddl = Vec::new();
94 for f in schema_files {
95 let id = sources.add(&f.name, &f.text);
96 let (stmts, mut d) = parser::parse_schema(id, &f.text);
97 ddl.extend(stmts);
98 diagnostics.append(&mut d);
99 }
100 let (schema, mut d) = resolver::build_schema(&ddl);
101 diagnostics.append(&mut d);
102 let mut queries = Vec::new();
103 for f in query_files {
104 let id = sources.add(&f.name, &f.text);
105 let (qs, mut d) = parser::parse_queries(id, &f.text);
106 queries.extend(qs);
107 diagnostics.append(&mut d);
108 }
109 let (ir, mut d) = checker::analyze(&schema, &queries);
110 diagnostics.append(&mut d);
111 Compilation {
112 sources,
113 schema,
114 ir,
115 diagnostics,
116 }
117}