From 78d800b461db9d3aa8142c85f282e3de0ca84d02 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Mon, 19 Feb 2024 18:09:10 +0100 Subject: [PATCH 1/2] prowocessing: extract experiment into its own file --- crates/prowocessing/src/experimental.rs | 1 + .../src/experimental/enum_based.rs | 64 +++++++++++++++++ crates/prowocessing/src/lib.rs | 69 +------------------ 3 files changed, 66 insertions(+), 68 deletions(-) create mode 100644 crates/prowocessing/src/experimental.rs create mode 100644 crates/prowocessing/src/experimental/enum_based.rs diff --git a/crates/prowocessing/src/experimental.rs b/crates/prowocessing/src/experimental.rs new file mode 100644 index 0000000..090198e --- /dev/null +++ b/crates/prowocessing/src/experimental.rs @@ -0,0 +1 @@ +pub mod enum_based; diff --git a/crates/prowocessing/src/experimental/enum_based.rs b/crates/prowocessing/src/experimental/enum_based.rs new file mode 100644 index 0000000..c60f4a9 --- /dev/null +++ b/crates/prowocessing/src/experimental/enum_based.rs @@ -0,0 +1,64 @@ +pub enum Instruction { + Uppercase, + Lowercase, +} + +pub struct Pipeline { + pipeline: Vec String>, +} + +impl Pipeline { + pub fn run(&self, val: String) -> String { + let mut current = val; + + for instr in &self.pipeline { + current = instr(current); + } + + current + } +} + +pub struct PipelineBuilder { + pipeline: Vec, +} + +impl PipelineBuilder { + pub fn new() -> Self { + Self { + pipeline: Vec::new(), + } + } + + #[must_use] + pub fn insert(mut self, instr: Instruction) -> Self { + self.pipeline.push(instr); + self + } + + pub fn build(&self) -> Pipeline { + fn uppercase(v: String) -> String { + str::to_uppercase(&v) + } + fn lowercase(v: String) -> String { + str::to_lowercase(&v) + } + + let mut res = Vec::new(); + + for item in &self.pipeline { + res.push(match item { + Instruction::Uppercase => uppercase, + Instruction::Lowercase => lowercase, + }); + } + + Pipeline { pipeline: res } + } +} + +impl Default for PipelineBuilder { + fn default() -> Self { + Self::new() + } +} diff --git a/crates/prowocessing/src/lib.rs b/crates/prowocessing/src/lib.rs index 778d544..2e7b047 100644 --- a/crates/prowocessing/src/lib.rs +++ b/crates/prowocessing/src/lib.rs @@ -7,74 +7,7 @@ use experimental::enum_based::{Instruction, PipelineBuilder}; /// just some experiments, to test whether the architecture i want is even possible (or how to do it). probably temporary. /// Gonna first try string processing... -pub mod experimental { - pub mod enum_based { - pub enum Instruction { - Uppercase, - Lowercase, - } - - pub struct Pipeline { - pipeline: Vec String>, - } - - impl Pipeline { - pub fn run(&self, val: String) -> String { - let mut current = val; - - for instr in &self.pipeline { - current = instr(current); - } - - current - } - } - - pub struct PipelineBuilder { - pipeline: Vec, - } - - impl PipelineBuilder { - pub fn new() -> Self { - Self { - pipeline: Vec::new(), - } - } - - #[must_use] - pub fn insert(mut self, instr: Instruction) -> Self { - self.pipeline.push(instr); - self - } - - pub fn build(&self) -> Pipeline { - fn uppercase(v: String) -> String { - str::to_uppercase(&v) - } - fn lowercase(v: String) -> String { - str::to_lowercase(&v) - } - - let mut res = Vec::new(); - - for item in &self.pipeline { - res.push(match item { - Instruction::Uppercase => uppercase, - Instruction::Lowercase => lowercase, - }); - } - - Pipeline { pipeline: res } - } - } - - impl Default for PipelineBuilder { - fn default() -> Self { - Self::new() - } - } - } -} +pub mod experimental; #[test] fn test_enums() { From 2d537cc4eefaed0cb5d19a64dc05c1aaa2fe291f Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Mon, 19 Feb 2024 20:54:24 +0100 Subject: [PATCH 2/2] prowocessing: add trait based experiment --- crates/app/src/main.rs | 14 +- crates/prowocessing/src/experimental.rs | 2 + .../src/experimental/trait_based.rs | 339 ++++++++++++++++++ crates/prowocessing/src/lib.rs | 4 +- 4 files changed, 356 insertions(+), 3 deletions(-) create mode 100644 crates/prowocessing/src/experimental/trait_based.rs diff --git a/crates/app/src/main.rs b/crates/app/src/main.rs index 2caaa2f..2534631 100644 --- a/crates/app/src/main.rs +++ b/crates/app/src/main.rs @@ -56,17 +56,20 @@ fn main() { mod dev { use clap::Subcommand; - use prowocessing::experimental::enum_based::{Pipeline, PipelineBuilder}; + use prowocessing::experimental::trait_based::DataType; #[derive(Subcommand)] pub(crate) enum DevCommands { Enums { test_str: String }, + Add { num0: i32, num1: i32 }, } impl DevCommands { pub fn run(self) { match self { DevCommands::Enums { test_str } => { + use prowocessing::experimental::enum_based::PipelineBuilder; + let upr = PipelineBuilder::new() .insert(prowocessing::experimental::enum_based::Instruction::Uppercase) .build(); @@ -77,6 +80,15 @@ mod dev { println!("Upr: {}", upr.run(test_str.clone())); println!("Lwr: {}", lwr.run(test_str.clone())); } + DevCommands::Add { num0, num1 } => { + use prowocessing::experimental::trait_based::PipelineBuilder; + + let pipe = PipelineBuilder::new().add(1).stringify().build(); + println!( + "{:?}", + pipe.run(vec![num0.into(), num1.into()].into()).into_inner()[0] + ); + } } } } diff --git a/crates/prowocessing/src/experimental.rs b/crates/prowocessing/src/experimental.rs index 090198e..fcc0d52 100644 --- a/crates/prowocessing/src/experimental.rs +++ b/crates/prowocessing/src/experimental.rs @@ -1 +1,3 @@ pub mod enum_based; + +pub mod trait_based; diff --git a/crates/prowocessing/src/experimental/trait_based.rs b/crates/prowocessing/src/experimental/trait_based.rs new file mode 100644 index 0000000..3be8f3a --- /dev/null +++ b/crates/prowocessing/src/experimental/trait_based.rs @@ -0,0 +1,339 @@ +use self::{ + numops::{Add, Stringify, Subtract}, + strops::{Concatenate, Lower, Upper}, +}; + +trait PipelineElement { + fn runner(&self) -> fn(&Inputs) -> Outputs; + fn signature(&self) -> ElementIo; +} + +struct ElementIo { + pub inputs: Vec, + pub outputs: Vec, +} + +// TODO: +// - Bind additional inputs if instruction has more then one and is passd without any additional +// - allow binding to pointers to other pipelines? +// - allow referencing earlier data +pub struct PipelineBuilder { + elements: Vec>, +} + +pub struct Pipeline { + runners: Vec Outputs>, +} + +impl Pipeline { + pub fn run(&self, inputs: Inputs) -> Outputs { + let mut out: Outputs = inputs.into(); + + for runner in &self.runners { + out = runner(&(&out).into()); + } + + out + } +} + +impl PipelineBuilder { + pub fn new() -> Self { + Self { + elements: Vec::new(), + } + } + + fn insert(mut self, el: T) -> Self { + if let Some(previous_item) = self.elements.last() { + assert_eq!( + previous_item.signature().outputs[0], + el.signature().inputs[0] + ); + } + self.elements.push(Box::new(el)); + self + } + + #[must_use] + pub fn concatenate(self, sec: String) -> Self { + self.insert(Concatenate(sec)) + } + + #[must_use] + pub fn upper(self) -> Self { + self.insert(Upper) + } + + #[must_use] + pub fn lower(self) -> Self { + self.insert(Lower) + } + + #[must_use] + #[allow( + clippy::should_implement_trait, + reason = "is not equivalent to addition" + )] + pub fn add(self, sec: i32) -> Self { + self.insert(Add(sec)) + } + + #[must_use] + pub fn subtract(self, sec: i32) -> Self { + self.insert(Subtract(sec)) + } + + #[must_use] + pub fn stringify(self) -> Self { + self.insert(Stringify) + } + + pub fn build(&self) -> Pipeline { + let mut r = Vec::new(); + + self.elements.iter().for_each(|el| r.push(el.runner())); + + Pipeline { runners: r } + } +} + +impl Default for PipelineBuilder { + fn default() -> Self { + Self::new() + } +} + +#[derive(Clone, Copy)] +pub enum Data<'a> { + String(&'a str), + Int(i32), +} +impl Data<'_> { + pub fn to_owned_data(&self) -> OwnedData { + match self { + Data::String(s) => (*s).to_owned().into(), + Data::Int(i) => (*i).into(), + } + } +} +impl<'a> From<&'a str> for Data<'a> { + fn from(value: &'a str) -> Self { + Self::String(value) + } +} +impl From for Data<'_> { + fn from(value: i32) -> Self { + Self::Int(value) + } +} +impl<'a> From<&'a OwnedData> for Data<'a> { + fn from(value: &'a OwnedData) -> Self { + match value { + OwnedData::String(s) => Data::String(s), + OwnedData::Int(i) => Data::Int(*i), + } + } +} + +#[derive(Clone, Debug)] +pub enum OwnedData { + String(String), + Int(i32), +} +impl From for OwnedData { + fn from(value: String) -> Self { + Self::String(value) + } +} +impl From for OwnedData { + fn from(value: i32) -> Self { + Self::Int(value) + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum DataType { + String, + Int, +} + +pub struct Inputs<'a>(Vec>); +impl<'a> Inputs<'a> { + fn inner(&self) -> Vec> { + self.0.clone() + } +} +impl<'a> From>> for Inputs<'a> { + fn from(value: Vec>) -> Self { + Self(value) + } +} +impl<'a, T: Into>> From for Inputs<'a> { + fn from(value: T) -> Self { + Self(vec![value.into()]) + } +} +impl<'a> From<&'a Outputs> for Inputs<'a> { + fn from(value: &'a Outputs) -> Self { + Self(value.0.iter().map(std::convert::Into::into).collect()) + } +} + +pub struct Outputs(Vec); +impl Outputs { + pub fn into_inner(self) -> Vec { + self.0 + } +} +impl From> for Outputs { + fn from(value: Vec) -> Self { + Self(value) + } +} +impl> From for Outputs { + fn from(value: T) -> Self { + Self(vec![value.into()]) + } +} +impl From> for Outputs { + fn from(value: Inputs) -> Self { + Self( + value + .0 + .into_iter() + .map(|i: Data<'_>| Data::to_owned_data(&i)) + .collect(), + ) + } +} + +mod strops { + use super::{Data, DataType, ElementIo, Inputs, Outputs, PipelineElement}; + + pub struct Concatenate(pub String); + impl PipelineElement for Concatenate { + fn runner(&self) -> fn(&Inputs) -> Outputs { + |input| { + if let [Data::String(s0), Data::String(s1), ..] = input.inner()[..] { + format!("{s0}{s1}").into() + } else { + panic!("Invalid data passed") + } + } + } + + fn signature(&self) -> ElementIo { + ElementIo { + inputs: vec![DataType::String, DataType::String], + outputs: vec![DataType::String], + } + } + } + + pub struct Upper; + impl PipelineElement for Upper { + fn runner(&self) -> fn(&Inputs) -> Outputs { + |input| { + if let [Data::String(s), ..] = input.inner()[..] { + s.to_uppercase().into() + } else { + panic!("Invalid data passed") + } + } + } + + fn signature(&self) -> ElementIo { + ElementIo { + inputs: vec![DataType::String], + outputs: vec![DataType::String], + } + } + } + + pub struct Lower; + impl PipelineElement for Lower { + fn runner(&self) -> fn(&Inputs) -> Outputs { + |input| { + if let [Data::String(s), ..] = input.inner()[..] { + s.to_lowercase().into() + } else { + panic!("Invalid data passed") + } + } + } + + fn signature(&self) -> ElementIo { + ElementIo { + inputs: vec![DataType::String], + outputs: vec![DataType::String], + } + } + } +} + +mod numops { + use core::panic; + + use super::{Data, DataType, ElementIo, Inputs, Outputs, PipelineElement}; + + pub struct Add(pub i32); + impl PipelineElement for Add { + fn runner(&self) -> fn(&Inputs) -> Outputs { + |input| { + if let [Data::Int(i0), Data::Int(i1), ..] = input.inner()[..] { + (i0 + i1).into() + } else { + panic!("Invalid data passed") + } + } + } + + fn signature(&self) -> ElementIo { + ElementIo { + inputs: vec![DataType::Int, DataType::Int], + outputs: vec![DataType::Int], + } + } + } + + pub struct Subtract(pub i32); + impl PipelineElement for Subtract { + fn runner(&self) -> fn(&Inputs) -> Outputs { + |input| { + if let [Data::Int(i0), Data::Int(i1), ..] = input.inner()[..] { + (i0 + i1).into() + } else { + panic!("Invalid data passed") + } + } + } + + fn signature(&self) -> ElementIo { + ElementIo { + inputs: vec![DataType::Int, DataType::Int], + outputs: vec![DataType::Int], + } + } + } + + pub struct Stringify; + impl PipelineElement for Stringify { + fn runner(&self) -> fn(&Inputs) -> Outputs { + |input| { + if let [Data::Int(int), ..] = input.inner()[..] { + int.to_string().into() + } else { + panic!("Invalid data passed") + } + } + } + + fn signature(&self) -> ElementIo { + ElementIo { + inputs: vec![DataType::Int], + outputs: vec![DataType::String], + } + } + } +} diff --git a/crates/prowocessing/src/lib.rs b/crates/prowocessing/src/lib.rs index 2e7b047..aa81057 100644 --- a/crates/prowocessing/src/lib.rs +++ b/crates/prowocessing/src/lib.rs @@ -2,8 +2,7 @@ //! //! One of the design goals for this library is, however, to be a simple, generic image processing library. //! For now, it's just indev... lets see what comes of it! - -use experimental::enum_based::{Instruction, PipelineBuilder}; +#![feature(lint_reasons)] /// just some experiments, to test whether the architecture i want is even possible (or how to do it). probably temporary. /// Gonna first try string processing... @@ -11,6 +10,7 @@ pub mod experimental; #[test] fn test_enums() { + use crate::experimental::enum_based::{Instruction, PipelineBuilder}; let builder = PipelineBuilder::new().insert(Instruction::Uppercase); let upr = builder.build(); let upr_lowr = builder.insert(Instruction::Lowercase).build();