use rmp_serde::{decode, Serializer}; use serde::ser::Serialize; use std::error::Error; use std::fmt; use std::os::unix::net::UnixStream; use std::path::PathBuf; use tokio::io::{self, AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::task; use eyeballs_common::git; use eyeballs_common::git_socket; #[derive(Debug)] struct IoError { message: String, } impl IoError { fn new(message: impl Into) -> Self { Self { message: message.into(), } } } impl fmt::Display for IoError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.message) } } impl Error for IoError {} #[tokio::main] async fn main() -> Result<(), Box> { let pre = match std::env::current_exe()? .file_name() .and_then(|x| x.to_str()) { Some("pre-receive") => true, Some("post-receive") => false, _ => return Err(Box::::from("Invalid hook executable name")), }; let input = io::stdin(); let reader = BufReader::new(input); let mut lines = reader.lines(); let mut request = git_socket::GitHookRequest { pre, receive: Vec::new(), }; let repo = git::Repository::new( PathBuf::from("."), true, None::, None::, None::, ); while let Some(line) = lines.next_line().await? { let data: Vec<&str> = line.split(' ').collect(); if data.len() == 3 { let mut commiter: Option = None; if pre && data[1] != git::EMPTY { if let Ok(user) = repo.get_commiter(data[1]).await { commiter = Some(user.username); } } request.receive.push(git_socket::GitReceive { old_value: data[0].to_string(), new_value: data[1].to_string(), reference: data[2].to_string(), commiter, }) } } let socket = PathBuf::from(repo.config_get("eyeballs.socket").await?); let response = task::spawn_blocking(move || { let stream = UnixStream::connect(socket).map_err(|e| IoError::new(e.to_string()))?; let mut serializer = Serializer::new(&stream); request .serialize(&mut serializer) .map_err(|e| IoError::new(e.to_string()))?; let result: Result = decode::from_read(stream).map_err(|e| IoError::new(e.to_string())); result }) .await? .map_err(Box::::from)?; let mut output = io::stdout(); output.write_all(response.message.as_bytes()).await?; if response.ok { Ok(()) } else { Err(Box::::from("Hook failed")) } }