WIP: image processing library (or libraries?) #12
|
@ -2,7 +2,8 @@
|
||||||
members = [
|
members = [
|
||||||
"crates/app",
|
"crates/app",
|
||||||
"crates/eval",
|
"crates/eval",
|
||||||
"crates/ir", "crates/prowocessing",
|
"crates/ir",
|
||||||
schrottkatze marked this conversation as resolved
Outdated
|
|||||||
|
"crates/prowocessing",
|
||||||
]
|
]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ enum Commands {
|
||||||
},
|
},
|
||||||
Dev {
|
Dev {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
dev_command: DevCommands,
|
command: DevCommands,
|
||||||
schrottkatze marked this conversation as resolved
Outdated
multisamplednight
commented
Nit: Is there clarity gained in prefixing the variable name with Nit: Is there clarity gained in prefixing the variable name with `dev_` given it's always taken out of the `Dev` variant?
schrottkatze
commented
nope, changing that nope, changing that
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,9 @@ fn main() {
|
||||||
machine.feed(ir);
|
machine.feed(ir);
|
||||||
machine.eval_full();
|
machine.eval_full();
|
||||||
}
|
}
|
||||||
Commands::Dev { dev_command } => dev_command.run(),
|
Commands::Dev {
|
||||||
|
command: dev_command,
|
||||||
|
} => dev_command.run(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
//! Definitions of the data transfer and storage types.
|
//! Definitions of the data transfer and storage types.
|
||||||
|
|
||||||
/// Types for element and pipeline IO
|
|
||||||
pub mod io;
|
pub mod io;
|
||||||
schrottkatze marked this conversation as resolved
Outdated
multisamplednight
commented
Given that you use a module comment at the very start of this file, it seems like these doc-comments would also belong into their respective modules. Given that you use a module comment at the very start of this file, it seems like these doc-comments would also belong into their respective modules.
This gives the reader added context when opening the respective module e.g. after following a type definition.
|
|||||||
|
|
||||||
/// Raw data types contained in `io`
|
|
||||||
pub mod raw;
|
pub mod raw;
|
||||||
|
|
|
@ -1,22 +1,26 @@
|
||||||
use super::raw::{Data, OwnedData};
|
//! Types for element and pipeline IO
|
||||||
|
|
||||||
|
use std::convert::Into;
|
||||||
|
|
||||||
|
use super::raw::{Data, DataRef};
|
||||||
|
|
||||||
/// Newtype struct with borrowed types for pipeline/element inputs, so that doesn't force a move or clone
|
/// Newtype struct with borrowed types for pipeline/element inputs, so that doesn't force a move or clone
|
||||||
schrottkatze marked this conversation as resolved
multisamplednight
commented
Quite ambiguous doc-comment also regarding the rather lengthy doc-comment on the type itself. Quite ambiguous doc-comment also regarding the rather lengthy doc-comment on the type itself.
How about removing this method altogether and making the content of `Inputs` directly public,
given that one's free to convert from/to it already?
|
|||||||
pub struct Inputs<'a>(Vec<Data<'a>>);
|
pub struct Inputs<'a>(Vec<DataRef<'a>>);
|
||||||
|
|
||||||
impl<'a> Inputs<'a> {
|
impl<'a> Inputs<'a> {
|
||||||
/// get inner value(s)
|
/// get inner value(s)
|
||||||
pub(crate) fn inner(&self) -> Vec<Data<'a>> {
|
pub(crate) fn inner(&self) -> Vec<DataRef<'a>> {
|
||||||
self.0.clone()
|
self.0.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<Vec<Data<'a>>> for Inputs<'a> {
|
impl<'a> From<Vec<DataRef<'a>>> for Inputs<'a> {
|
||||||
fn from(value: Vec<Data<'a>>) -> Self {
|
fn from(value: Vec<DataRef<'a>>) -> Self {
|
||||||
Self(value)
|
Self(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Into<Data<'a>>> From<T> for Inputs<'a> {
|
impl<'a, T: Into<DataRef<'a>>> From<T> for Inputs<'a> {
|
||||||
fn from(value: T) -> Self {
|
fn from(value: T) -> Self {
|
||||||
Self(vec![value.into()])
|
Self(vec![value.into()])
|
||||||
}
|
}
|
||||||
|
@ -24,25 +28,25 @@ impl<'a, T: Into<Data<'a>>> From<T> for Inputs<'a> {
|
||||||
|
|
||||||
impl<'a> From<&'a Outputs> for Inputs<'a> {
|
impl<'a> From<&'a Outputs> for Inputs<'a> {
|
||||||
fn from(value: &'a Outputs) -> Self {
|
fn from(value: &'a Outputs) -> Self {
|
||||||
Self(value.0.iter().map(std::convert::Into::into).collect())
|
Self(value.0.iter().map(Into::into).collect())
|
||||||
schrottkatze marked this conversation as resolved
Outdated
multisamplednight
commented
I already see that this is a newtype around I already see that this is a newtype around `OwnedData`, just re-stating it does not add semantical value. Rather, what would this struct be used for?
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Newtype struct around `OwnedData` for pipeline/element outputs
|
/// Used for pipeline/element outputs
|
||||||
pub struct Outputs(Vec<OwnedData>);
|
pub struct Outputs(Vec<Data>);
|
||||||
schrottkatze marked this conversation as resolved
multisamplednight
commented
Wait, why is Wait, why is `Outputs` allowed to be consumed for its inner content consumed while `Inputs` doesn't?
schrottkatze
commented
(I'm currently not happy how the IO of instructions works anyway, planning on reworking that to be more sensible, clear and flexible)) `Inputs` only contains a `Vec` of `Data` which either contains a string slice or an integer, which are really cheap to clone. `OwnedData` would be much heavier to clone in this case.
(I'm currently not happy how the IO of instructions works anyway, planning on reworking that to be more sensible, clear and flexible))
|
|||||||
|
|
||||||
impl Outputs {
|
impl Outputs {
|
||||||
/// consume self and return inner value(s)
|
/// consume self and return inner value(s)
|
||||||
pub fn into_inner(self) -> Vec<OwnedData> {
|
pub fn into_inner(self) -> Vec<Data> {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<Vec<OwnedData>> for Outputs {
|
impl From<Vec<Data>> for Outputs {
|
||||||
fn from(value: Vec<OwnedData>) -> Self {
|
fn from(value: Vec<Data>) -> Self {
|
||||||
Self(value)
|
Self(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: Into<OwnedData>> From<T> for Outputs {
|
impl<T: Into<Data>> From<T> for Outputs {
|
||||||
fn from(value: T) -> Self {
|
fn from(value: T) -> Self {
|
||||||
Self(vec![value.into()])
|
Self(vec![value.into()])
|
||||||
}
|
}
|
||||||
|
@ -53,7 +57,7 @@ impl From<Inputs<'_>> for Outputs {
|
||||||
value
|
value
|
||||||
.0
|
.0
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|i: Data<'_>| Data::to_owned_data(&i))
|
.map(|i: DataRef<'_>| DataRef::to_owned_data(&i))
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,58 +1,58 @@
|
||||||
//! Dynamic data storage and transfer types
|
//! Dynamic data storage and transfer types for use in [`io`]
|
||||||
|
|
||||||
/// Owned data type, for use mostly in outputs and storage
|
/// Owned data type, for use mostly in outputs and storage
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum OwnedData {
|
pub enum Data {
|
||||||
schrottkatze marked this conversation as resolved
Outdated
multisamplednight
commented
Given the common pattern of Given the common pattern of `AsRef` in the stdlib and `InstructionRef` already being used in this project, how about renaming `OwnedData` → `Data` and `Data` below → `DataRef`?
schrottkatze
commented
did the renaming, gonna take care of the did the renaming, gonna take care of the `AsRef` impl later (I remember trying that earlier and something causing problems, i forgot what that was though.)
|
|||||||
String(String),
|
String(String),
|
||||||
Int(i32),
|
Int(i32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<String> for OwnedData {
|
impl From<String> for Data {
|
||||||
fn from(value: String) -> Self {
|
fn from(value: String) -> Self {
|
||||||
Self::String(value)
|
Self::String(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<i32> for OwnedData {
|
impl From<i32> for Data {
|
||||||
fn from(value: i32) -> Self {
|
fn from(value: i32) -> Self {
|
||||||
Self::Int(value)
|
Self::Int(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unowned data type, for inputs into runner functions
|
/// Unowned data type, for inputs into runner functions
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
schrottkatze marked this conversation as resolved
Outdated
multisamplednight
commented
Why does Why does `OwnedData` implement `Debug` while `Data` doesn't?
|
|||||||
pub enum Data<'a> {
|
pub enum DataRef<'a> {
|
||||||
String(&'a str),
|
String(&'a str),
|
||||||
Int(i32),
|
Int(i32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Data<'_> {
|
impl DataRef<'_> {
|
||||||
/// converts itself to `OwnedData`
|
/// converts itself to `OwnedData`
|
||||||
pub fn to_owned_data(&self) -> OwnedData {
|
pub fn to_owned_data(&self) -> Data {
|
||||||
schrottkatze marked this conversation as resolved
Outdated
multisamplednight
commented
Consider implementing OT: Wondering that clippy didn't scream about this. Interesting, since it has a lint for this, but apparently it didn't trigger since the name was (imo unnecessarily) suffixed with Consider implementing [`ToOwned`] and [`AsRef`] appropiately instead.
OT: Wondering that clippy didn't scream about this. Interesting, since [it has a lint for this](https://rust-lang.github.io/rust-clippy/master/index.html#/should_implement_trait), but apparently it didn't trigger since the name was (imo unnecessarily) suffixed with `_data`.
[`ToOwned`]: https://doc.rust-lang.org/std/borrow/trait.ToOwned.html
[`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
schrottkatze
commented
I did that deliberately to not trigger the lint, but I'll fix that and properly do it after fixing your simpler reviews I did that deliberately to not trigger the lint, but I'll fix that and properly do it after fixing your simpler reviews
schrottkatze
commented
As I've just realized because I was trying to implement As I've just realized because I was trying to implement `ToOwned`/`AsRef`, it isn't even necessary to have a seperate `DataRef`, so i removed that entirely and... who would've thought, it's much more readable and concise overall now
|
|||||||
match self {
|
match self {
|
||||||
Data::String(s) => (*s).to_owned().into(),
|
DataRef::String(s) => (*s).to_owned().into(),
|
||||||
Data::Int(i) => (*i).into(),
|
DataRef::Int(i) => (*i).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a str> for Data<'a> {
|
impl<'a> From<&'a str> for DataRef<'a> {
|
||||||
fn from(value: &'a str) -> Self {
|
fn from(value: &'a str) -> Self {
|
||||||
Self::String(value)
|
Self::String(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<i32> for Data<'_> {
|
impl From<i32> for DataRef<'_> {
|
||||||
fn from(value: i32) -> Self {
|
fn from(value: i32) -> Self {
|
||||||
Self::Int(value)
|
Self::Int(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a OwnedData> for Data<'a> {
|
impl<'a> From<&'a Data> for DataRef<'a> {
|
||||||
fn from(value: &'a OwnedData) -> Self {
|
fn from(value: &'a Data) -> Self {
|
||||||
match value {
|
match value {
|
||||||
OwnedData::String(s) => Data::String(s),
|
Data::String(s) => DataRef::String(s),
|
||||||
OwnedData::Int(i) => Data::Int(*i),
|
Data::Int(i) => DataRef::Int(*i),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use core::panic;
|
||||||
use crate::experimental::trait_based::{
|
use crate::experimental::trait_based::{
|
||||||
data::{
|
data::{
|
||||||
io::{Inputs, Outputs},
|
io::{Inputs, Outputs},
|
||||||
raw::Data,
|
raw::DataRef,
|
||||||
},
|
},
|
||||||
element::{DataType, ElementSignature, PipelineElement},
|
element::{DataType, ElementSignature, PipelineElement},
|
||||||
};
|
};
|
||||||
|
@ -14,7 +14,7 @@ pub struct Add(pub i32);
|
||||||
impl PipelineElement for Add {
|
impl PipelineElement for Add {
|
||||||
fn runner(&self) -> fn(&Inputs) -> Outputs {
|
fn runner(&self) -> fn(&Inputs) -> Outputs {
|
||||||
|input| {
|
|input| {
|
||||||
if let [Data::Int(i0), Data::Int(i1), ..] = input.inner()[..] {
|
if let [DataRef::Int(i0), DataRef::Int(i1), ..] = input.inner()[..] {
|
||||||
(i0 + i1).into()
|
(i0 + i1).into()
|
||||||
} else {
|
} else {
|
||||||
panic!("Invalid data passed")
|
panic!("Invalid data passed")
|
||||||
|
@ -35,7 +35,7 @@ pub struct Subtract(pub i32);
|
||||||
impl PipelineElement for Subtract {
|
impl PipelineElement for Subtract {
|
||||||
fn runner(&self) -> fn(&Inputs) -> Outputs {
|
fn runner(&self) -> fn(&Inputs) -> Outputs {
|
||||||
|input| {
|
|input| {
|
||||||
if let [Data::Int(i0), Data::Int(i1), ..] = input.inner()[..] {
|
if let [DataRef::Int(i0), DataRef::Int(i1), ..] = input.inner()[..] {
|
||||||
(i0 + i1).into()
|
(i0 + i1).into()
|
||||||
} else {
|
} else {
|
||||||
panic!("Invalid data passed")
|
panic!("Invalid data passed")
|
||||||
|
@ -56,7 +56,7 @@ pub struct Stringify;
|
||||||
impl PipelineElement for Stringify {
|
impl PipelineElement for Stringify {
|
||||||
fn runner(&self) -> fn(&Inputs) -> Outputs {
|
fn runner(&self) -> fn(&Inputs) -> Outputs {
|
||||||
|input| {
|
|input| {
|
||||||
if let [Data::Int(int), ..] = input.inner()[..] {
|
if let [DataRef::Int(int), ..] = input.inner()[..] {
|
||||||
int.to_string().into()
|
int.to_string().into()
|
||||||
} else {
|
} else {
|
||||||
panic!("Invalid data passed")
|
panic!("Invalid data passed")
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
use crate::experimental::trait_based::{
|
use crate::experimental::trait_based::{
|
||||||
data::{
|
data::{
|
||||||
io::{Inputs, Outputs},
|
io::{Inputs, Outputs},
|
||||||
raw::Data,
|
raw::DataRef,
|
||||||
},
|
},
|
||||||
element::{DataType, ElementSignature, PipelineElement},
|
element::{DataType, ElementSignature, PipelineElement},
|
||||||
};
|
};
|
||||||
|
@ -12,7 +12,7 @@ pub struct Concatenate(pub String);
|
||||||
impl PipelineElement for Concatenate {
|
impl PipelineElement for Concatenate {
|
||||||
fn runner(&self) -> fn(&Inputs) -> Outputs {
|
fn runner(&self) -> fn(&Inputs) -> Outputs {
|
||||||
|input| {
|
|input| {
|
||||||
if let [Data::String(s0), Data::String(s1), ..] = input.inner()[..] {
|
if let [DataRef::String(s0), DataRef::String(s1), ..] = input.inner()[..] {
|
||||||
format!("{s0}{s1}").into()
|
format!("{s0}{s1}").into()
|
||||||
} else {
|
} else {
|
||||||
panic!("Invalid data passed")
|
panic!("Invalid data passed")
|
||||||
|
@ -33,7 +33,7 @@ pub struct Upper;
|
||||||
impl PipelineElement for Upper {
|
impl PipelineElement for Upper {
|
||||||
fn runner(&self) -> fn(&Inputs) -> Outputs {
|
fn runner(&self) -> fn(&Inputs) -> Outputs {
|
||||||
|input| {
|
|input| {
|
||||||
if let [Data::String(s), ..] = input.inner()[..] {
|
if let [DataRef::String(s), ..] = input.inner()[..] {
|
||||||
s.to_uppercase().into()
|
s.to_uppercase().into()
|
||||||
} else {
|
} else {
|
||||||
panic!("Invalid data passed")
|
panic!("Invalid data passed")
|
||||||
|
@ -54,7 +54,7 @@ pub struct Lower;
|
||||||
impl PipelineElement for Lower {
|
impl PipelineElement for Lower {
|
||||||
fn runner(&self) -> fn(&Inputs) -> Outputs {
|
fn runner(&self) -> fn(&Inputs) -> Outputs {
|
||||||
|input| {
|
|input| {
|
||||||
if let [Data::String(s), ..] = input.inner()[..] {
|
if let [DataRef::String(s), ..] = input.inner()[..] {
|
||||||
s.to_lowercase().into()
|
s.to_lowercase().into()
|
||||||
} else {
|
} else {
|
||||||
panic!("Invalid data passed")
|
panic!("Invalid data passed")
|
||||||
|
|
Nit: Regarding formatting, you may want to split the newly added entry to its own line.
ah cargo keeps doing that and i keep forgetting when adding new crates lol