rename crates/ to engrams/; add el-compiler el package with bootstrap artifact

- crates/ → engrams/ (Rust engrams live here)
- el-compiler/ added: el self-hosting compiler as an el package
  - src/{compiler,lexer,parser,codegen}.el
  - bootstrap/el-compiler.elc (114KB, Rust-compiled seed)
- el.toml Cargo.toml workspace paths updated
- neuron-rs cross-repo path deps fixed (were pointing to products/ instead of foundation/)
This commit is contained in:
Will Anderson
2026-04-29 03:27:32 -05:00
parent 19ed2721ee
commit a42429012e
120 changed files with 3836 additions and 64 deletions
+39
View File
@@ -0,0 +1,39 @@
//! Manifest error types.
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ManifestError {
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("toml parse error: {0}")]
Toml(#[from] toml::de::Error),
#[error("semver parse error for '{field}': {source}")]
Semver {
field: String,
#[source]
source: semver::Error,
},
#[error("missing required field: {0}")]
MissingField(String),
#[error("invalid value for '{field}': {reason}")]
InvalidValue { field: String, reason: String },
#[error("el.toml not found (searched from {0})")]
NotFound(String),
#[error("invalid cross target '{0}': use x86_64-linux, aarch64-linux, x86_64-macos, aarch64-macos, wasm32")]
InvalidCrossTarget(String),
#[error("invalid build target '{0}': use debug, release, prod")]
InvalidBuildTarget(String),
#[error("invalid seal key source '{0}': use env:VAR, file:path, or literal")]
InvalidSealKeySource(String),
}
pub type ManifestResult<T> = Result<T, ManifestError>;
+28
View File
@@ -0,0 +1,28 @@
//! el-manifest — `el.toml` project manifest parser.
//!
//! Every Engram project has an `el.toml` at its root. This crate defines the
//! manifest data model and parses it from TOML text.
//!
//! # Quick start
//! ```rust
//! use el_manifest::Manifest;
//!
//! let toml = r#"
//! [package]
//! name = "my-service"
//! version = "0.1.0"
//! edition = "2026"
//! "#;
//! let manifest = Manifest::parse(toml).unwrap();
//! assert_eq!(manifest.package.name, "my-service");
//! ```
mod error;
mod manifest;
mod parse;
pub use error::{ManifestError, ManifestResult};
pub use manifest::{
BuildConfig, BuildTarget, CrossConfig, CrossTarget, Dependency, Manifest, NativeTarget,
PackageInfo, SealKeySource,
};
+378
View File
@@ -0,0 +1,378 @@
//! Core data model for the `el.toml` manifest.
use std::collections::HashMap;
use std::path::PathBuf;
use semver::{Version, VersionReq};
use serde::{Deserialize, Serialize};
// ── Package info ──────────────────────────────────────────────────────────────
/// Metadata about the package itself (`[package]` section).
#[derive(Debug, Clone, PartialEq)]
pub struct PackageInfo {
pub name: String,
pub version: Version,
pub description: Option<String>,
pub authors: Vec<String>,
pub license: Option<String>,
/// Language edition, e.g. "2026".
pub edition: String,
}
// ── Dependencies ──────────────────────────────────────────────────────────────
/// A single dependency specifier.
#[derive(Debug, Clone, PartialEq)]
pub enum Dependency {
/// A bare semver version requirement string (`"1.2"`, `"^0.8.1"`).
VersionReq(VersionReq),
/// A path-local dependency (`{ path = "../some-local" }`).
Path(PathBuf),
/// A registry package with an explicit registry URL.
Registry {
version: VersionReq,
registry: String,
},
}
impl Dependency {
/// Returns the version requirement if this is a registry / version dep.
pub fn version_req(&self) -> Option<&VersionReq> {
match self {
Dependency::VersionReq(req) => Some(req),
Dependency::Registry { version, .. } => Some(version),
Dependency::Path(_) => None,
}
}
/// Returns the local path if this is a path dependency.
pub fn local_path(&self) -> Option<&PathBuf> {
match self {
Dependency::Path(p) => Some(p),
_ => None,
}
}
}
// ── Build config ──────────────────────────────────────────────────────────────
/// The three compilation targets supported by the Engram toolchain.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum BuildTarget {
Debug,
Release,
Prod,
}
impl BuildTarget {
pub fn as_str(&self) -> &'static str {
match self {
BuildTarget::Debug => "debug",
BuildTarget::Release => "release",
BuildTarget::Prod => "prod",
}
}
}
impl std::fmt::Display for BuildTarget {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl std::str::FromStr for BuildTarget {
type Err = crate::ManifestError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"debug" => Ok(BuildTarget::Debug),
"release" => Ok(BuildTarget::Release),
"prod" => Ok(BuildTarget::Prod),
other => Err(crate::ManifestError::InvalidBuildTarget(other.to_string())),
}
}
}
/// Where to read the sealing key from.
#[derive(Debug, Clone, PartialEq)]
pub enum SealKeySource {
/// `env:VAR_NAME` — read from an environment variable at build time.
EnvVar(String),
/// `file:path/to/key` — read raw bytes from a file.
File(PathBuf),
/// A literal key value — for development/testing only.
Literal(String),
}
impl SealKeySource {
/// Parse from the `el.toml` string representation.
pub fn parse(s: &str) -> Result<Self, crate::ManifestError> {
if let Some(var) = s.strip_prefix("env:") {
Ok(SealKeySource::EnvVar(var.to_string()))
} else if let Some(path) = s.strip_prefix("file:") {
Ok(SealKeySource::File(PathBuf::from(path)))
} else if s.is_empty() {
Err(crate::ManifestError::InvalidSealKeySource(s.to_string()))
} else {
Ok(SealKeySource::Literal(s.to_string()))
}
}
/// Resolve the key bytes at runtime.
pub fn resolve(&self) -> Result<Vec<u8>, crate::ManifestError> {
match self {
SealKeySource::EnvVar(var) => {
std::env::var(var)
.map(|v| v.into_bytes())
.map_err(|_| crate::ManifestError::InvalidValue {
field: format!("env:{var}"),
reason: "environment variable not set".to_string(),
})
}
SealKeySource::File(path) => {
std::fs::read(path).map_err(crate::ManifestError::Io)
}
SealKeySource::Literal(s) => Ok(s.as_bytes().to_vec()),
}
}
}
/// The `[build]` section.
#[derive(Debug, Clone, PartialEq)]
pub struct BuildConfig {
pub target: BuildTarget,
pub entry: PathBuf,
pub output: PathBuf,
pub seal_key: Option<SealKeySource>,
}
impl Default for BuildConfig {
fn default() -> Self {
Self {
target: BuildTarget::Debug,
entry: PathBuf::from("src/main.el"),
output: PathBuf::from("dist/"),
seal_key: None,
}
}
}
// ── Cross-compilation ─────────────────────────────────────────────────────────
/// A native target triple for cross-compilation.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum CrossTarget {
#[serde(rename = "x86_64-linux")]
X86_64Linux,
#[serde(rename = "aarch64-linux")]
Aarch64Linux,
#[serde(rename = "x86_64-macos")]
X86_64Macos,
#[serde(rename = "aarch64-macos")]
Aarch64Macos,
#[serde(rename = "wasm32")]
Wasm32,
}
impl CrossTarget {
/// Parse from the string used in `el.toml`.
pub fn parse(s: &str) -> Result<Self, crate::ManifestError> {
match s {
"x86_64-linux" => Ok(CrossTarget::X86_64Linux),
"aarch64-linux" => Ok(CrossTarget::Aarch64Linux),
"x86_64-macos" => Ok(CrossTarget::X86_64Macos),
"aarch64-macos" => Ok(CrossTarget::Aarch64Macos),
"wasm32" => Ok(CrossTarget::Wasm32),
other => Err(crate::ManifestError::InvalidCrossTarget(other.to_string())),
}
}
/// The canonical Rust/LLVM target triple for this target.
pub fn triple(&self) -> &'static str {
match self {
CrossTarget::X86_64Linux => "x86_64-unknown-linux-gnu",
CrossTarget::Aarch64Linux => "aarch64-unknown-linux-gnu",
CrossTarget::X86_64Macos => "x86_64-apple-darwin",
CrossTarget::Aarch64Macos => "aarch64-apple-darwin",
CrossTarget::Wasm32 => "wasm32-unknown-unknown",
}
}
/// File extension for compiled artifacts on this target.
pub fn artifact_extension(&self) -> &'static str {
match self {
CrossTarget::Wasm32 => ".wasm",
_ => "",
}
}
/// The string as it appears in `el.toml`.
pub fn as_str(&self) -> &'static str {
match self {
CrossTarget::X86_64Linux => "x86_64-linux",
CrossTarget::Aarch64Linux => "aarch64-linux",
CrossTarget::X86_64Macos => "x86_64-macos",
CrossTarget::Aarch64Macos => "aarch64-macos",
CrossTarget::Wasm32 => "wasm32",
}
}
}
impl std::fmt::Display for CrossTarget {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
/// The `[cross]` section.
#[derive(Debug, Clone, PartialEq, Default)]
pub struct CrossConfig {
pub targets: Vec<CrossTarget>,
}
// ── Native target (compiler-level) ────────────────────────────────────────────
/// Compiler-level native target — includes `Host` for the current machine.
///
/// This is stored in the sealed artifact header so the runtime knows which
/// native code generation backend to use.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum NativeTarget {
#[serde(rename = "x86_64-linux")]
X86_64Linux,
#[serde(rename = "aarch64-linux")]
Aarch64Linux,
#[serde(rename = "x86_64-macos")]
X86_64Macos,
#[serde(rename = "aarch64-macos")]
Aarch64Macos,
#[serde(rename = "wasm32")]
Wasm32,
/// The host machine — resolved at runtime.
#[serde(rename = "host")]
Host,
}
impl NativeTarget {
/// The canonical LLVM triple for this target.
///
/// For `Host`, returns the compile-time host triple using
/// `std::env::consts`.
pub fn triple(&self) -> &str {
match self {
NativeTarget::X86_64Linux => "x86_64-unknown-linux-gnu",
NativeTarget::Aarch64Linux => "aarch64-unknown-linux-gnu",
NativeTarget::X86_64Macos => "x86_64-apple-darwin",
NativeTarget::Aarch64Macos => "aarch64-apple-darwin",
NativeTarget::Wasm32 => "wasm32-unknown-unknown",
NativeTarget::Host => {
// This is a static string that depends on the compile-time target.
// We detect at compile time via cfg! macros.
host_triple()
}
}
}
/// Output file extension for artifacts on this target.
pub fn artifact_extension(&self) -> &'static str {
match self {
NativeTarget::Wasm32 => ".wasm",
_ => {
if cfg!(target_os = "windows") {
".exe"
} else {
""
}
}
}
}
/// Convert a [`CrossTarget`] to the equivalent [`NativeTarget`].
pub fn from_cross(c: &CrossTarget) -> Self {
match c {
CrossTarget::X86_64Linux => NativeTarget::X86_64Linux,
CrossTarget::Aarch64Linux => NativeTarget::Aarch64Linux,
CrossTarget::X86_64Macos => NativeTarget::X86_64Macos,
CrossTarget::Aarch64Macos => NativeTarget::Aarch64Macos,
CrossTarget::Wasm32 => NativeTarget::Wasm32,
}
}
}
fn host_triple() -> &'static str {
// Determine at compile time — these cfg values are set by rustc.
if cfg!(all(target_arch = "x86_64", target_os = "linux")) {
"x86_64-unknown-linux-gnu"
} else if cfg!(all(target_arch = "aarch64", target_os = "linux")) {
"aarch64-unknown-linux-gnu"
} else if cfg!(all(target_arch = "x86_64", target_os = "macos")) {
"x86_64-apple-darwin"
} else if cfg!(all(target_arch = "aarch64", target_os = "macos")) {
"aarch64-apple-darwin"
} else if cfg!(target_arch = "wasm32") {
"wasm32-unknown-unknown"
} else {
"unknown-unknown-unknown"
}
}
impl std::fmt::Display for NativeTarget {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.triple())
}
}
// ── Top-level manifest ────────────────────────────────────────────────────────
/// The parsed contents of an `el.toml` project manifest.
#[derive(Debug, Clone)]
pub struct Manifest {
pub package: PackageInfo,
pub dependencies: HashMap<String, Dependency>,
pub dev_dependencies: HashMap<String, Dependency>,
pub build: BuildConfig,
pub cross: CrossConfig,
/// Plugin name → version requirement string.
pub plugins: HashMap<String, String>,
}
impl Manifest {
/// Parse a manifest from a TOML string.
pub fn parse(s: &str) -> crate::ManifestResult<Self> {
crate::parse::parse_manifest(s)
}
/// Parse a manifest from a file on disk.
pub fn from_file(path: &std::path::Path) -> crate::ManifestResult<Self> {
let text = std::fs::read_to_string(path).map_err(crate::ManifestError::Io)?;
Self::parse(&text)
}
/// Walk up the directory tree from `from` until an `el.toml` is found.
///
/// Returns the path to the manifest file (not its directory).
pub fn find_manifest(from: &std::path::Path) -> crate::ManifestResult<PathBuf> {
let mut dir = if from.is_file() {
from.parent().unwrap_or(from).to_path_buf()
} else {
from.to_path_buf()
};
loop {
let candidate = dir.join("el.toml");
if candidate.exists() {
return Ok(candidate);
}
match dir.parent() {
Some(parent) => dir = parent.to_path_buf(),
None => {
return Err(crate::ManifestError::NotFound(from.display().to_string()))
}
}
}
}
}
+447
View File
@@ -0,0 +1,447 @@
//! TOML parsing for `el.toml` manifests.
//!
//! We define a set of intermediate `Raw*` structs that map 1:1 to the TOML
//! schema, then convert them into the strongly-typed `Manifest` model.
use std::collections::HashMap;
use std::path::PathBuf;
use serde::Deserialize;
use toml::Value as TomlValue;
use crate::error::{ManifestError, ManifestResult};
use crate::manifest::{
BuildConfig, BuildTarget, CrossConfig, CrossTarget, Dependency, Manifest, PackageInfo,
SealKeySource,
};
// ── Raw TOML structs ──────────────────────────────────────────────────────────
#[derive(Debug, Deserialize)]
struct RawManifest {
package: RawPackage,
#[serde(default)]
dependencies: HashMap<String, TomlValue>,
#[serde(rename = "dev-dependencies", default)]
dev_dependencies: HashMap<String, TomlValue>,
#[serde(default)]
build: RawBuild,
#[serde(default)]
cross: RawCross,
#[serde(default)]
plugins: HashMap<String, String>,
}
#[derive(Debug, Deserialize)]
struct RawPackage {
name: String,
version: String,
description: Option<String>,
#[serde(default)]
authors: Vec<String>,
license: Option<String>,
#[serde(default = "default_edition")]
edition: String,
}
fn default_edition() -> String {
"2026".to_string()
}
#[derive(Debug, Deserialize)]
#[serde(default)]
struct RawBuild {
target: String,
entry: String,
output: String,
seal_key: Option<String>,
}
impl Default for RawBuild {
fn default() -> Self {
Self {
target: "debug".to_string(),
entry: "src/main.el".to_string(),
output: "dist/".to_string(),
seal_key: None,
}
}
}
#[derive(Debug, Deserialize, Default)]
struct RawCross {
#[serde(default)]
targets: Vec<String>,
}
// ── Entry point ───────────────────────────────────────────────────────────────
/// Parse a raw TOML string into a [`Manifest`].
pub(crate) fn parse_manifest(s: &str) -> ManifestResult<Manifest> {
let raw: RawManifest = toml::from_str(s)?;
convert(raw)
}
fn convert(raw: RawManifest) -> ManifestResult<Manifest> {
let package = convert_package(raw.package)?;
let dependencies = convert_deps(&raw.dependencies, "dependencies")?;
let dev_dependencies = convert_deps(&raw.dev_dependencies, "dev-dependencies")?;
let build = convert_build(raw.build)?;
let cross = convert_cross(raw.cross)?;
Ok(Manifest {
package,
dependencies,
dev_dependencies,
build,
cross,
plugins: raw.plugins,
})
}
fn convert_package(raw: RawPackage) -> ManifestResult<PackageInfo> {
let version = semver::Version::parse(&raw.version).map_err(|e| ManifestError::Semver {
field: "package.version".to_string(),
source: e,
})?;
Ok(PackageInfo {
name: raw.name,
version,
description: raw.description,
authors: raw.authors,
license: raw.license,
edition: raw.edition,
})
}
fn convert_deps(
raw: &HashMap<String, TomlValue>,
section: &str,
) -> ManifestResult<HashMap<String, Dependency>> {
let mut out = HashMap::new();
for (name, value) in raw {
let dep = convert_single_dep(name, value, section)?;
out.insert(name.clone(), dep);
}
Ok(out)
}
fn convert_single_dep(
name: &str,
value: &TomlValue,
section: &str,
) -> ManifestResult<Dependency> {
match value {
// String form: `name = "1.2"` or `name = "^0.8.1"`
TomlValue::String(s) => {
let req =
semver::VersionReq::parse(s).map_err(|e| ManifestError::Semver {
field: format!("{section}.{name}"),
source: e,
})?;
Ok(Dependency::VersionReq(req))
}
// Table form: `name = { path = "..." }` or `name = { version = "...", registry = "..." }`
TomlValue::Table(t) => {
if let Some(TomlValue::String(path)) = t.get("path") {
return Ok(Dependency::Path(PathBuf::from(path)));
}
if let Some(TomlValue::String(ver_str)) = t.get("version") {
let version =
semver::VersionReq::parse(ver_str).map_err(|e| ManifestError::Semver {
field: format!("{section}.{name}.version"),
source: e,
})?;
let registry = t
.get("registry")
.and_then(|v| v.as_str())
.unwrap_or("https://packages.neurontechnologies.ai")
.to_string();
return Ok(Dependency::Registry { version, registry });
}
Err(ManifestError::InvalidValue {
field: format!("{section}.{name}"),
reason: "dependency table must have 'path' or 'version' key".to_string(),
})
}
other => Err(ManifestError::InvalidValue {
field: format!("{section}.{name}"),
reason: format!("expected string or table, got {}", other.type_str()),
}),
}
}
fn convert_build(raw: RawBuild) -> ManifestResult<BuildConfig> {
let target = raw.target.parse::<BuildTarget>()?;
let seal_key = raw
.seal_key
.map(|s| SealKeySource::parse(&s))
.transpose()?;
Ok(BuildConfig {
target,
entry: PathBuf::from(raw.entry),
output: PathBuf::from(raw.output),
seal_key,
})
}
fn convert_cross(raw: RawCross) -> ManifestResult<CrossConfig> {
let targets = raw
.targets
.iter()
.map(|s| CrossTarget::parse(s))
.collect::<ManifestResult<Vec<_>>>()?;
Ok(CrossConfig { targets })
}
// ── Tests ─────────────────────────────────────────────────────────────────────
#[cfg(test)]
mod tests {
use super::*;
use crate::manifest::{BuildTarget, CrossTarget, Dependency, NativeTarget, SealKeySource};
fn full_toml() -> &'static str {
r#"
[package]
name = "my-service"
version = "0.1.0"
description = "What this does"
authors = ["Will Anderson <will@neurontechnologies.ai>"]
license = "MIT"
edition = "2026"
[dependencies]
engram-http = "1.2"
engram-auth = "0.8.1"
some-local = { path = "../some-local" }
[dev-dependencies]
el-test = "0.1"
[build]
target = "prod"
entry = "src/main.el"
output = "dist/"
seal_key = "env:ENGRAM_SEAL_KEY"
[cross]
targets = ["x86_64-linux", "aarch64-linux", "x86_64-macos", "aarch64-macos", "wasm32"]
[plugins]
el-fmt = "1.0"
el-doc = "0.3"
"#
}
#[test]
fn test_parse_full_manifest() {
let m = parse_manifest(full_toml()).unwrap();
assert_eq!(m.package.name, "my-service");
assert_eq!(m.package.version.to_string(), "0.1.0");
assert_eq!(m.package.edition, "2026");
assert_eq!(m.package.license.as_deref(), Some("MIT"));
assert_eq!(m.package.authors, vec!["Will Anderson <will@neurontechnologies.ai>"]);
}
#[test]
fn test_parse_dependencies() {
let m = parse_manifest(full_toml()).unwrap();
// Version requirement dep
assert!(m.dependencies.contains_key("engram-http"));
match &m.dependencies["engram-http"] {
Dependency::VersionReq(req) => {
assert!(req.to_string().contains("1"));
}
other => panic!("expected VersionReq, got {other:?}"),
}
// Path dep
assert!(m.dependencies.contains_key("some-local"));
match &m.dependencies["some-local"] {
Dependency::Path(p) => assert_eq!(p.to_str().unwrap(), "../some-local"),
other => panic!("expected Path, got {other:?}"),
}
// Dev dep
assert!(m.dev_dependencies.contains_key("el-test"));
}
#[test]
fn test_parse_build_config() {
let m = parse_manifest(full_toml()).unwrap();
assert_eq!(m.build.target, BuildTarget::Prod);
assert_eq!(m.build.entry.to_str().unwrap(), "src/main.el");
assert_eq!(m.build.output.to_str().unwrap(), "dist/");
match &m.build.seal_key {
Some(SealKeySource::EnvVar(v)) => assert_eq!(v, "ENGRAM_SEAL_KEY"),
other => panic!("expected EnvVar seal_key, got {other:?}"),
}
}
#[test]
fn test_parse_cross_targets() {
let m = parse_manifest(full_toml()).unwrap();
assert_eq!(m.cross.targets.len(), 5);
assert!(m.cross.targets.contains(&CrossTarget::X86_64Linux));
assert!(m.cross.targets.contains(&CrossTarget::Aarch64Linux));
assert!(m.cross.targets.contains(&CrossTarget::X86_64Macos));
assert!(m.cross.targets.contains(&CrossTarget::Aarch64Macos));
assert!(m.cross.targets.contains(&CrossTarget::Wasm32));
}
#[test]
fn test_parse_plugins() {
let m = parse_manifest(full_toml()).unwrap();
assert_eq!(m.plugins.get("el-fmt").map(|s| s.as_str()), Some("1.0"));
assert_eq!(m.plugins.get("el-doc").map(|s| s.as_str()), Some("0.3"));
}
#[test]
fn test_minimal_manifest() {
let toml = r#"
[package]
name = "hello"
version = "0.1.0"
"#;
let m = parse_manifest(toml).unwrap();
assert_eq!(m.package.name, "hello");
assert_eq!(m.package.edition, "2026"); // default
assert_eq!(m.build.target, BuildTarget::Debug); // default
assert_eq!(m.build.entry.to_str().unwrap(), "src/main.el"); // default
assert!(m.dependencies.is_empty());
assert!(m.cross.targets.is_empty());
}
#[test]
fn test_invalid_semver_rejected() {
let toml = r#"
[package]
name = "bad"
version = "not-a-version"
"#;
let err = parse_manifest(toml).unwrap_err();
let msg = err.to_string();
assert!(msg.contains("semver"), "expected semver error, got: {msg}");
}
#[test]
fn test_invalid_build_target_rejected() {
let toml = r#"
[package]
name = "bad"
version = "0.1.0"
[build]
target = "turbo"
"#;
let err = parse_manifest(toml).unwrap_err();
let msg = err.to_string();
assert!(msg.contains("turbo"), "expected target name in error: {msg}");
}
#[test]
fn test_invalid_cross_target_rejected() {
let toml = r#"
[package]
name = "bad"
version = "0.1.0"
[cross]
targets = ["x86_64-linux", "solaris-sparc"]
"#;
let err = parse_manifest(toml).unwrap_err();
let msg = err.to_string();
assert!(msg.contains("solaris-sparc"), "expected target name in error: {msg}");
}
#[test]
fn test_seal_key_env_parse() {
let src = SealKeySource::parse("env:MY_KEY").unwrap();
assert_eq!(src, SealKeySource::EnvVar("MY_KEY".to_string()));
}
#[test]
fn test_seal_key_file_parse() {
let src = SealKeySource::parse("file:/tmp/key.bin").unwrap();
assert_eq!(src, SealKeySource::File(PathBuf::from("/tmp/key.bin")));
}
#[test]
fn test_seal_key_literal_parse() {
let src = SealKeySource::parse("my-literal-key").unwrap();
assert_eq!(src, SealKeySource::Literal("my-literal-key".to_string()));
}
#[test]
fn test_native_target_host_triple() {
let t = NativeTarget::Host;
// Should return a non-empty triple
assert!(!t.triple().is_empty());
}
#[test]
fn test_native_target_wasm_extension() {
let t = NativeTarget::Wasm32;
assert_eq!(t.artifact_extension(), ".wasm");
}
#[test]
fn test_cross_target_triple_roundtrip() {
let targets = [
CrossTarget::X86_64Linux,
CrossTarget::Aarch64Linux,
CrossTarget::X86_64Macos,
CrossTarget::Aarch64Macos,
CrossTarget::Wasm32,
];
for t in &targets {
// as_str → parse → same target
let s = t.as_str();
let parsed = CrossTarget::parse(s).unwrap();
assert_eq!(&parsed, t, "roundtrip failed for {s}");
}
}
#[test]
fn test_native_target_from_cross() {
let nt = NativeTarget::from_cross(&CrossTarget::Wasm32);
assert_eq!(nt, NativeTarget::Wasm32);
let nt2 = NativeTarget::from_cross(&CrossTarget::Aarch64Macos);
assert_eq!(nt2, NativeTarget::Aarch64Macos);
}
#[test]
fn test_registry_dep_table() {
let toml = r#"
[package]
name = "test"
version = "1.0.0"
[dependencies]
special-pkg = { version = "2.0", registry = "https://custom.registry.io" }
"#;
let m = parse_manifest(toml).unwrap();
match &m.dependencies["special-pkg"] {
Dependency::Registry { version, registry } => {
assert!(version.to_string().contains("2"));
assert_eq!(registry, "https://custom.registry.io");
}
other => panic!("expected Registry dep, got {other:?}"),
}
}
#[test]
fn test_dep_version_req_method() {
let d = Dependency::VersionReq(semver::VersionReq::parse("1.0").unwrap());
assert!(d.version_req().is_some());
assert!(d.local_path().is_none());
let d2 = Dependency::Path(PathBuf::from("/tmp"));
assert!(d2.local_path().is_some());
assert!(d2.version_req().is_none());
}
}