From a1a185370da27f2cc3df84d3a8d7141f9ce7db16 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 24 Oct 2021 22:40:08 +0200 Subject: Split defintions used by both runner and executor into separate crate Also get rid of the Runner trait - different runner implementations do not make sense with our current design. --- crates/common/Cargo.toml | 12 ++++ crates/common/src/error.rs | 119 +++++++++++++++++++++++++++++++++++++++ crates/common/src/lib.rs | 3 + crates/common/src/string_hash.rs | 53 +++++++++++++++++ crates/common/src/types.rs | 43 ++++++++++++++ 5 files changed, 230 insertions(+) create mode 100644 crates/common/Cargo.toml create mode 100644 crates/common/src/error.rs create mode 100644 crates/common/src/lib.rs create mode 100644 crates/common/src/string_hash.rs create mode 100644 crates/common/src/types.rs (limited to 'crates') diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml new file mode 100644 index 0000000..2d5b70d --- /dev/null +++ b/crates/common/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "rebel-common" +version = "0.1.0" +authors = ["Matthias Schiffer "] +license = "MIT" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +hex = { version = "0.4.3", features = ["std", "serde"] } +serde = { version = "1", features = ["derive"] } diff --git a/crates/common/src/error.rs b/crates/common/src/error.rs new file mode 100644 index 0000000..ba25af4 --- /dev/null +++ b/crates/common/src/error.rs @@ -0,0 +1,119 @@ +//! Serializable errors with context + +use std::{error::Error as _, fmt::Display, io, result}; + +use serde::{Deserialize, Serialize}; + +pub trait Contextualizable: Sized { + type Output; + + fn with_context C>(self, f: F) -> Self::Output; + + fn context(self, c: C) -> Self::Output { + self.with_context(|| c) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub enum ErrorCause { + Code(i32), + String(String), +} + +impl Display for ErrorCause { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ErrorCause::Code(code) => io::Error::from_raw_os_error(*code).fmt(f), + ErrorCause::String(string) => f.write_str(string), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct Error { + pub cause: ErrorCause, + pub context: Vec, +} + +impl Error { + pub fn new(cause: D) -> Self { + Error { + cause: ErrorCause::String(cause.to_string()), + context: Vec::new(), + } + } + + pub fn from_io(err: &io::Error) -> Self { + if let Some(source) = err + .source() + .and_then(|source| source.downcast_ref::()) + { + return Error::from_io(source).context(err.to_string()); + } + + let cause = match err.raw_os_error() { + Some(code) => ErrorCause::Code(code), + None => ErrorCause::String(err.to_string()), + }; + Error { + cause, + context: vec![], + } + } +} + +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("Error: ")?; + + let mut it = self.context.iter().rev(); + if let Some(ctx) = it.next() { + write!(f, "{}\n\nCaused by:\n ", ctx)?; + + for ctx in it { + write!(f, "{}\n ", ctx)?; + } + } + + self.cause.fmt(f) + } +} + +impl Contextualizable for Error { + type Output = Error; + fn with_context C>(self, f: F) -> Self::Output { + let Error { cause, mut context } = self; + context.push(f().to_string()); + Error { cause, context } + } +} + +impl From for Error +where + E: Into, +{ + fn from(err: E) -> Self { + Error::from_io(&err.into()) + } +} + +pub type Result = result::Result; + +impl Contextualizable for result::Result +where + E: Into, +{ + type Output = Result; + + fn with_context C>(self, f: F) -> Self::Output { + self.map_err(|err| err.into().with_context(f)) + } +} + +impl Contextualizable for Option { + type Output = Result; + + fn with_context C>(self, f: F) -> Self::Output { + self.ok_or_else(|| Error::new(f())) + } +} diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs new file mode 100644 index 0000000..8d630dd --- /dev/null +++ b/crates/common/src/lib.rs @@ -0,0 +1,3 @@ +pub mod error; +pub mod string_hash; +pub mod types; diff --git a/crates/common/src/string_hash.rs b/crates/common/src/string_hash.rs new file mode 100644 index 0000000..a2b00db --- /dev/null +++ b/crates/common/src/string_hash.rs @@ -0,0 +1,53 @@ +use std::fmt::Display; + +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct StringHash(pub [u8; 32]); + +impl Display for StringHash { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&hex::encode(self.0)) + } +} + +impl std::fmt::Debug for StringHash { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "\"{}\"", self) + } +} + +impl Serialize for StringHash { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + hex::serialize(self.0, serializer) + } +} +impl<'de> Deserialize<'de> for StringHash { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Ok(StringHash(hex::deserialize(deserializer)?)) + } +} + +macro_rules! stringhash_newtype { + ($id:ident) => { + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] + pub struct $id(pub StringHash); + + impl Display for $id { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } + } + }; +} + +stringhash_newtype!(InputHash); +stringhash_newtype!(DependencyHash); +stringhash_newtype!(LayerHash); +stringhash_newtype!(ArchiveHash); diff --git a/crates/common/src/types.rs b/crates/common/src/types.rs new file mode 100644 index 0000000..51f9209 --- /dev/null +++ b/crates/common/src/types.rs @@ -0,0 +1,43 @@ +use std::{ + collections::{HashMap, HashSet}, + fmt::Display, +}; + +use serde::{Deserialize, Serialize}; + +use crate::string_hash::*; + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)] +pub struct TaskID { + pub recipe: String, + pub task: String, +} + +impl Display for TaskID { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}:{}", self.recipe, self.task) + } +} + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)] +#[serde(rename_all = "snake_case")] +pub enum Dependency { + Fetch { name: String, sha256: StringHash }, + Task { output: ArchiveHash, path: String }, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Task { + pub label: String, + pub command: String, + pub inherit: Vec, + pub depends: HashSet, + pub outputs: HashMap, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct TaskOutput { + pub input_hash: InputHash, + pub layer: Option, + pub outputs: HashMap, +} -- cgit v1.2.3