b638abf1e2
Two unrelated hygiene iters bundled because they shipped together in one autonomous-while-Boss-away batch: - iter rustdoc-sweep: cleared all 23 `cargo doc --workspace --no-deps` warnings (17 in ailang-check + 4 in ailang-core + 2 in ailang-surface). Two warning classes: pub-doc-comment links to pub(crate)/private items (15 hits — replaced with plain backtick-code-spans), and unresolved/ambiguous links (8 hits — fully-qualified, disambiguated to fn-form, or escaped). No behaviour change; tests 562 → 562. - iter drift-test-narrowing: design_schema_drift.rs now scans §"Data model" only via new helper data_model_section(), instead of full DESIGN.md. Closes the audit-form-a-precursor [high] "anchor-elsewhere-passes-silently" failure mode. All 38 anchors verified pre-edit to live in §"Data model" so the tightening doesn't regress to red. New pin data_model_section_is_bounded guards the extractor against silent regression. Tests 562 → 563.
72 lines
2.8 KiB
Rust
72 lines
2.8 KiB
Rust
//! Extension-dispatching loader for AILang source files.
|
|
//!
|
|
//! AILang has two file forms today: Form A (`.ail`, the LLM
|
|
//! authoring surface) and Form B (`.ail.json`, the canonical
|
|
//! JSON-AST). These loaders accept both: for `.ail` they read the
|
|
//! source text and parse it via [`crate::parse()`]; for `.ail.json`
|
|
//! they delegate to [`ailang_core::load_module`].
|
|
//!
|
|
//! These functions live in `ailang-surface` rather than
|
|
//! `ailang-core` because `ailang-core` cannot import `ailang-surface`
|
|
//! without creating a circular crate dependency. The cross-crate
|
|
//! injection point is [`ailang_core::workspace::load_workspace_with`],
|
|
//! introduced in iter ext-cli.1.
|
|
|
|
use ailang_core::ast::Module;
|
|
use ailang_core::workspace::{load_workspace_with, Workspace, WorkspaceLoadError};
|
|
use std::path::Path;
|
|
|
|
fn is_ail_source(path: &Path) -> bool {
|
|
path.extension().and_then(|s| s.to_str()) == Some("ail")
|
|
}
|
|
|
|
/// Load a single module from either `.ail` or `.ail.json`.
|
|
///
|
|
/// Dispatches on file extension:
|
|
///
|
|
/// - `.ail` — read source text, parse via [`crate::parse()`], return
|
|
/// the in-memory [`Module`].
|
|
/// - anything else — delegate to [`ailang_core::load_module`] and
|
|
/// convert its `Error` into the corresponding
|
|
/// [`WorkspaceLoadError`] variant.
|
|
///
|
|
/// Errors are returned as [`WorkspaceLoadError`] so that single-
|
|
/// module callers and workspace callers can share the same error
|
|
/// type (and `workspace_error_to_diagnostic` in the binary can
|
|
/// route them through one channel).
|
|
pub fn load_module(path: &Path) -> Result<Module, WorkspaceLoadError> {
|
|
if is_ail_source(path) {
|
|
let src = std::fs::read_to_string(path).map_err(|e| WorkspaceLoadError::Io {
|
|
path: path.to_path_buf(),
|
|
source: e,
|
|
})?;
|
|
crate::parse(&src).map_err(|e| WorkspaceLoadError::SurfaceParse {
|
|
path: path.to_path_buf(),
|
|
message: format!("{e}"),
|
|
})
|
|
} else {
|
|
match ailang_core::load_module(path) {
|
|
Ok(m) => Ok(m),
|
|
Err(ailang_core::Error::Io(e)) => Err(WorkspaceLoadError::Io {
|
|
path: path.to_path_buf(),
|
|
source: e,
|
|
}),
|
|
Err(e) => Err(WorkspaceLoadError::Schema {
|
|
path: path.to_path_buf(),
|
|
source: e,
|
|
}),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Load a workspace from an entry path, accepting either `.ail` or
|
|
/// `.ail.json` for the entry and for every transitive import.
|
|
///
|
|
/// Import resolution prefers a sibling `<module>.ail` over
|
|
/// `<module>.ail.json`; this is the new precedence rule baked into
|
|
/// `core::workspace::visit`. Mixed-extension workspaces (entry is
|
|
/// `.ail`, some imports are `.ail.json`) are valid.
|
|
pub fn load_workspace(entry: &Path) -> Result<Workspace, WorkspaceLoadError> {
|
|
load_workspace_with(entry, load_module)
|
|
}
|