Files
core/test/rust/native/dependency/fixture.rs
T
2026-11-03 03:32:07 +00:00

161 lines
4.9 KiB
Rust

use super::super::Directory;
use dever::compiler::project::Project;
use serde::Serialize;
use sha2::{Digest, Sha256};
use std::collections::BTreeMap;
use std::io::{Cursor, Write};
use std::path::Path;
#[derive(Clone, Serialize)]
pub(super) struct Entry {
pub name: String,
pub version: String,
pub sha256: String,
pub bytes: u64,
pub dependencies: Vec<Dependency>,
pub manifest_sha256: String,
}
#[derive(Clone, Serialize)]
pub(super) struct Dependency {
pub name: String,
pub version: String,
}
pub(super) struct Fixture {
pub directory: Directory,
pub entries: Vec<Entry>,
pub assets: BTreeMap<String, Vec<u8>>,
}
impl Fixture {
pub fn new(roots: &[&str]) -> Self {
let directory = Directory::new();
write(
&directory.0,
"config/setting.json",
serde_json::to_vec(&serde_json::json!({
"package":{"registry":"https://packages.example.test","use":roots}
}))
.unwrap(),
);
Self {
directory,
entries: Vec::new(),
assets: BTreeMap::new(),
}
}
pub fn package(&mut self, name: &str, dependencies: &[(&str, &str)], files: &[(&str, &str)]) {
let manifest = serde_json::to_vec(&serde_json::json!({
"format":"dever-package-v1", "name":name, "version":"1.0.0",
"dependencies":dependencies.iter().copied().collect::<BTreeMap<_, _>>(), "lib":[]
}))
.unwrap();
let mut zip = zip::ZipWriter::new(Cursor::new(Vec::new()));
let options = zip::write::SimpleFileOptions::default()
.compression_method(zip::CompressionMethod::Deflated);
zip.start_file("dever-package.json", options).unwrap();
zip.write_all(&manifest).unwrap();
for &(path, text) in files {
zip.start_file(path, options).unwrap();
zip.write_all(text.as_bytes()).unwrap();
}
let bytes = zip.finish().unwrap().into_inner();
let entry = Entry {
name: name.into(),
version: "1.0.0".into(),
sha256: digest(&bytes),
bytes: bytes.len() as u64,
dependencies: dependencies
.iter()
.map(|(name, _)| Dependency {
name: (*name).into(),
version: "1.0.0".into(),
})
.collect(),
manifest_sha256: digest(&manifest),
};
self.assets.insert(entry.sha256.clone(), bytes);
self.entries.push(entry);
self.lock();
}
pub fn lock(&mut self) {
#[derive(Serialize)]
struct Lock<'entries> {
format: &'static str,
toolchain: &'static str,
libs: Vec<()>,
workers: Vec<()>,
#[serde(skip_serializing_if = "Vec::is_empty")]
packages: &'entries Vec<Entry>,
}
self.entries
.sort_by(|left, right| left.name.cmp(&right.name));
for entry in &mut self.entries {
entry
.dependencies
.sort_by(|left, right| left.name.cmp(&right.name));
}
let lock = Lock {
format: "dever-lock-v6",
toolchain: env!("CARGO_PKG_VERSION"),
libs: vec![],
workers: vec![],
packages: &self.entries,
};
write(
&self.directory.0,
"dever.lock",
serde_json::to_string_pretty(&lock).unwrap() + "\n",
);
}
pub fn mutate_archive(&mut self, name: &str, change: impl FnOnce(&mut Vec<u8>)) {
let entry = self
.entries
.iter_mut()
.find(|entry| entry.name == name)
.unwrap();
let mut bytes = self.assets.remove(&entry.sha256).unwrap();
change(&mut bytes);
entry.sha256 = digest(&bytes);
entry.bytes = bytes.len() as u64;
self.assets.insert(entry.sha256.clone(), bytes);
self.lock();
}
pub fn load(&self) -> Result<Project, String> {
Project::load_with_artifacts(&self.directory.0, |sha256, _| {
self.assets
.get(sha256)
.cloned()
.ok_or_else(|| "locked asset is missing".into())
})
}
pub fn reject(&self, needle: &str) {
let error = self.load().err().expect("invalid dependencies accepted");
assert!(error.contains(needle), "expected {needle}: {error}");
}
}
pub(super) fn digest(bytes: &[u8]) -> String {
format!("{:x}", Sha256::digest(bytes))
}
pub(super) fn write(root: &Path, relative: &str, contents: impl AsRef<[u8]>) {
let path = root.join(relative);
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(path, contents).unwrap();
}
pub(super) fn app(expression: &str) -> String {
format!("value(input: Int) (answer: Int) {{ answer = {expression} }}\n")
}
pub(super) fn command() -> String {
"cmd value = app.value\n".to_owned()
}