pub enum Instruction {
    Uppercase,
    Lowercase,
}

pub struct Pipeline {
    pipeline: Vec<fn(String) -> 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<Instruction>,
}

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()
    }
}
