1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#[macro_use]
extern crate proc_macro_hack;
extern crate libflate;
#[macro_use]
extern crate syn;
use libflate::gzip::Encoder;
use std::io::Write;
use syn::punctuated::Punctuated;
use syn::synom::Parser;
use syn::{Expr, Lit};
fn repo_tarball(filter: &Vec<String>) -> Vec<u8> {
let toplevel = std::process::Command::new("git")
.arg("rev-parse")
.arg("--show-toplevel")
.output()
.expect("could not get top level directory");
let toplevel_dir = String::from_utf8(toplevel.stdout).unwrap();
let toplevel_dir = toplevel_dir.trim();
let mut archive = std::process::Command::new("git");
archive
.current_dir(toplevel_dir)
.arg("archive")
.arg("HEAD")
.arg("--");
for f in filter {
archive.arg(f);
}
let output = archive
.output()
.expect("could not run 'git archive HEAD' for repo_tarball");
if !output.status.success() {
panic!("[include-repo]: error running git-archive: Exit {}: {}", output.status, String::from_utf8_lossy(&output.stderr));
}
output.stdout
}
fn parse_input(input: &str) -> (String, Vec<String>) {
let parts = input.splitn(2, ",").collect::<Vec<_>>();
let const_name = parts[0];
if parts.len() == 1 {
return (const_name.to_string(), Vec::new());
}
let git_filter = parts[1];
let parser = Punctuated::<Expr, Token![,]>::parse_terminated;
let args = parser.parse_str(git_filter).unwrap();
let filters: Vec<String> = args
.iter()
.map(|item| match item {
Expr::Lit(lit) => match lit.lit {
Lit::Str(ref s) => return s.value(),
_ => {
panic!("[include-repo] git filters must be string literals");
}
},
_ => {
panic!("[include-repo] git filters must be string literals");
}
}).collect();
return (const_name.to_string(), filters);
}
proc_macro_item_impl! {
pub fn include_repo_tarball(input: &str) -> String {
let (const_name, filters) = parse_input(input);
let tar = repo_tarball(&filters);
format!("const {}: [u8; {}] = [{}];", const_name, tar.len(), tar.iter().map(|b| b.to_string()).collect::<Vec<_>>().join(", "))
}
pub fn include_repo_targz(input: &str) -> String {
let (const_name, filters) = parse_input(input);
let tar = repo_tarball(&filters);
let mut encoder = Encoder::new(Vec::new()).unwrap();
encoder.write_all(&tar).unwrap();
let targz = encoder.finish().into_result().unwrap();
format!("const {}: [u8; {}] = [{}];", const_name, targz.len(), targz.iter().map(|b| b.to_string()).collect::<Vec<_>>().join(", "))
}
}