| 6d3eefbc Andy McCall ↶ |
1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
use super::*; |
| 8 |
use std::process::{Command, Stdio}; |
| 9 |
|
| 10 |
|
| 11 |
pub fn json_str(s: &str) -> String { |
| 12 |
let mut out = String::with_capacity(s.len() + 2); |
| 13 |
out.push('"'); |
| 14 |
for c in s.chars() { |
| 15 |
match c { |
| 16 |
'"' => out.push_str("\\\""), |
| 17 |
'\\' => out.push_str("\\\\"), |
| 18 |
'\n' => out.push_str("\\n"), |
| 19 |
'\r' => out.push_str("\\r"), |
| 20 |
'\t' => out.push_str("\\t"), |
| 21 |
c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)), |
| 22 |
c => out.push(c), |
| 23 |
} |
| 24 |
} |
| 25 |
out.push('"'); |
| 26 |
out |
| 27 |
} |
| 28 |
|
| 29 |
pub fn json_array<I: IntoIterator<Item = String>>(items: I) -> String { |
| 30 |
format!("[{}]", items.into_iter().collect::<Vec<_>>().join(",")) |
| 31 |
} |
| 32 |
|
| 33 |
|
| 34 |
#[derive(Debug, Clone, PartialEq, Eq)] |
| 35 |
pub struct HookOutcome { |
| 36 |
pub ran: bool, |
| 37 |
pub stdout: String, |
| 38 |
} |
| 39 |
|
| 40 |
impl Repo { |
| 41 |
fn hook_path(&self, event: &str) -> PathBuf { |
| 42 |
self.oxy.join("hooks").join(event) |
| 43 |
} |
| 44 |
|
| 45 |
|
| 46 |
|
| 47 |
pub fn run_hook(&self, event: &str, input: &str) -> Result<HookOutcome> { |
| 48 |
let path = self.hook_path(event); |
| 49 |
if !path.is_file() { |
| 75167672 Andy McCall |
50 |
return Ok(HookOutcome { |
| 51 |
ran: false, |
| 52 |
stdout: String::new(), |
| 53 |
}); |
| 6d3eefbc Andy McCall ↶ |
54 |
} |
| 55 |
let mut child = Command::new(&path) |
| 56 |
.current_dir(&self.root) |
| 57 |
.env("OXY_EVENT", event) |
| 58 |
.env("OXY_ROOT", &self.root) |
| 59 |
.stdin(Stdio::piped()) |
| 60 |
.stdout(Stdio::piped()) |
| 61 |
.stderr(Stdio::piped()) |
| 62 |
.spawn() |
| 63 |
.map_err(|e| RepoError::Msg(format!("running hook {event}: {e}")))?; |
| 64 |
if let Some(mut stdin) = child.stdin.take() { |
| 65 |
let _ = stdin.write_all(input.as_bytes()); |
| 66 |
} |
| 67 |
let out = child.wait_with_output()?; |
| 68 |
let stdout = String::from_utf8_lossy(&out.stdout).to_string(); |
| 69 |
if !out.status.success() { |
| 70 |
let stderr = String::from_utf8_lossy(&out.stderr).trim().to_string(); |
| 75167672 Andy McCall |
71 |
return fail(format!( |
| 72 |
"hook {event} refused: {}", |
| 73 |
if stderr.is_empty() { |
| 74 |
"(no message)".to_string() |
| 75 |
} else { |
| 76 |
stderr |
| 77 |
} |
| 78 |
)); |
| 6d3eefbc Andy McCall ↶ |
79 |
} |
| 80 |
Ok(HookOutcome { ran: true, stdout }) |
| 81 |
} |
| 82 |
|
| 83 |
|
| 75167672 Andy McCall |
84 |
pub(crate) fn semantic_deps( |
| 85 |
&self, |
| 86 |
change_id: &ChangeId, |
| 87 |
touched: &[String], |
| 88 |
candidates: &[(ChangeId, ChangeHash, Vec<String>)], |
| 89 |
) -> Result<Vec<ChangeHash>> { |
| 6d3eefbc Andy McCall ↶ |
90 |
if !self.config.semantic_deps { |
| 91 |
return Ok(vec![]); |
| 92 |
} |
| 93 |
let input = format!( |
| 94 |
"{{\"change_id\":{},\"touched_paths\":{},\"candidate_deps\":{}}}", |
| 95 |
json_str(&change_id.to_hex()), |
| 96 |
json_array(touched.iter().map(|p| json_str(p))), |
| 75167672 Andy McCall |
97 |
json_array(candidates.iter().map(|(id, h, paths)| format!( |
| 98 |
"{{\"change_id\":{},\"hash\":{},\"paths\":{}}}", |
| 99 |
json_str(&id.to_hex()), |
| 100 |
json_str(&h.to_hex()), |
| 101 |
json_array(paths.iter().map(|p| json_str(p))) |
| 102 |
))) |
| 6d3eefbc Andy McCall ↶ |
103 |
); |
| 104 |
let out = self.run_hook("semantic-deps", &input)?; |
| 105 |
if !out.ran { |
| 106 |
return Ok(vec![]); |
| 107 |
} |
| 108 |
|
| 75167672 Andy McCall |
109 |
let body = out |
| 110 |
.stdout |
| 111 |
.split_once("\"deps\"") |
| 112 |
.map(|(_, rest)| rest.to_string()) |
| 113 |
.unwrap_or_default(); |
| 6d3eefbc Andy McCall ↶ |
114 |
let mut deps = Vec::new(); |
| 115 |
let bytes = body.as_bytes(); |
| 116 |
let mut i = 0; |
| 117 |
while i + 64 <= bytes.len() { |
| 75167672 Andy McCall |
118 |
if bytes[i..i + 64].iter().all(|b| b.is_ascii_hexdigit()) |
| 119 |
&& (i == 0 || !bytes[i - 1].is_ascii_hexdigit()) |
| 120 |
&& (i + 64 == bytes.len() || !bytes[i + 64].is_ascii_hexdigit()) |
| 121 |
{ |
| 6d3eefbc Andy McCall ↶ |
122 |
if let Some(h) = ObjectRef::from_hex(&body[i..i + 64]) { |
| 123 |
deps.push(h); |
| 124 |
} |
| 125 |
i += 64; |
| 126 |
} else { |
| 127 |
i += 1; |
| 128 |
} |
| 129 |
} |
| 130 |
Ok(deps) |
| 131 |
} |
| 132 |
} |