tortoise 0.1.0

Build terminal user interfaces in Rust.
Documentation
use std::mem::transmute;

pub fn query(from_state: State, byte: u8) -> (Action, State) {
	let table_value = TABLE[from_state as usize][byte as usize];
	let to_action: Action = unsafe { transmute((table_value & 0xF0) >> 4) };
	let to_state: State = unsafe { transmute(table_value & 0x0F) };
	(to_action, to_state)
}

const TABLE: [[u8; 256]; 16] = build_state_table();

const fn build_state_table() -> [[u8; 256]; 16] {
	let mut table = [[0u8; 256]; 16];
	let mut state_index = 0;
	while state_index < STATES.len() {
		let from_state = STATES[state_index];
		let mut i: usize = 0;
		while i <= 0xFF {
			let (to_action, to_state) = table_fn(from_state, i as u8);
			let value = ((to_action as u8) << 4) | (to_state as u8);
			table[from_state as usize][i] = value;
			i += 1;
		}
		state_index += 1;
	}
	table
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(u8)]
pub enum State {
	CsiEntry = 0,
	CsiIgnore = 1,
	CsiIntermediate = 2,
	CsiParam = 3,
	DcsEntry = 4,
	DcsIgnore = 5,
	DcsIntermediate = 6,
	DcsParam = 7,
	DcsPassthrough = 8,
	Error = 9,
	Escape = 10,
	EscapeIntermediate = 11,
	Ground = 12,
	OscString = 13,
	SosPmApcString = 14,
}

const STATES: &[State] = &[
	State::CsiEntry,
	State::CsiIgnore,
	State::CsiIntermediate,
	State::CsiParam,
	State::DcsEntry,
	State::DcsIgnore,
	State::DcsIntermediate,
	State::DcsParam,
	State::DcsPassthrough,
	State::Error,
	State::Escape,
	State::EscapeIntermediate,
	State::Ground,
	State::OscString,
	State::SosPmApcString,
];

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(u8)]
pub enum Action {
	None = 0,
	Clear = 1,
	Collect = 2,
	CsiDispatch = 3,
	EscDispatch = 4,
	Execute = 5,
	Hook = 6,
	Ignore = 7,
	OscEnd = 8,
	OscPut = 9,
	OscStart = 10,
	Param = 11,
	Print = 12,
	Put = 13,
	Unhook = 14,
}

pub static ENTRY_ACTIONS: &[Action] = &[
	Action::Clear,    // State::CsiEntry
	Action::None,     // State::CsiIgnore
	Action::None,     // State::CsiIntermediate
	Action::None,     // State::CsiParam
	Action::Clear,    // State::DcsEntry
	Action::None,     // State::DcsIgnore
	Action::None,     // State::DcsIntermediate
	Action::None,     // State::DcsParam
	Action::Hook,     // State::DcsPassthrough
	Action::None,     // State::Error
	Action::Clear,    // State::Escape
	Action::None,     // State::EscapeIntermediate
	Action::None,     // State::Ground
	Action::OscStart, // State::OscString
	Action::None,     // State::SosPmApcString
];

pub static EXIT_ACTIONS: &[Action] = &[
	Action::None,   // State::CsiEntry
	Action::None,   // State::CsiIgnore
	Action::None,   // State::CsiIntermediate
	Action::None,   // State::CsiParam
	Action::None,   // State::DcsEntry
	Action::None,   // State::DcsIgnore
	Action::None,   // State::DcsIntermediate
	Action::None,   // State::DcsParam
	Action::Unhook, // State::DcsPassthrough
	Action::None,   // State::Error
	Action::None,   // State::Escape
	Action::None,   // State::EscapeIntermediate
	Action::None,   // State::Ground
	Action::OscEnd, // State::OscString
	Action::None,   // State::SosPmApcString
];

#[rustfmt::skip]
pub static UTF8_CHAR_WIDTH: [u8; 256] = [
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x0F
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1F
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x2F
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3F
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x4F
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5F
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x6F
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7F
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x8F
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9F
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xAF
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBF
	0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xCF
	2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xDF
	3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEF
	4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, // 0xFF
];

// https://vt100.net/emu/dec_ansi_parser
const fn table_fn(state: State, byte: u8) -> (Action, State) {
	match (state, byte) {
		(_, 0x18) => (Action::Execute, State::Ground),
		(_, 0x1A) => (Action::Execute, State::Ground),
		(_, 0x80..=0x8F) => (Action::Execute, State::Ground),
		(_, 0x91..=0x97) => (Action::Execute, State::Ground),
		(_, 0x99) => (Action::Execute, State::Ground),
		(_, 0x9A) => (Action::Execute, State::Ground),

		(_, 0x9C) => (Action::None, State::Ground),
		(_, 0x1B) => (Action::None, State::Escape),

		(_, 0x98) => (Action::None, State::SosPmApcString),
		(_, 0x9E) => (Action::None, State::SosPmApcString),
		(_, 0x9F) => (Action::None, State::SosPmApcString),

		(_, 0x90) => (Action::None, State::DcsEntry),
		(_, 0x9D) => (Action::None, State::OscString),
		(_, 0x9B) => (Action::None, State::CsiEntry),

		(State::CsiEntry, byte) => match byte {
			0x00..=0x17 => (Action::Execute, State::CsiEntry),
			0x19 => (Action::Execute, State::CsiEntry),
			0x1C..=0x1F => (Action::Execute, State::CsiEntry),
			0x7F => (Action::Ignore, State::CsiEntry),

			0x30..=0x39 => (Action::Param, State::CsiParam),
			0x3C..=0x3F => (Action::Collect, State::CsiParam),

			0x3A => (Action::None, State::CsiIgnore),

			0x20..=0x2F => (Action::Collect, State::CsiIntermediate),

			0x40..=0x7E => (Action::CsiDispatch, State::Ground),

			_ => (Action::None, State::Error),
		},
		(State::CsiIgnore, byte) => match byte {
			0x00..=0x17 => (Action::Execute, State::CsiIgnore),
			0x19 => (Action::Execute, State::CsiIgnore),
			0x1C..=0x1F => (Action::Execute, State::CsiIgnore),
			0x20..=0x3F => (Action::Ignore, State::CsiIgnore),
			0x7F => (Action::Ignore, State::CsiIgnore),

			0x40..=0x7E => (Action::None, State::Ground),

			_ => (Action::None, State::Error),
		},
		(State::CsiIntermediate, byte) => match byte {
			0x00..=0x17 => (Action::Execute, State::CsiIntermediate),
			0x19 => (Action::Execute, State::CsiIntermediate),
			0x1C..=0x1F => (Action::Execute, State::CsiIntermediate),
			0x20..=0x2F => (Action::Collect, State::CsiIntermediate),
			0x7F => (Action::Ignore, State::CsiIntermediate),

			0x40..=0x7E => (Action::CsiDispatch, State::Ground),

			_ => (Action::None, State::Error),
		},
		(State::CsiParam, byte) => match byte {
			0x00..=0x17 => (Action::Execute, State::CsiParam),
			0x19 => (Action::Execute, State::CsiParam),
			0x1C..=0x1F => (Action::Execute, State::CsiParam),
			0x30..=0x39 => (Action::Param, State::CsiParam),
			0x3B => (Action::Param, State::CsiParam),
			0x7F => (Action::Ignore, State::CsiParam),

			0x40..=0x7E => (Action::CsiDispatch, State::Ground),

			0x20..=0x2F => (Action::Collect, State::CsiIntermediate),

			0x3A => (Action::None, State::CsiIgnore),
			0x3C..=0x3F => (Action::None, State::CsiIgnore),

			_ => (Action::None, State::Error),
		},
		(State::DcsEntry, byte) => match byte {
			0x00..=0x17 => (Action::Ignore, State::DcsEntry),
			0x19 => (Action::Ignore, State::DcsEntry),
			0x1C..=0x1F => (Action::Ignore, State::DcsEntry),
			0x7F => (Action::Ignore, State::DcsEntry),

			0x20..=0x2F => (Action::Collect, State::DcsIntermediate),

			0x3A => (Action::None, State::DcsIgnore),

			0x30..=0x39 => (Action::Param, State::DcsParam),
			0x3C..=0x3F => (Action::Collect, State::DcsParam),

			0x40..=0x7E => (Action::None, State::DcsPassthrough),

			_ => (Action::None, State::Error),
		},
		(State::DcsIgnore, byte) => match byte {
			0x00..=0x17 => (Action::Ignore, State::DcsIgnore),
			0x19 => (Action::Ignore, State::DcsIgnore),
			0x1C..=0x1F => (Action::Ignore, State::DcsIgnore),
			0x20..=0x7F => (Action::Ignore, State::DcsIgnore),

			_ => (Action::None, State::Error),
		},
		(State::DcsIntermediate, byte) => match byte {
			0x00..=0x17 => (Action::Ignore, State::DcsIntermediate),
			0x19 => (Action::Ignore, State::DcsIntermediate),
			0x1C..=0x1F => (Action::Ignore, State::DcsIntermediate),
			0x20..=0x2F => (Action::Collect, State::DcsIntermediate),
			0x7F => (Action::Ignore, State::DcsIntermediate),

			0x40..=0x7E => (Action::None, State::DcsPassthrough),

			0x30..=0x3F => (Action::None, State::DcsIgnore),

			_ => (Action::None, State::Error),
		},
		(State::DcsParam, byte) => match byte {
			0x00..=0x17 => (Action::Ignore, State::DcsParam),
			0x19 => (Action::Ignore, State::DcsParam),
			0x1C..=0x1F => (Action::Ignore, State::DcsParam),
			0x30..=0x39 => (Action::Param, State::DcsParam),
			0x3B => (Action::Param, State::DcsParam),
			0x7F => (Action::Ignore, State::DcsParam),

			0x3A => (Action::None, State::DcsIgnore),
			0x3C..=0x3F => (Action::None, State::DcsIgnore),

			0x20..=0x2F => (Action::Collect, State::DcsIntermediate),

			0x40..=0x7E => (Action::None, State::DcsPassthrough),

			_ => (Action::None, State::Error),
		},
		(State::DcsPassthrough, byte) => match byte {
			0x00..=0x17 => (Action::Put, State::DcsPassthrough),
			0x19 => (Action::Put, State::DcsPassthrough),
			0x1C..=0x1F => (Action::Put, State::DcsPassthrough),
			0x20..=0x7E => (Action::Put, State::DcsPassthrough),
			0x7F => (Action::Ignore, State::DcsPassthrough),

			_ => (Action::None, State::Error),
		},
		(State::Error, _) => (Action::None, State::Error),
		(State::Escape, byte) => match byte {
			0x00..=0x17 => (Action::Execute, State::Escape),
			0x19 => (Action::Execute, State::Escape),
			0x1C..=0x1F => (Action::Execute, State::Escape),
			0x7F => (Action::Ignore, State::Escape),

			0x20..=0x2F => (Action::Collect, State::EscapeIntermediate),

			0x30..=0x4F => (Action::EscDispatch, State::Ground),
			0x51..=0x57 => (Action::EscDispatch, State::Ground),
			0x59 => (Action::EscDispatch, State::Ground),
			0x5A => (Action::EscDispatch, State::Ground),
			0x5C => (Action::EscDispatch, State::Ground),
			0x60..=0x7E => (Action::EscDispatch, State::Ground),

			0x5B => (Action::None, State::CsiEntry),

			0x5D => (Action::None, State::OscString),

			0x50 => (Action::None, State::DcsEntry),

			0x58 => (Action::None, State::SosPmApcString),
			0x5E => (Action::None, State::SosPmApcString),
			0x5F => (Action::None, State::SosPmApcString),

			_ => (Action::None, State::Error),
		},
		(State::EscapeIntermediate, byte) => match byte {
			0x00..=0x17 => (Action::Execute, State::EscapeIntermediate),
			0x19 => (Action::Execute, State::EscapeIntermediate),
			0x1C..=0x1F => (Action::Execute, State::EscapeIntermediate),
			0x20..=0x2F => (Action::Collect, State::EscapeIntermediate),
			0x7F => (Action::Ignore, State::EscapeIntermediate),

			0x30..=0x7E => (Action::EscDispatch, State::Ground),

			_ => (Action::None, State::Error),
		},
		(State::Ground, byte) => match byte {
			0x00..=0x17 => (Action::Execute, State::Ground),
			0x19 => (Action::Execute, State::Ground),
			0x1C..=0x1F => (Action::Execute, State::Ground),

			// UTF-8 1 byte code point
			0x20..=0x7F => (Action::Print, State::Ground),
			// UTF-8 2 byte code point
			0xC0..=0xDF => (Action::Print, State::Ground),
			// UTF-8 3 byte code point
			0xE0..=0xEF => (Action::Print, State::Ground),
			// UTF-8 4 byte code point
			0xF0..=0xF7 => (Action::Print, State::Ground),

			_ => (Action::None, State::Error),
		},
		(State::OscString, byte) => match byte {
			0x00..=0x17 => (Action::Ignore, State::OscString),
			0x19 => (Action::Ignore, State::OscString),
			0x1C..=0x1F => (Action::Ignore, State::OscString),
			0x20..=0x7F => (Action::OscPut, State::OscString),

			_ => (Action::None, State::Error),
		},
		(State::SosPmApcString, byte) => match byte {
			0x00..=0x17 => (Action::Ignore, State::SosPmApcString),
			0x19 => (Action::Ignore, State::SosPmApcString),
			0x1C..=0x1F => (Action::Ignore, State::SosPmApcString),
			0x20..=0x7F => (Action::Ignore, State::SosPmApcString),

			_ => (Action::None, State::Error),
		},
	}
}