#![deny(missing_debug_implementations, missing_docs)]
#![allow(clippy::zero_prefixed_literal)]
use std::{error::Error, fmt, str};
macro_rules! ascii {
($($xx:literal/$yy:literal), *) => {
unsafe { std::str::from_utf8_unchecked(&[$(($xx << 4) + $yy),*]) }
};
}
#[derive(Debug)]
pub enum InvalidControlFunction {
InvalidAsciiError,
InvalidFunctionValueError,
InvalidIntermediateByteError,
InvalidPrivateUseError,
}
impl fmt::Display for InvalidControlFunction {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InvalidControlFunction::InvalidAsciiError => {
write!(formatter, "Control function must be valid ASCII")
}
InvalidControlFunction::InvalidFunctionValueError => write!(
formatter,
"Control function must have one- or two-byte identifier"
),
InvalidControlFunction::InvalidIntermediateByteError => {
write!(formatter, "Intermediate byte must be 02/00")
}
InvalidControlFunction::InvalidPrivateUseError => write!(
formatter,
"Private use functions are only allowed in range 07/00 to 07/15"
),
}
}
}
impl Error for InvalidControlFunction {}
#[derive(Clone, Copy, PartialEq, Eq)]
enum ControlFunctionType {
C0,
C1,
ControlSequence,
IndependentControlFunction,
}
impl fmt::Debug for ControlFunctionType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ControlFunctionType::C0 => write!(f, "C0"),
ControlFunctionType::C1 => write!(f, "C1"),
ControlFunctionType::ControlSequence => write!(f, "Control Sequence"),
ControlFunctionType::IndependentControlFunction => {
write!(f, "Independent Control Function")
}
}
}
}
#[derive(PartialEq, Eq)]
pub struct ControlFunction<'a> {
function_type: ControlFunctionType,
value: &'a str,
parameters: Vec<String>,
}
impl ControlFunction<'static> {
const fn new_c0(value: &'static str) -> Self {
ControlFunction {
function_type: ControlFunctionType::C0,
value,
parameters: vec![],
}
}
const fn new_c1(value: &'static str) -> Self {
ControlFunction {
function_type: ControlFunctionType::C1,
value,
parameters: vec![],
}
}
const fn new_independent_control_function(value: &'static str) -> Self {
ControlFunction {
function_type: ControlFunctionType::IndependentControlFunction,
value,
parameters: vec![],
}
}
}
impl<'a> ControlFunction<'a> {
const fn new_sequence(value: &'a str, parameters: Vec<String>) -> Self {
ControlFunction {
function_type: ControlFunctionType::ControlSequence,
value,
parameters,
}
}
pub fn private_use(
value: &'a str,
parameters: Vec<String>,
) -> Result<Self, InvalidControlFunction> {
if !value.is_ascii() {
return Err(InvalidControlFunction::InvalidAsciiError);
}
if value.as_bytes().len() > 2 {
return Err(InvalidControlFunction::InvalidFunctionValueError);
}
let function_value = if value.as_bytes().len() == 2 {
if &value[0..1] != ascii!(02 / 00) {
return Err(InvalidControlFunction::InvalidIntermediateByteError);
}
&value[1..2]
} else {
&value[0..1]
};
if function_value.as_bytes()[0] >> 4 != 7 {
return Err(InvalidControlFunction::InvalidPrivateUseError);
}
Ok(ControlFunction {
function_type: ControlFunctionType::ControlSequence,
value,
parameters,
})
}
fn format_parameters(&self) -> String {
self.parameters.join(ascii!(03 / 11))
}
}
impl<'a> fmt::Display for ControlFunction<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.function_type {
ControlFunctionType::C0 => {
write!(f, "{}", self.value)
}
ControlFunctionType::C1 | ControlFunctionType::IndependentControlFunction => {
write!(f, "{}{}", c0::ESC, self.value)
}
ControlFunctionType::ControlSequence => {
let parameters = self.format_parameters();
write!(f, "{}{}{}", c1::CSI, parameters, self.value)
}
}
}
}
impl<'a> fmt::Debug for ControlFunction<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let function: String = self
.value
.as_bytes()
.iter()
.map(|b| format!("{:02}/{:02}", b >> 4, (b & 0xF)))
.collect::<Vec<_>>()
.join(" ");
f.debug_struct("ControlFunction")
.field("function_type", &self.function_type)
.field("function", &function)
.field("parameters", &self.parameters)
.finish()
}
}
impl<'a> From<ControlFunction<'a>> for String {
fn from(control_function: ControlFunction) -> Self {
format!("{}", control_function)
}
}
impl<'a, T> PartialEq<T> for ControlFunction<'a>
where
T: AsRef<str>,
{
#[allow(clippy::cmp_owned)]
fn eq(&self, other: &T) -> bool {
let other_str = other.as_ref();
match self.function_type {
ControlFunctionType::C0 => self.value == other_str,
ControlFunctionType::C1 | ControlFunctionType::IndependentControlFunction => {
if other_str.len() != 2 {
return false;
}
other_str[0..1] == *c0::ESC.value && other_str[1..2] == *self.value
}
ControlFunctionType::ControlSequence => self.to_string() == other_str,
}
}
}
impl<'a> PartialEq<ControlFunction<'a>> for &str {
fn eq(&self, other: &ControlFunction) -> bool {
other == self
}
}
impl<'a> PartialEq<ControlFunction<'a>> for String {
fn eq(&self, other: &ControlFunction) -> bool {
other == self
}
}
pub mod c0;
pub mod c1;
pub mod categories;
pub mod control_sequences;
pub mod control_strings;
pub mod independent_control_functions;
pub mod modes;
#[cfg(feature = "parser")]
pub mod parser;
#[cfg(feature = "explain")]
pub mod explain;
#[cfg(test)]
mod tests {
use crate::c0::{BEL, ESC};
use crate::c1::CSI;
use crate::control_sequences::CNL;
use crate::independent_control_functions::INT;
use crate::ControlFunctionType;
#[test]
fn debug_control_function_type() {
assert_eq!(format!("{:?}", ControlFunctionType::C0), "C0");
assert_eq!(format!("{:?}", ControlFunctionType::C1), "C1");
assert_eq!(
format!("{:?}", ControlFunctionType::ControlSequence),
"Control Sequence"
);
assert_eq!(
format!("{:?}", ControlFunctionType::IndependentControlFunction),
"Independent Control Function"
);
}
#[test]
fn debug_control_function() {
assert_eq!(
format!("{:?}", BEL),
"ControlFunction { function_type: C0, function: \"00/07\", parameters: [] }"
);
assert_eq!(
format!("{:?}", crate::control_sequences::CUP(None, Some(10))),
"ControlFunction { function_type: Control Sequence, function: \"04/08\", parameters: [\"1\", \"10\"] }"
);
}
#[test]
fn string_equality_c0() {
let esc_control = ESC;
let esc_str = "\u{001B}";
let esc_string = String::from(esc_str);
assert_eq!(
esc_control, esc_control,
"Asserting equality between same control codes failed"
);
assert_eq!(
esc_control, esc_str,
"Asserting equality between control code and string slice failed"
);
assert_eq!(
esc_control, esc_string,
"Asserting equality between control code and string failed"
);
assert!(
esc_control == esc_str,
"Failed to compare control code and string slice"
);
assert!(
esc_control == esc_string,
"Failed to compare control code and string"
);
assert_ne!(
esc_control, "\u{0007}",
"Different control codes should not be equal"
);
}
#[test]
fn string_equality_c1() {
let csi_control = CSI;
let csi_str = "\u{001B}[";
let csi_string = String::from(csi_str);
assert_eq!(
csi_control, csi_control,
"Asserting equality between same control codes failed"
);
assert_eq!(
csi_control, csi_str,
"Asserting equality between control code and string slice failed"
);
assert_eq!(
csi_control, csi_string,
"Asserting equality between control code and string failed"
);
assert!(
csi_control == csi_str,
"Failed to compare control code and string slice"
);
assert!(
csi_control == csi_string,
"Failed to compare control code and string"
);
assert_ne!(
csi_control, "\u{001B}]",
"Different control codes should not be equal"
);
}
#[test]
fn string_equality_control_sequence() {
let cnl_control = CNL(4.into());
let cnl_str = "\u{001B}[4E";
let cnl_string = String::from(cnl_str);
assert_eq!(
cnl_control, cnl_control,
"Asserting equality between same control codes failed"
);
assert_eq!(
cnl_control, cnl_str,
"Asserting equality between control code and string slice failed"
);
assert_eq!(
cnl_control, cnl_string,
"Asserting equality between control code and string failed"
);
assert!(
cnl_control == cnl_str,
"Failed to compare control code and string slice"
);
assert!(
cnl_control == cnl_string,
"Failed to compare control code and string"
);
assert_ne!(
cnl_control, "\u{001B}[3E",
"Different control codes should not be equal"
);
}
#[test]
fn string_equality_independent_control_functions() {
let icf_control = INT;
let icf_str = "\u{001B}a";
let icf_string = String::from(icf_str);
assert_eq!(
icf_control, icf_control,
"Asserting equality between same control codes failed"
);
assert_eq!(
icf_control, icf_str,
"Asserting equality between control code and string slice failed"
);
assert_eq!(
icf_control, icf_string,
"Asserting equality between control code and string failed"
);
assert!(
icf_control == icf_str,
"Failed to compare control code and string slice"
);
assert!(
icf_control == icf_string,
"Failed to compare control code and string"
);
assert_ne!(
icf_control, "\u{001B}b",
"Different control codes should not be equal"
);
}
}