From 39f1b8bec3891b3fcc7f60aea86e2ab2f8c3ea8a Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Mon, 3 Jan 2022 09:24:42 -0500 Subject: use real serialization --- Cargo.lock | 31 +++++++++++++++++++ Cargo.toml | 2 ++ src/env.rs | 78 +++++++++++++++++++++++++++++++++++++++++++----- src/pipeline/mod.rs | 17 +++++------ src/state/history/mod.rs | 15 +++------- 5 files changed, 114 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 776304a..90cc3c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -154,6 +154,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -493,6 +502,7 @@ version = "0.1.0" dependencies = [ "anyhow", "async-std", + "bincode", "blocking", "futures-lite", "futures-util", @@ -503,6 +513,7 @@ dependencies = [ "pest", "pest_derive", "pty-process", + "serde", "signal-hook", "signal-hook-async-std", "terminal_size", @@ -651,6 +662,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "serde" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "sha-1" version = "0.8.2" diff --git a/Cargo.toml b/Cargo.toml index e5b7df8..e02cb53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ license = "MIT" [dependencies] anyhow = "1.0.52" async-std = { version = "1.10.0", features = ["unstable"] } +bincode = "1.3.3" blocking = "1.1.0" futures-lite = "1.12.0" futures-util = "0.3.19" @@ -18,6 +19,7 @@ once_cell = "1.9.0" pest = "2.1.3" pest_derive = "2.1.0" pty-process = { version = "0.2.0", features = ["async"] } +serde = { version = "1.0.133", features = ["derive"] } signal-hook = "0.3.13" signal-hook-async-std = "0.2.1" terminal_size = "0.1.17" diff --git a/src/env.rs b/src/env.rs index 31671a3..f78b790 100644 --- a/src/env.rs +++ b/src/env.rs @@ -1,23 +1,85 @@ +use serde::Deserialize as _; use std::os::unix::process::ExitStatusExt as _; -pub struct Env { +#[derive(serde::Serialize, serde::Deserialize)] +pub enum Env { + V0(V0), +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct V0 { + pipeline: Option, + #[serde( + serialize_with = "serialize_status", + deserialize_with = "deserialize_status" + )] latest_status: async_std::process::ExitStatus, } impl Env { - pub fn new(code: i32) -> Self { - Self { - latest_status: async_std::process::ExitStatus::from_raw( - code << 8, - ), + pub fn new() -> Self { + Self::V0(V0 { + pipeline: None, + latest_status: std::process::ExitStatus::from_raw(0), + }) + } + + pub fn set_pipeline(&mut self, pipeline: String) { + match self { + Self::V0(env) => { + env.pipeline = Some(pipeline); + } } } pub fn set_status(&mut self, status: async_std::process::ExitStatus) { - self.latest_status = status; + match self { + Self::V0(env) => { + env.latest_status = status; + } + } + } + + pub fn pipeline(&self) -> Option<&str> { + match self { + Self::V0(env) => env.pipeline.as_deref(), + } } pub fn latest_status(&self) -> &async_std::process::ExitStatus { - &self.latest_status + match self { + Self::V0(env) => &env.latest_status, + } } + + pub fn as_bytes(&self) -> Vec { + bincode::serialize(self).unwrap() + } + + pub fn from_bytes(bytes: &[u8]) -> Self { + bincode::deserialize(bytes).unwrap() + } +} + +#[allow(clippy::trivially_copy_pass_by_ref)] +fn serialize_status( + status: &std::process::ExitStatus, + s: S, +) -> Result +where + S: serde::Serializer, +{ + let code: u16 = status.code().unwrap_or(0).try_into().unwrap(); + let signal: u16 = status.signal().unwrap_or(0).try_into().unwrap(); + s.serialize_u16((code << 8) | signal) +} + +fn deserialize_status<'de, D>( + d: D, +) -> Result +where + D: serde::Deserializer<'de>, +{ + let status = u16::deserialize(d)?; + Ok(std::process::ExitStatus::from_raw(i32::from(status))) } diff --git a/src/pipeline/mod.rs b/src/pipeline/mod.rs index 8fa0041..89381c6 100644 --- a/src/pipeline/mod.rs +++ b/src/pipeline/mod.rs @@ -10,8 +10,8 @@ mod command; pub use command::{Child, Command}; pub async fn run() -> anyhow::Result { - let (code, pipeline) = read_data().await?; - let env = crate::env::Env::new(code); + let env = read_data().await?; + let pipeline = crate::parse::Pipeline::parse(env.pipeline().unwrap())?; let (children, pg) = spawn_children(pipeline, &env)?; let status = wait_children(children, pg).await; if let Some(signal) = status.signal() { @@ -20,18 +20,15 @@ pub async fn run() -> anyhow::Result { Ok(status.code().unwrap()) } -async fn read_data() -> anyhow::Result<(i32, crate::parse::Pipeline)> { +async fn read_data() -> anyhow::Result { // Safety: this code is only called by crate::history::run_pipeline, which // passes data through on fd 3, and which will not spawn this process // unless the pipe was successfully opened on that fd let mut fd3 = unsafe { async_std::fs::File::from_raw_fd(3) }; - let mut be_bytes = [0; 4]; - fd3.read_exact(&mut be_bytes).await?; - let code = i32::from_be_bytes(be_bytes); - let mut pipeline = String::new(); - fd3.read_to_string(&mut pipeline).await?; - let ast = crate::parse::Pipeline::parse(&pipeline)?; - Ok((code, ast)) + let mut data = vec![]; + fd3.read_to_end(&mut data).await?; + let env = crate::env::Env::from_bytes(&data); + Ok(env) } fn spawn_children( diff --git a/src/state/history/mod.rs b/src/state/history/mod.rs index 3573a1c..430eb28 100644 --- a/src/state/history/mod.rs +++ b/src/state/history/mod.rs @@ -104,7 +104,7 @@ impl History { run_commands( ast, async_std::sync::Arc::clone(&entry), - crate::env::Env::new(0), + crate::env::Env::new(), input_r, resize_r, event_w, @@ -541,8 +541,8 @@ fn run_commands( }; for pipeline in ast.pipelines() { - let (pipeline_status, done) = - run_pipeline(pipeline, &pty, &env).await; + env.set_pipeline(pipeline.input_string().to_string()); + let (pipeline_status, done) = run_pipeline(&pty, &env).await; env.set_status(pipeline_status); if done { break; @@ -556,7 +556,6 @@ fn run_commands( } async fn run_pipeline( - pipeline: &crate::parse::Pipeline, pty: &pty::Pty, env: &crate::env::Env, ) -> (async_std::process::ExitStatus, bool) { @@ -573,13 +572,7 @@ async fn run_pipeline( nix::unistd::close(r).unwrap(); let mut w = unsafe { async_std::fs::File::from_raw_fd(w) }; - // TODO: actual serialization - w.write_all(&env.latest_status().code().unwrap_or(1).to_be_bytes()) - .await - .unwrap(); - w.write_all(pipeline.input_string().as_bytes()) - .await - .unwrap(); + w.write_all(&env.as_bytes()).await.unwrap(); drop(w); let status = child.status_no_drop().await.unwrap(); -- cgit v1.2.3-54-g00ecf