Skip to main content

ridl_core/
std_lib.rs

1//! The embedded `ridl.std` package (typl reference Appendix A, ADR-0007
2//! decision 15).
3//!
4//! The Appendix A source is committed verbatim as
5//! `crates/ridl-core/assets/ridl_std.typl` and compiled in via `include_str!`
6//! — a built-in, implicitly imported package with no filesystem or network
7//! lookup, version-locked to the compiler binary.
8
9use crate::db::{InputFile, RidlDatabase};
10use crate::manifest::TimingDefaults;
11use crate::package::{Package, PackageOrigin};
12
13/// The `ridl.std` source, verbatim from the typl reference Appendix A.
14pub const RIDL_STD_SOURCE: &str = include_str!("../assets/ridl_std.typl");
15
16/// The package name the embedded source declares.
17pub const RIDL_STD_NAME: &str = "ridl.std";
18
19/// Every package name the compiler provides itself, which a user package
20/// therefore cannot declare (TYPL-010).
21///
22/// Derived from what is built in rather than from a reserved-prefix policy:
23/// the list is exactly the set of compiler-provided packages, so a second
24/// built-in joins it here and the diagnostic follows without a rule change.
25/// Nothing else under `ridl.` is reserved.
26pub const RESERVED_PACKAGE_NAMES: &[&str] = &[RIDL_STD_NAME];
27
28/// Whether `name` is a package the compiler provides.
29pub fn is_reserved_package_name(name: &str) -> bool {
30    RESERVED_PACKAGE_NAMES.contains(&name)
31}
32
33/// The virtual path the embedded source is registered under. The `<builtin>`
34/// prefix marks it as compiler-provided, not a filesystem path.
35pub const RIDL_STD_PATH: &str = "<builtin>/ridl_std.typl";
36
37/// The built-in `ridl.std` package, created at most once per database.
38///
39/// The first call registers the embedded source as an [`InputFile`] and wraps
40/// it in a [`Package`] with [`PackageOrigin::Std`]; every later call on the
41/// same database returns that same package.
42pub fn std_package(db: &mut RidlDatabase) -> Package {
43    if let Some(&package) = db.std_package_cache.get() {
44        return package;
45    }
46    let file = InputFile::new(&*db, RIDL_STD_PATH.to_string(), RIDL_STD_SOURCE.to_string());
47    let package = Package::new(
48        &*db,
49        RIDL_STD_NAME.to_string(),
50        "ridl.std".to_string(),
51        vec![file],
52        PackageOrigin::Std,
53        std::collections::BTreeMap::new(),
54        TimingDefaults::default(),
55        None,
56    );
57    let _ = db.std_package_cache.set(package);
58    package
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64    use crate::db::parse_file;
65
66    /// Appendix A is normative and the embedded copy must parse clean under
67    /// the current parser — a parse error here means the asset and the parser
68    /// have drifted apart.
69    #[test]
70    fn std_package_parses_clean_and_exposes_ridl_std() {
71        let mut db = RidlDatabase::default();
72        let package = std_package(&mut db);
73
74        assert_eq!(package.name(&db).as_str(), "ridl.std");
75        assert_eq!(*package.origin(&db), PackageOrigin::Std);
76        assert!(
77            package.imports(&db).is_empty(),
78            "the built-in package has no manifest and no imports",
79        );
80
81        let files = package.files(&db);
82        assert_eq!(files.len(), 1, "ridl.std is a single embedded file");
83        assert_eq!(files[0].path(&db).as_str(), RIDL_STD_PATH);
84
85        let parse = parse_file(&db, files[0]);
86        assert_eq!(
87            parse.errors(),
88            &[],
89            "the embedded Appendix A source must parse without errors",
90        );
91
92        let declared = parse.syntax().text().to_string();
93        assert_eq!(declared, RIDL_STD_SOURCE, "the parse is lossless");
94        assert!(
95            RIDL_STD_SOURCE.starts_with("package ridl.std\n"),
96            "the asset starts with the `package ridl.std` declaration",
97        );
98    }
99
100    #[test]
101    fn std_package_is_memoized_per_database() {
102        let mut db = RidlDatabase::default();
103        let first = std_package(&mut db);
104        let second = std_package(&mut db);
105        assert_eq!(first, second, "the same database returns the same package");
106    }
107
108    /// The asset is the normative Appendix A, committed verbatim. Nothing
109    /// enforced that until this test: both were edited by hand in #198, and
110    /// every gate would have passed had only one been.
111    ///
112    /// This matters more since the standard package became a shipped artifact
113    /// (issue #190). An asset that has drifted from the appendix now generates
114    /// code that disagrees with the specification.
115    #[test]
116    fn the_asset_is_appendix_a_verbatim() {
117        let reference = std::fs::read_to_string(concat!(
118            env!("CARGO_MANIFEST_DIR"),
119            "/../../docs/specification/typl-language-reference.md"
120        ))
121        .expect("the typl reference is readable");
122
123        let block = reference
124            .split("```ridl\n")
125            .find(|block| block.starts_with("package ridl.std\n"))
126            .and_then(|block| block.split("\n```").next())
127            .expect("Appendix A carries a `ridl.std` fenced block");
128
129        assert_eq!(
130            format!("{block}\n"),
131            RIDL_STD_SOURCE,
132            "`crates/ridl-core/assets/ridl_std.typl` and Appendix A of \
133             `docs/specification/typl-language-reference.md` have drifted apart. \
134             They are the same normative text and are edited together.",
135        );
136    }
137}