diesel_migration_patcher/
main.rs

1use std::collections::BTreeMap;
2use std::io::Write;
3use std::process::Stdio;
4
5use camino::Utf8PathBuf;
6use clap::Parser;
7
8#[derive(clap::Parser)]
9struct Args {
10    #[clap(long, env = "OUTPUT_FILE")]
11    output_file: Utf8PathBuf,
12
13    #[clap(long, env = "SCHEMA_FILE")]
14    schema_file: Utf8PathBuf,
15
16    #[clap(long, env = "TEMP_DIR")]
17    temp_dir: Utf8PathBuf,
18
19    #[clap(long, env = "SCHEMA_PATCH_FILE")]
20    schema_patch_file: Utf8PathBuf,
21
22    #[clap(long, env = "SCHEMA_FILE_RESULTS")]
23    schema_file_results: Utf8PathBuf,
24
25    #[clap(long, env = "PATCH_BINARY")]
26    patch_binary: Utf8PathBuf,
27
28    #[clap(long, env = "DIFF_BINARY")]
29    diff_binary: Utf8PathBuf,
30}
31
32fn main() {
33    let args = Args::parse();
34
35    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
36
37    std::fs::create_dir_all(&args.temp_dir).expect("failed to create temp dir");
38
39    let results = std::fs::read_to_string(&args.schema_file_results).expect("failed to read results");
40    let results: BTreeMap<Utf8PathBuf, String> = serde_json::from_str(&results).expect("failed to parse results");
41    let unedited_file = results.get(&args.schema_file).expect("failed to get unedited file").clone();
42
43    let unedited = args.temp_dir.join("unedited.rs");
44
45    std::fs::write(&unedited, &unedited_file).expect("failed to write unedited file");
46
47    // So the unedited file has been patched via the patch file, so we need to first reverse the patch
48    // and then generate a new diff file and output it to the output file
49    let reverse_patch = std::process::Command::new(args.patch_binary)
50        .arg("--follow-symlinks")
51        .arg("-R")
52        .arg("-i")
53        .arg(&args.schema_patch_file)
54        .arg("-o")
55        .arg("-")
56        .arg(&unedited)
57        .output()
58        .expect("failed to reverse patch");
59
60    if !reverse_patch.status.success() {
61        std::io::stderr()
62            .write_all(&reverse_patch.stderr)
63            .expect("failed to write stderr");
64        std::io::stderr()
65            .write_all(&reverse_patch.stdout)
66            .expect("failed to write stdout");
67        panic!("failed to reverse patch");
68    }
69
70    let reverse_patch_output = String::from_utf8(reverse_patch.stdout).expect("failed to read reverse patch output");
71
72    let mut diff = std::process::Command::new(args.diff_binary)
73        .arg("-u")
74        .arg(format!("--label=a/{}", args.schema_file))
75        .arg(format!("--label=b/{}", args.schema_file.with_extension("unpatched.rs")))
76        .arg("-")
77        .arg(&args.schema_file)
78        .stdin(Stdio::piped())
79        .stdout(Stdio::piped())
80        .stderr(Stdio::piped())
81        .spawn()
82        .expect("failed to generate diff");
83
84    let mut stdin = diff.stdin.take().unwrap();
85    std::thread::spawn(move || {
86        stdin
87            .write_all(reverse_patch_output.as_bytes())
88            .expect("failed to write stdin");
89    });
90
91    let output = diff.wait_with_output().expect("failed to wait for output");
92    if output.status.code().is_none_or(|c| c != 0 && c != 1) {
93        std::io::stderr().write_all(&output.stderr).expect("failed to write stderr");
94        std::io::stderr().write_all(&output.stdout).expect("failed to write stdout");
95        panic!("failed to generate diff");
96    }
97
98    let content = String::from_utf8(output.stdout).expect("failed to convert stdout to utf8");
99    let json = serde_json::to_string(&BTreeMap::from_iter([(args.schema_patch_file, content)]))
100        .expect("failed to serialize output");
101    std::fs::write(args.output_file, json).expect("failed to write diff");
102}