Skip to main content

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}