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        vec![file],
51        PackageOrigin::Std,
52        std::collections::BTreeMap::new(),
53        TimingDefaults::default(),
54        None,
55    );
56    let _ = db.std_package_cache.set(package);
57    package
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63    use crate::db::parse_file;
64
65    /// Appendix A is normative and the embedded copy must parse clean under
66    /// the current parser — a parse error here means the asset and the parser
67    /// have drifted apart.
68    #[test]
69    fn std_package_parses_clean_and_exposes_ridl_std() {
70        let mut db = RidlDatabase::default();
71        let package = std_package(&mut db);
72
73        assert_eq!(package.name(&db).as_str(), "ridl.std");
74        assert_eq!(*package.origin(&db), PackageOrigin::Std);
75        assert!(
76            package.imports(&db).is_empty(),
77            "the built-in package has no manifest and no imports",
78        );
79
80        let files = package.files(&db);
81        assert_eq!(files.len(), 1, "ridl.std is a single embedded file");
82        assert_eq!(files[0].path(&db).as_str(), RIDL_STD_PATH);
83
84        let parse = parse_file(&db, files[0]);
85        assert_eq!(
86            parse.errors(),
87            &[],
88            "the embedded Appendix A source must parse without errors",
89        );
90
91        let declared = parse.syntax().text().to_string();
92        assert_eq!(declared, RIDL_STD_SOURCE, "the parse is lossless");
93        assert!(
94            RIDL_STD_SOURCE.starts_with("package ridl.std\n"),
95            "the asset starts with the `package ridl.std` declaration",
96        );
97    }
98
99    #[test]
100    fn std_package_is_memoized_per_database() {
101        let mut db = RidlDatabase::default();
102        let first = std_package(&mut db);
103        let second = std_package(&mut db);
104        assert_eq!(first, second, "the same database returns the same package");
105    }
106
107    /// The asset is the normative Appendix A, committed verbatim. Nothing
108    /// enforced that until this test: both were edited by hand in #198, and
109    /// every gate would have passed had only one been.
110    ///
111    /// This matters more since the standard package became a shipped artifact
112    /// (issue #190). An asset that has drifted from the appendix now generates
113    /// code that disagrees with the specification.
114    #[test]
115    fn the_asset_is_appendix_a_verbatim() {
116        let reference = std::fs::read_to_string(concat!(
117            env!("CARGO_MANIFEST_DIR"),
118            "/../../docs/specification/typl-language-reference.md"
119        ))
120        .expect("the typl reference is readable");
121
122        let block = reference
123            .split("```ridl\n")
124            .find(|block| block.starts_with("package ridl.std\n"))
125            .and_then(|block| block.split("\n```").next())
126            .expect("Appendix A carries a `ridl.std` fenced block");
127
128        assert_eq!(
129            format!("{block}\n"),
130            RIDL_STD_SOURCE,
131            "`crates/ridl-core/assets/ridl_std.typl` and Appendix A of \
132             `docs/specification/typl-language-reference.md` have drifted apart. \
133             They are the same normative text and are edited together.",
134        );
135    }
136}