Hello, I am trying add common functionality to my Composition types as they all have ing_id, fraction/percentage, action_id, action_count. Common functionality will prevents unnecessary repetition, as I don't want to write a separate function for each of them. My functions handle checks, comparisons, and some modifications.
Here are the types:
#[derive(Debug, Clone, Copy,)]
pub struct CompositionWithoutMixId {
pub fraction: f64,
pub ingredient_id: i64,
pub action_id: i64,
pub action_counter: i32,
}
pub type Composition = Vec<CompositionWithoutMixId>;
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub struct CompositionFormPart {
pub ingredient_id: i64,
pub percentage: f64, // 100 is max
pub action_id: Option<i64>,
pub action_counter: Option<i32>,
}
pub type CompositionForm = Vec<CompositionFormPart>;
#[derive(Serialize, Default, Debug)]
pub struct CompositionInterface {
pub fraction: f64,
pub mix_id: i64,
pub ingredient_id: i64,
pub action_id: i64,
pub action_counter: i32,
pub action_name: String,
pub ingredient_name: String,
pub ingredient_version_group: i64,
pub ingredient_version: i64,
pub ingredient_deleted: bool,
pub ingredient_comment: Option<String>,
}
pub type MixCompositionInterface = Vec<CompositionInterface>;
Currently I have found a solution where:
I implement a getter trait:
trait CompositionTrait {
fn fraction(&self) -> f64;
fn ingredient_id(&self) -> i64;
fn action_id(&self) -> i64;
fn action_counter(&self) -> i32;
}
Various Into implementations for mut access:
// This struct is used in some implementations
pub struct MixComposition {
pub mix_id: i64,
pub composition: Composition,
}
impl Into<Vec<MixCompositionForCreate>> for MixComposition {
fn into(self) -> Vec<MixCompositionForCreate> {
self.composition
.into_iter()
.map(|cp| MixCompositionForCreate {
mix_id: self.mix_id,
fraction: cp.fraction,
ingredient_id: cp.ingredient_id,
action_id: cp.action_id,
action_counter: cp.action_counter,
})
.collect()
}
}
pub type CompositionForm = Vec<CompositionFormPart>;
impl From<CompositionFormPart> for CompositionWithoutMixId {
fn from(value: CompositionFormPart) -> CompositionWithoutMixId {
CompositionWithoutMixId {
fraction: value.percentage / 100.0,
ingredient_id: value.ingredient_id,
action_id: value.action_id.unwrap_or(ActionTypeEnum::default().into()),
action_counter: value.action_counter.unwrap_or(1),
}
}
}
pub fn form_into_comp(form_comp: CompositionForm) -> Composition {
form_comp
.into_iter()
.map(|p| CompositionWithoutMixId {
fraction: p.percentage / 100.0,
ingredient_id: p.ingredient_id,
action_id: p.action_id.unwrap_or(ActionTypeEnum::default().into()),
action_counter: p.action_counter.unwrap_or(1),
})
.collect()
}
pub fn comp_into_form(comp: Composition) -> CompositionForm {
comp.into_iter()
.map(|p| CompositionFormPart {
percentage: p.fraction * 100.0,
ingredient_id: p.ingredient_id,
action_id: Some(p.action_id),
action_counter: Some(p.action_counter),
})
.collect()
}
impl From<CompositionInterface> for CompositionWithoutMixId {
fn from(value: CompositionInterface) -> CompositionWithoutMixId {
CompositionWithoutMixId {
fraction: value.fraction,
ingredient_id: value.ingredient_id,
action_id: value.action_id,
action_counter: value.action_counter,
}
}
}
Finnaly I use impl Into or impl CompositionTrait as my function's parameters:
part: & impl CompositionTrait
// or
part: impl Into<CompositionWithoutMixId>
Is there a better way to do this?
I think I am achieving common functionality in a pretty obscure way...