1#![feature(if_let_guard)]
10
11use clap::Parser;
12
13use std::fs::File;
14use std::io::{self, Read, Write};
15use std::path::{Path, PathBuf};
16use std::sync::OnceLock;
17
18mod comptime;
19mod consts;
20mod pages;
21mod parser;
22
23use comptime::{Link, LinkKVPair};
24use parser::Result as ParseResult;
25
26pub use pages::{Interaction, InteractionMap, Metaline, Page, PageMeta, Speaker};
28pub use parser::{DgParser, DialogueChoice, DialogueEnding, Label};
29
30pub mod prelude {
31 pub use crate::{
32 DialogueChoice, DialogueEnding, Interaction, InteractionMap, Label, Metaline, Page,
33 PageMeta, Speaker,
34 };
35}
36
37type Error = Box<dyn std::error::Error>;
38
39pub(crate) static SILENT: OnceLock<bool> = OnceLock::new();
40
41macro_rules! log {
42 ($($arg:tt)*) => {
43 if !SILENT.get().unwrap_or(&false) {
44 eprintln!($($arg)*);
45 }
46 };
47}
48
49pub fn deserialize(data: &[u8]) -> Result<InteractionMap, Error> {
50 bincode::deserialize(data).map_err(Into::into)
51}
52
53pub fn compile(entry: &str, out: &str) -> Result<(), Error> {
58 let args = Args {
59 file: Some(entry.into()),
60 output: Some(out.into()),
61 silent: true,
62 };
63
64 cli_main(args, None)
65}
66
67pub fn cli_main(args: Args, cwd: Option<&Path>) -> Result<(), Error> {
68 SILENT.set(args.silent).unwrap();
69
70 let input_stream: Box<dyn Read> = match args.file {
72 Some(ref file) => Box::new(File::open(file).unwrap()),
73 None => Box::new(io::stdin()),
74 };
75
76 let mut output_stream: Box<dyn Write> = match args.output {
77 Some(file) => Box::new(File::create(file).unwrap()),
78 None => Box::new(io::stdout()),
79 };
80
81 log!("Reading...");
82 let data = io::read_to_string(input_stream)?;
83
84 log!("Parsing...");
89 let path = cwd
90 .map(PathBuf::from)
91 .or_else(|| args.file.as_ref().map(PathBuf::from))
92 .unwrap_or_else(|| std::env::current_dir().unwrap());
93
94 let mut parser = DgParser::new(path);
95 let res = parser.parse_all(&data)?;
96
97 log!("Serializing...");
98 let res = bincode::serialize(&res)?;
99
100 log!("Writing...");
101 output_stream.write(&res)?;
102
103 log!("Done!");
104 Ok(())
105}
106
107#[derive(Parser, Debug)]
108#[command(arg_required_else_help(true))]
109#[command(author, version, about)]
110pub struct Args {
112 #[arg(short, long)]
114 output: Option<String>,
115
116 file: Option<String>,
118
119 #[arg(short, long)]
121 silent: bool,
122}