shellshot 0.5.0

Transform your command-line output into clean, shareable images with a single command.
Documentation
use termwiz::{
    cell::{AttributeChange, CellAttributes},
    escape::csi::Sgr,
    surface::{Change, SEQ_ZERO, SequenceNo, Surface},
};

pub fn process_sgr(surface: &mut Surface, sgr: &Sgr) -> SequenceNo {
    match sgr {
        Sgr::Reset => surface.add_change(Change::AllAttributes(CellAttributes::default())),
        Sgr::Intensity(intensity) => {
            surface.add_change(Change::Attribute(AttributeChange::Intensity(*intensity)))
        }
        Sgr::Underline(underline) => {
            surface.add_change(Change::Attribute(AttributeChange::Underline(*underline)))
        }
        Sgr::Inverse(inverse) => {
            surface.add_change(Change::Attribute(AttributeChange::Reverse(*inverse)))
        }
        Sgr::Foreground(color) => surface.add_change(Change::Attribute(
            AttributeChange::Foreground((*color).into()),
        )),
        Sgr::Background(color) => surface.add_change(Change::Attribute(
            AttributeChange::Background((*color).into()),
        )),
        Sgr::Italic(italic) => {
            surface.add_change(Change::Attribute(AttributeChange::Italic(*italic)))
        }
        Sgr::StrikeThrough(enabled) => {
            surface.add_change(Change::Attribute(AttributeChange::StrikeThrough(*enabled)))
        }
        Sgr::Invisible(enabled) => {
            surface.add_change(Change::Attribute(AttributeChange::Invisible(*enabled)))
        }
        Sgr::UnderlineColor(_)
        | Sgr::Blink(_)
        | Sgr::Font(_)
        | Sgr::Overline(_)
        | Sgr::VerticalAlign(_) => SEQ_ZERO,
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use termwiz::cell::{Intensity, Underline};
    use termwiz::color::{ColorAttribute, ColorSpec, SrgbaTuple};
    use termwiz::escape::csi::Sgr;
    use termwiz::surface::Surface;

    fn make_surface() -> Surface {
        Surface::new(80, 24)
    }

    fn apply_sgr(surface: &mut Surface, sgr: &Sgr) {
        process_sgr(surface, sgr);
    }

    #[test]
    fn sgr_reset() {
        let mut s = make_surface();
        apply_sgr(&mut s, &Sgr::Reset);

        s.add_change("A");
        let screen = s.screen_cells();
        let first_cell = &screen[0][0];

        assert_eq!(first_cell.attrs().intensity(), Intensity::Normal);
        assert_eq!(first_cell.attrs().underline(), Underline::None);
        assert_eq!(s.cursor_shape(), None);
    }

    #[test]
    fn sgr_intensity() {
        let mut s = make_surface();
        apply_sgr(&mut s, &Sgr::Intensity(Intensity::Bold));

        s.add_change("B");
        let screen = s.screen_cells();
        let first_cell = &screen[0][0];

        assert_eq!(first_cell.attrs().intensity(), Intensity::Bold);
    }

    #[test]
    fn sgr_underline() {
        let mut s = make_surface();
        apply_sgr(&mut s, &Sgr::Underline(Underline::Single));

        s.add_change("C");
        let screen = s.screen_cells();
        let first_cell = &screen[0][0];
        assert_eq!(first_cell.attrs().underline(), Underline::Single);

        apply_sgr(&mut s, &Sgr::Underline(Underline::Double));
        s.add_change("D");
        let screen = s.screen_cells();
        let second_cell = &screen[0][1];
        assert_eq!(second_cell.attrs().underline(), Underline::Double);

        apply_sgr(&mut s, &Sgr::Underline(Underline::None));
        s.add_change("E");
        let screen = s.screen_cells();
        let third_cell = &screen[0][2];
        assert_eq!(third_cell.attrs().underline(), Underline::None);
    }

    #[test]
    fn sgr_inverse() {
        let mut s = make_surface();
        apply_sgr(&mut s, &Sgr::Inverse(true));

        s.add_change("F");
        let screen = s.screen_cells();
        let first_cell = &screen[0][0];
        assert!(first_cell.attrs().reverse());

        apply_sgr(&mut s, &Sgr::Inverse(false));
        s.add_change("G");
        let screen = s.screen_cells();
        let second_cell = &screen[0][1];
        assert!(!second_cell.attrs().reverse());
    }

    #[test]
    fn sgr_italic() {
        let mut s = make_surface();
        apply_sgr(&mut s, &Sgr::Italic(true));

        s.add_change("H");
        let screen = s.screen_cells();
        let first_cell = &screen[0][0];
        assert!(first_cell.attrs().italic());
    }

    #[test]
    fn sgr_strikethrough() {
        let mut s = make_surface();
        apply_sgr(&mut s, &Sgr::StrikeThrough(true));

        s.add_change("I");
        let screen = s.screen_cells();
        let first_cell = &screen[0][0];
        assert!(first_cell.attrs().strikethrough());
    }

    #[test]
    fn sgr_invisible() {
        let mut s = make_surface();
        apply_sgr(&mut s, &Sgr::Invisible(true));

        s.add_change("J");
        let screen = s.screen_cells();
        let first_cell = &screen[0][0];
        assert!(first_cell.attrs().invisible());
    }

    #[test]
    fn sgr_foreground_background() {
        let mut s = make_surface();
        apply_sgr(&mut s, &Sgr::Foreground(ColorSpec::PaletteIndex(120)));

        s.add_change("K");
        let screen = s.screen_cells();
        let first_cell = &screen[0][0];
        assert_eq!(
            first_cell.attrs().foreground(),
            ColorAttribute::PaletteIndex(120)
        );

        apply_sgr(&mut s, &Sgr::Background(ColorSpec::Default));
        s.add_change("L");
        let screen = s.screen_cells();
        let second_cell = &screen[0][1];
        assert_eq!(second_cell.attrs().background(), ColorAttribute::Default);
    }

    #[test]
    fn sgr_foreground_truecolor() {
        let mut s = make_surface();
        let color = SrgbaTuple(1.0, 0.0, 0.0, 1.0);
        apply_sgr(&mut s, &Sgr::Foreground(ColorSpec::TrueColor(color)));

        s.add_change("M");
        let screen = s.screen_cells();
        let first_cell = &screen[0][0];
        assert_eq!(
            first_cell.attrs().foreground(),
            ColorAttribute::TrueColorWithDefaultFallback(color)
        );
    }
}