tzcompile/source/records.rs
1//! Lexical records: the output of the lexer, before any semantic interpretation.
2//!
3//! A tzdata source file is line-oriented. After comment stripping and field splitting,
4//! each non-blank line becomes a [`Line`] holding its 1-based number and the [`Field`]s on
5//! it. Keeping the column of each field lets diagnostics point precisely at the offending
6//! token rather than just the line.
7
8/// One whitespace-delimited (or quoted) field, with the byte column it started at.
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct Field {
11 /// The field's content with surrounding quotes removed and escapes resolved.
12 pub text: String,
13 /// 0-based byte column of the field's first character on the original line.
14 pub col: usize,
15}
16
17impl Field {
18 pub fn new(text: impl Into<String>, col: usize) -> Self {
19 Field {
20 text: text.into(),
21 col,
22 }
23 }
24}
25
26/// A single logical source line that contained at least one field.
27#[derive(Debug, Clone)]
28pub struct Line {
29 /// 1-based line number in the source file.
30 pub number: usize,
31 pub fields: Vec<Field>,
32}
33
34impl Line {
35 /// The first field's text, if any — the record keyword for a fresh line (`Rule`,
36 /// `Zone`, `Link`), or the first column of a zone continuation line.
37 pub fn keyword(&self) -> Option<&str> {
38 self.fields.first().map(|f| f.text.as_str())
39 }
40}