prowocessing: refactor trait based experiment to individual files

This commit is contained in:
Schrottkatze 2024-02-21 13:11:31 +01:00
parent 2d537cc4ee
commit 98f6af78be
Signed by: schrottkatze
SSH key fingerprint: SHA256:hXb3t1vINBFCiDCmhRABHX5ocdbLiKyCdKI4HK2Rbbc
10 changed files with 362 additions and 341 deletions

View file

@ -56,7 +56,6 @@ fn main() {
mod dev {
use clap::Subcommand;
use prowocessing::experimental::trait_based::DataType;
#[derive(Subcommand)]
pub(crate) enum DevCommands {
@ -81,7 +80,7 @@ mod dev {
println!("Lwr: {}", lwr.run(test_str.clone()));
}
DevCommands::Add { num0, num1 } => {
use prowocessing::experimental::trait_based::PipelineBuilder;
use prowocessing::experimental::trait_based::pipeline::PipelineBuilder;
let pipe = PipelineBuilder::new().add(1).stringify().build();
println!(

View file

@ -1,339 +1,4 @@
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<DataType>,
pub outputs: Vec<DataType>,
}
// 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<Box<dyn PipelineElement>>,
}
pub struct Pipeline {
runners: Vec<fn(&Inputs) -> 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<T: PipelineElement + 'static>(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<i32> 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<String> for OwnedData {
fn from(value: String) -> Self {
Self::String(value)
}
}
impl From<i32> 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<Data<'a>>);
impl<'a> Inputs<'a> {
fn inner(&self) -> Vec<Data<'a>> {
self.0.clone()
}
}
impl<'a> From<Vec<Data<'a>>> for Inputs<'a> {
fn from(value: Vec<Data<'a>>) -> Self {
Self(value)
}
}
impl<'a, T: Into<Data<'a>>> From<T> 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<OwnedData>);
impl Outputs {
pub fn into_inner(self) -> Vec<OwnedData> {
self.0
}
}
impl From<Vec<OwnedData>> for Outputs {
fn from(value: Vec<OwnedData>) -> Self {
Self(value)
}
}
impl<T: Into<OwnedData>> From<T> for Outputs {
fn from(value: T) -> Self {
Self(vec![value.into()])
}
}
impl From<Inputs<'_>> 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],
}
}
}
}
pub mod data;
pub mod element;
pub mod ops;
pub mod pipeline;

View file

@ -0,0 +1,2 @@
pub mod io;
pub mod raw;

View file

@ -0,0 +1,51 @@
use super::raw::{Data, OwnedData};
pub struct Inputs<'a>(Vec<Data<'a>>);
impl<'a> Inputs<'a> {
pub(crate) fn inner(&self) -> Vec<Data<'a>> {
self.0.clone()
}
}
impl<'a> From<Vec<Data<'a>>> for Inputs<'a> {
fn from(value: Vec<Data<'a>>) -> Self {
Self(value)
}
}
impl<'a, T: Into<Data<'a>>> From<T> 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<OwnedData>);
impl Outputs {
pub fn into_inner(self) -> Vec<OwnedData> {
self.0
}
}
impl From<Vec<OwnedData>> for Outputs {
fn from(value: Vec<OwnedData>) -> Self {
Self(value)
}
}
impl<T: Into<OwnedData>> From<T> for Outputs {
fn from(value: T) -> Self {
Self(vec![value.into()])
}
}
impl From<Inputs<'_>> for Outputs {
fn from(value: Inputs) -> Self {
Self(
value
.0
.into_iter()
.map(|i: Data<'_>| Data::to_owned_data(&i))
.collect(),
)
}
}

View file

@ -0,0 +1,48 @@
#[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<i32> 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<String> for OwnedData {
fn from(value: String) -> Self {
Self::String(value)
}
}
impl From<i32> for OwnedData {
fn from(value: i32) -> Self {
Self::Int(value)
}
}

View file

@ -0,0 +1,19 @@
use crate::experimental::trait_based::data::io::Inputs;
use super::data::io::Outputs;
pub(crate) trait PipelineElement {
fn runner(&self) -> fn(&Inputs) -> Outputs;
fn signature(&self) -> ElementIo;
}
pub(crate) struct ElementIo {
pub inputs: Vec<DataType>,
pub outputs: Vec<DataType>,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum DataType {
String,
Int,
}

View file

@ -0,0 +1,7 @@
mod num;
mod str;
pub mod prelude {
pub(crate) use super::num::*;
pub(crate) use super::str::*;
}

View file

@ -0,0 +1,69 @@
use core::panic;
use crate::experimental::trait_based::{
data::{
io::{Inputs, Outputs},
raw::Data,
},
element::{DataType, ElementIo, 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],
}
}
}

View file

@ -0,0 +1,67 @@
use crate::experimental::trait_based::{
data::{
io::{Inputs, Outputs},
raw::Data,
},
element::{DataType, ElementIo, 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],
}
}
}

View file

@ -0,0 +1,94 @@
use super::data::io::{Inputs, Outputs};
use super::element::PipelineElement;
use super::ops::prelude::*;
// 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<Box<dyn PipelineElement>>,
}
impl PipelineBuilder {
pub fn new() -> Self {
Self {
elements: Vec::new(),
}
}
fn insert<T: PipelineElement + 'static>(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()
}
}
pub struct Pipeline {
runners: Vec<fn(&Inputs) -> 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
}
}