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