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