use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};
use crate::core::ir::ApiSurface;
use crate::e2e::field_access::FieldResolver;
use crate::e2e::fixture::Assertion;
pub(crate) fn core_source(wire: bool) -> String {
crate::e2e::template_env::render(
"go/enum_collection_core.rs.jinja",
crate::alef_context! {
wire_key => wire.then_some("\"wireText\""),
},
)
}
pub(crate) fn surface(wire: bool) -> ApiSurface {
let directory = tempfile::tempdir().unwrap();
let source = directory.path().join("lib.rs");
std::fs::write(&source, core_source(wire)).unwrap();
crate::extract::extractor::extract(&[source.as_path()], "sample", "1.0.0", None).unwrap()
}
pub(crate) fn resolver(api: &ApiSurface, language: &str) -> FieldResolver {
let optional = HashSet::from(["choices[0].message.content".to_string()]);
let arrays = HashSet::from(["choices".to_string()]);
FieldResolver::new(
&HashMap::new(),
&optional,
&HashSet::from(["choices".to_string()]),
&arrays,
&HashSet::new(),
)
.with_ir_enum_map(
FieldResolver::ir_enum_fields(&api.types, &api.enums),
Some("Reply".to_string()),
)
.with_ir_result_fields(
FieldResolver::ir_result_field_facts(&api.types, language),
Some("Reply".to_string()),
)
}
pub(crate) fn assertions() -> Vec<Assertion> {
crate::e2e::codegen::php::enum_collection_test_support::assertions()
}
fn all_core_cases(wire: bool) -> Vec<serde_json::Value> {
let directory = tempfile::tempdir().unwrap();
let source = crate::e2e::template_env::render(
"go/enum_collection_core_probe.rs.jinja",
crate::alef_context! { core => core_source(wire) },
);
let path = directory.path().join("probe.rs");
let binary = directory.path().join(if cfg!(windows) { "probe.exe" } else { "probe" });
std::fs::write(&path, source).unwrap();
let compiler = tool("rustc", directory.path());
let mut command = std::process::Command::new(compiler);
command
.arg("--edition=2024")
.arg(path)
.arg("-o")
.arg(&binary)
.current_dir(directory.path());
for name in ["serde", "serde_json"] {
let library = library(name);
command.arg("--extern").arg(format!("{name}={}", library.display()));
command
.arg("-L")
.arg(format!("dependency={}", library.parent().unwrap().display()));
}
let output = command.output().unwrap();
assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr));
let output = std::process::Command::new(binary)
.current_dir(directory.path())
.output()
.unwrap();
assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr));
let cases: Vec<serde_json::Value> = serde_json::from_slice(&output.stdout).unwrap();
assert_eq!(cases.len(), 7);
cases
}
pub(crate) fn core_cases(wire: bool) -> Vec<serde_json::Value> {
let cases = all_core_cases(wire).into_iter().take(6).collect::<Vec<_>>();
assert_eq!(cases.len(), 6);
cases
}
pub(crate) fn core_collection_cases(wire: bool) -> Vec<serde_json::Value> {
let [good, _, _, _, wrong_shape, null, empty]: [serde_json::Value; 7] = all_core_cases(wire).try_into().unwrap();
vec![empty, good, wrong_shape, null]
}
pub(crate) fn tool(name: &str, directory: &Path) -> PathBuf {
which::which_in(name, std::env::var_os("PATH"), directory)
.unwrap_or_else(|error| panic!("native proof requires {name}: {error}"))
}
fn library(name: &str) -> PathBuf {
let executable = std::env::current_exe().unwrap();
std::fs::read_dir(executable.parent().unwrap())
.unwrap()
.map(|entry| entry.unwrap().path())
.filter(|path| {
path.file_name()
.unwrap()
.to_str()
.is_some_and(|file| file.starts_with(&format!("lib{name}-")) && file.ends_with(".rlib"))
})
.max_by_key(|path| path.metadata().unwrap().modified().unwrap())
.unwrap()
}
pub(crate) fn verify_cases(
cases: Vec<serde_json::Value>,
markers: [&str; 6],
mut run: impl FnMut(&serde_json::Value) -> std::process::Output,
) {
assert_eq!(cases.len(), 6);
let mut checked = 0;
for (index, (case, marker)) in cases.iter().zip(markers).enumerate() {
let output = run(case);
let evidence = format!(
"{}{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
assert_eq!(output.status.success(), index == 0, "case {index}: {evidence}");
assert!(evidence.contains(marker), "case {index} expected {marker}: {evidence}");
checked += 1;
}
assert_eq!(checked, 6);
}
pub(crate) fn call_surface(wire: bool) -> ApiSurface {
let directory = tempfile::tempdir().unwrap();
let path = directory.path().join("lib.rs");
std::fs::write(
&path,
core_source(wire) + "\npub fn fetch_reply() -> Reply { unimplemented!() }\n",
)
.unwrap();
let api = crate::extract::extractor::extract(&[path.as_path()], "sample", "1.0.0", None).unwrap();
assert_eq!(api.functions.len(), 1);
api
}
pub(crate) fn generate_collection_call(
generator: &dyn crate::e2e::codegen::E2eCodegen,
api: &ApiSurface,
) -> Vec<crate::core::backend::GeneratedFile> {
let assertions = assertions();
assert_eq!(assertions.len(), 3);
generate_call_with_assertions(generator, api, assertions)
}
fn collection_call_config() -> crate::e2e::config::E2eConfig {
crate::e2e::config::E2eConfig {
call: crate::e2e::config::CallConfig {
function: "fetch_reply".to_string(),
module: "example.com/sample".to_string(),
returns_result: true,
overrides: HashMap::from([(
"c".to_string(),
crate::e2e::config::CallOverride {
function: Some("sample_fetch_reply".to_string()),
..Default::default()
},
)]),
..Default::default()
},
..Default::default()
}
}
pub(crate) fn generate_call_with_assertions(
generator: &dyn crate::e2e::codegen::E2eCodegen,
api: &ApiSurface,
assertions: Vec<Assertion>,
) -> Vec<crate::core::backend::GeneratedFile> {
use crate::e2e::fixture::{Fixture, FixtureGroup};
assert!(!assertions.is_empty());
let e2e = collection_call_config();
let groups = [FixtureGroup {
category: "payload".to_string(),
fixtures: vec![Fixture {
id: "nested_collection".to_string(),
assertions,
..Default::default()
}],
}];
generator
.generate(
&groups,
&e2e,
&crate::core::config::ResolvedCrateConfig {
name: "sample".to_string(),
..Default::default()
},
&api.types,
&api.enums,
&api.functions,
&api.errors,
)
.unwrap()
}