use ts_rs::TS;
use crate::{
ChangeKind, CloudCreateSandboxRequest, CloudErrorBody, CloudErrorDetails, CloudMessageResponse,
CloudPaginated, CloudSandbox, CloudSandboxStatus, ConfigPlannedChange, DiskImageFormat, EnvVar,
HandoffInit, HostPermissions, LogSource, ModificationConflict, ModificationDisposition,
ModificationPolicy, ModificationWarning, MountOptions, NamedVolumeCreate, NamedVolumeMode,
NetworkSpec, OciRootfsSource, Patch, PlannedChange, PortProtocol, PublishedPortSpec,
PullPolicy, ResourceConvergenceState, ResourceKind, ResourceResizeStatus, Rlimit,
RlimitResource, RootfsSource, SandboxLogLevel, SandboxModificationPatch,
SandboxModificationPlan, SandboxPolicy, SandboxResources, SandboxRuntimeOptions, SandboxSpec,
SecretChangeKind, SecretModificationPatch, SecretPlannedChange, SecretSource, SecurityProfile,
SnapshotDestination, SnapshotSpec, StatVirtualization, VolumeKind, VolumeMount, VolumeSpec,
};
const HEADER: &str = "// @generated by microsandbox-types. Do not edit by hand.\n\n";
pub fn render_bindings() -> String {
let mut output = String::from(HEADER);
output.push_str(
&declarations()
.into_iter()
.map(export_declaration)
.map(trim_line_endings)
.collect::<Vec<_>>()
.join("\n\n"),
);
output.push('\n');
output
}
pub fn declarations() -> Vec<String> {
let cfg = ts_rs::Config::new().with_large_int("number");
vec![
serde_json::Value::decl(&cfg),
DiskImageFormat::decl(&cfg),
OciRootfsSource::decl(&cfg),
RootfsSource::decl(&cfg),
PullPolicy::decl(&cfg),
StatVirtualization::decl(&cfg),
HostPermissions::decl(&cfg),
SecurityProfile::decl(&cfg),
MountOptions::decl(&cfg),
VolumeKind::decl(&cfg),
VolumeSpec::decl(&cfg),
NamedVolumeMode::decl(&cfg),
NamedVolumeCreate::decl(&cfg),
VolumeMount::decl(&cfg),
Patch::decl(&cfg),
NetworkSpec::decl(&cfg),
PublishedPortSpec::decl(&cfg),
PortProtocol::decl(&cfg),
HandoffInit::decl(&cfg),
SandboxPolicy::decl(&cfg),
SnapshotDestination::decl(&cfg),
SnapshotSpec::decl(&cfg),
SandboxSpec::decl(&cfg),
SandboxResources::decl(&cfg),
SandboxRuntimeOptions::decl(&cfg),
EnvVar::decl(&cfg),
SandboxLogLevel::decl(&cfg),
RlimitResource::decl(&cfg),
Rlimit::decl(&cfg),
LogSource::decl(&cfg),
SandboxModificationPatch::decl(&cfg),
ModificationPolicy::decl(&cfg),
SecretModificationPatch::decl(&cfg),
SecretSource::decl(&cfg),
SandboxModificationPlan::decl(&cfg),
PlannedChange::decl(&cfg),
ConfigPlannedChange::decl(&cfg),
SecretPlannedChange::decl(&cfg),
ChangeKind::decl(&cfg),
SecretChangeKind::decl(&cfg),
ModificationDisposition::decl(&cfg),
ModificationConflict::decl(&cfg),
ModificationWarning::decl(&cfg),
ResourceKind::decl(&cfg),
ResourceConvergenceState::decl(&cfg),
ResourceResizeStatus::decl(&cfg),
CloudCreateSandboxRequest::decl(&cfg),
CloudSandbox::decl(&cfg),
CloudSandboxStatus::decl(&cfg),
CloudPaginated::<CloudSandbox>::decl(&cfg),
CloudMessageResponse::decl(&cfg),
CloudErrorBody::decl(&cfg),
CloudErrorDetails::decl(&cfg),
]
}
fn export_declaration(declaration: String) -> String {
if declaration.starts_with("export ") {
return declaration;
}
for keyword in ["type ", "interface "] {
if let Some(rest) = declaration.strip_prefix(keyword) {
return format!("export {keyword}{rest}");
}
}
declaration
}
fn trim_line_endings(declaration: String) -> String {
declaration
.lines()
.map(str::trim_end)
.collect::<Vec<_>>()
.join("\n")
}
#[cfg(test)]
mod tests {
use std::fs;
use std::path::PathBuf;
use super::*;
#[test]
fn checked_in_bindings_match_generated_output() {
let generated = render_bindings();
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let package_root = manifest_dir
.parent()
.expect("microsandbox-types rust crate should live under <package>/rust");
let bindings_path = package_root.join("typescript/src/index.ts");
let bindings = fs::read_to_string(&bindings_path).unwrap_or_else(|err| {
panic!("failed to read {}: {err}", bindings_path.display());
});
assert_eq!(bindings, generated, "{} is stale", bindings_path.display());
}
#[test]
fn ts_rs_renders_cloud_contract_declarations() {
let declarations = declarations();
assert_eq!(declarations.len(), 53);
assert!(
declarations
.iter()
.any(|decl| decl.contains("RootfsSource"))
);
assert!(declarations.iter().any(|decl| decl.contains("SandboxSpec")));
assert!(declarations.iter().any(|decl| decl.contains("Rlimit")));
assert!(
declarations
.iter()
.any(|decl| decl.contains("CloudCreateSandboxRequest"))
);
assert!(
declarations
.iter()
.any(|decl| decl.contains("CloudSandboxStatus"))
);
}
}