use super::expression::Expression;
use super::shell::Shell;
use std::fmt::Write;
use std::marker::PhantomData;

#[derive(Debug)]
pub(crate) struct LineSeparatedExpressions<S: Shell, A: Expression<S>, B: Expression<S>> {
    pub(crate) _shell: std::marker::PhantomData<S>,
    pub(crate) a: A,
    pub(crate) b: B,
}

impl<S: Shell, A: Expression<S>, B: Expression<S>> LineSeparatedExpressions<S, A, B> {
    pub fn then<C: Expression<S>>(
        self,
        c: C,
    ) -> LineSeparatedExpressions<S, LineSeparatedExpressions<S, A, B>, C> {
        LineSeparatedExpressions {
            _shell: PhantomData,
            a: self,
            b: c,
        }
    }
}

impl<S: Shell, B: Expression<S>> LineSeparatedExpressions<S, (), B> {
    pub(crate) fn new(b: B) -> Self {
        Self {
            _shell: std::marker::PhantomData,
            a: (),
            b,
        }
    }
}

impl<S: Shell, A: Expression<S>, B: Expression<S>> Expression<S>
    for LineSeparatedExpressions<S, A, B>
{
    fn write_shell(&self, writer: &mut impl Write) -> std::fmt::Result {
        self.a.write_shell(writer)?;
        write!(writer, "\n")?;
        self.b.write_shell(writer)
    }
}

mod tests {
    use super::super::raw::Raw;
    use super::super::shell::Zsh;
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn test_line_separated_expression() {
        let mut s = String::new();
        LineSeparatedExpressions::<Zsh, _, _>::new(Raw("Hello".into()))
            .then(Raw("World".into()))
            .then(Raw("Other".into()))
            .write_shell(&mut s)
            .unwrap();
        assert_eq!(s.trim(), "Hello\nWorld\nOther");
    }
}