161 lines
4.9 KiB
Rust
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()
|
|
}
|