use std::fs;
use std::path::{Path, PathBuf};
use manabrew_protocol::deck_dto::Deck;
use manabrew_protocol::display::DisplayEvent;
use manabrew_protocol::prompts::{PromptInput, PromptOutput};
use manabrew_protocol::protocol::{ResumeRoomRequest, PROTOCOL_VERSION};
use manabrew_protocol::transport::{
AgentPrompt, ClientToServerMessage, DirectiveInput, ProtocolError, StateUpdate,
};
use ts_rs::TS;
const HEADER: &str =
"// AUTO-GENERATED by `cargo run -p manabrew-protocol --bin gen-protocol`. Do not edit.\n";
const MARK: &str = "generated by";
fn prune(dir: &Path) {
let Ok(entries) = fs::read_dir(dir) else {
return;
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
prune(&path);
} else if path.extension().is_some_and(|e| e == "ts")
&& fs::read_to_string(&path)
.map(|c| c.to_lowercase().contains(MARK))
.unwrap_or(false)
{
let _ = fs::remove_file(&path);
}
}
}
fn ts_modules(dir: &Path) -> Vec<String> {
let mut names = Vec::new();
if let Ok(entries) = fs::read_dir(dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.extension().is_some_and(|e| e == "ts") {
if let Some(stem) = path.file_stem().and_then(|s| s.to_str()) {
if stem != "index" {
names.push(stem.to_string());
}
}
}
}
}
names.sort();
names
}
fn main() {
let out = PathBuf::from(
std::env::args()
.nth(1)
.unwrap_or_else(|| "bindings".to_string()),
);
prune(&out);
PromptInput::export_all_to(&out).expect("export PromptInput");
PromptOutput::export_all_to(&out).expect("export PromptOutput");
AgentPrompt::export_all_to(&out).expect("export AgentPrompt");
StateUpdate::export_all_to(&out).expect("export StateUpdate");
DirectiveInput::export_all_to(&out).expect("export DirectiveInput");
ClientToServerMessage::export_all_to(&out).expect("export ClientToServerMessage");
ProtocolError::export_all_to(&out).expect("export ProtocolError");
DisplayEvent::export_all_to(&out).expect("export DisplayEvent");
Deck::export_all_to(&out).expect("export Deck");
ResumeRoomRequest::export_all_to(&out).expect("export ResumeRoomRequest");
let prompts_dir = out.join("prompts");
let mut prompts_index = String::from(HEADER);
for module in ts_modules(&prompts_dir) {
prompts_index.push_str(&format!("export * from \"./{module}\";\n"));
}
prompts_index.push_str("\nimport type { PromptInput } from \"./promptInput\";\n");
prompts_index.push_str("export type PromptType = PromptInput[\"type\"];\n");
fs::write(prompts_dir.join("index.ts"), prompts_index).expect("write prompts/index.ts");
let transport_dir = out.join("transport");
fs::write(transport_dir.join("envelope.ts"), ENVELOPE).expect("write transport/envelope.ts");
fs::write(
transport_dir.join("index.ts"),
format!("{HEADER}export * from \"./messages\";\nexport * from \"./envelope\";\n"),
)
.expect("write transport/index.ts");
fs::write(
out.join("version.ts"),
format!(
"{HEADER}export const VERSION = \"{}\";\nexport const PROTOCOL_VERSION = {PROTOCOL_VERSION};\n",
env!("CARGO_PKG_VERSION")
),
)
.expect("write version.ts");
fs::write(
out.join("index.ts"),
format!(
"{HEADER}export * from \"./prompts\";\nexport * from \"./transport\";\nexport type * from \"./display\";\nexport type * from \"./game\";\nexport type * from \"./deck\";\nexport type * from \"./lobby\";\nexport * from \"./version\";\n"
),
)
.expect("write index.ts");
}
const ENVELOPE: &str = r#"// AUTO-GENERATED by `cargo run -p manabrew-protocol --bin gen-protocol`. Do not edit.
import type { PromptInput } from "../prompts";
export interface PromptRequestEnvelope {
promptId?: string;
decidingPlayerId?: string;
sourceCardId?: string;
}
export type PromptRequest<TInput extends { type: string }> = PromptRequestEnvelope & {
input: TInput;
};
type DistributeRequest<TInput extends { type: string }> = TInput extends unknown
? PromptRequest<TInput>
: never;
export type Prompt = DistributeRequest<PromptInput>;
"#;