1use crate::imports::*;
6use crate::utils::*;
7use crate::html::*;
8
9type E = anyhow::Error;
10
11pub trait Component : Debug {
12 fn default_desc(&self) -> Html;
18 #[throws(E)]
19 fn bundle(&self, outbasepath : &String) -> Option<String>;
20}
21
22#[derive(Debug)]
23pub struct FileComponent(pub String);
24impl Component for FileComponent {
25 fn default_desc(&self) -> Html {
26 Html(format!(r#"file {}"#, Html::from_literal(&self.0).0))
27 }
28 #[throws(E)]
29 fn bundle(&self, outbasepath : &String) -> Option<String> {
30 let src = &self.0;
31 fs::copy(src, outbasepath)?;
32 None
33 }
34}
35
36#[derive(Debug)]
37pub struct DirectoryComponent(Box<dyn Component>);
38impl DirectoryComponent {
39 #[throws(E)]
40 pub fn new(src : String) -> DirectoryComponent {
41 let is_git = match fs::metadata(src.to_owned() + "/.git") {
42 Ok(_) => true,
43 Err(e) if e.kind() == NotFound => false,
44 Err(e) => throw!(e),
45 };
46
47 DirectoryComponent(if is_git {
48 Box::new( GitComponent(src) )
49 } else {
50 Box::new( RawDirectoryComponent(src) )
51 })
52 }
53}
54impl Component for DirectoryComponent {
55 fn default_desc(&self) -> Html { self.0.default_desc() }
56 #[throws(E)]
57 fn bundle(&self, outbasepath : &String) -> Option<String> {
58 self.0.bundle(outbasepath)?
59 }
60}
61
62#[derive(Debug)]
63pub struct RawDirectoryComponent(pub String);
64impl Component for RawDirectoryComponent {
65 fn default_desc(&self) -> Html {
66 Html(format!(r#"directory {}"#, Html::from_literal(&self.0).0))
67 }
68 #[throws(E)]
69 fn bundle(&self, outbasepath : &String) -> Option<String> {
70 let src = &self.0;
71 let mut split = src.rsplitn(2,'/');
72 let srcleaf = split.next().unwrap();
73 let dir = split.next().ok_or_else(||
74 anyhow!("trying to bundle directory with no parent in pathname"))?;
75 let outfile = outbasepath.to_owned() + ".tar";
76 let stdout = File::create(&outfile)?;
77 let mut cmd = tar_command();
78 cmd
79 .current_dir(&dir)
80 .stdout(stdout)
81 .args(&["-cf","-","--",srcleaf]);
82 run_cmd(cmd).cxm(||format!("tar for raw directory {:?}", &dir))?;
83 Some(".tar".to_owned())
84 }
85}
86
87#[derive(Debug)]
88pub struct GitComponent(pub String);
89impl Component for GitComponent {
90 fn default_desc(&self) -> Html {
91 Html(format!(r#"git working directory {}"#,
92 Html::from_literal(&self.0).0))
93 }
94 #[throws(E)]
95 fn bundle(&self, outbasepath : &String) -> Option<String> {
96 let src = &self.0;
97 let gitclone = r#"#!/bin/bash
98 set -e
99 set -o pipefail
100
101 src="$1"; shift
102 dest="$1"; shift
103
104 rm -rf "$dest"
105 mkdir "$dest"
106 cd "$dest"
107 desta=$(pwd)
108
109 git init -q
110
111 git fetch -q "$src" HEAD
112 git update-ref refs/heads/master FETCH_HEAD
113
114 (
115 set -e
116 cd "$src"
117 git ls-files --exclude-standard -oc -z
118 ) | (
119 cd "$src"
120 cpio -p0 -dum --no-preserve-owner --quiet "$desta"
121 )
122
123 git reset -q
124
125 cd ..
126 "#;
127
128 let mut c = Command::new("bash");
129 c.args(&["-ec",gitclone,"x",&src,outbasepath]);
130 run_cmd(c).context("git fetch, copy etc. script")?;
131
132 let r = RawDirectoryComponent(outbasepath.clone()).bundle(outbasepath)?;
133 remove_dir_all(outbasepath)?;
134 r
135 }
136}