Added rustfmt.toml, formatted codebase
This commit is contained in:
@@ -1,356 +1,439 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::{
|
||||
libsh::term::{Style, Styled},
|
||||
parse::lex::Span,
|
||||
prelude::*
|
||||
libsh::term::{Style, Styled},
|
||||
parse::lex::Span,
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
pub type ShResult<T> = Result<T,ShErr>;
|
||||
pub type ShResult<T> = Result<T, ShErr>;
|
||||
|
||||
pub trait ShResultExt {
|
||||
fn blame(self, span: Span) -> Self;
|
||||
fn try_blame(self, span: Span) -> Self;
|
||||
fn blame(self, span: Span) -> Self;
|
||||
fn try_blame(self, span: Span) -> Self;
|
||||
}
|
||||
|
||||
impl<T> ShResultExt for Result<T,ShErr> {
|
||||
/// Blame a span for an error
|
||||
fn blame(self, new_span: Span) -> Self {
|
||||
let Err(e) = self else {
|
||||
return self
|
||||
};
|
||||
match e {
|
||||
ShErr::Simple { kind, msg, notes } |
|
||||
ShErr::Full { kind, msg, notes, span: _ } => Err(ShErr::Full { kind: kind.clone(), msg: msg.clone(), notes: notes.clone(), span: new_span }),
|
||||
}
|
||||
}
|
||||
/// Blame a span if no blame has been assigned yet
|
||||
fn try_blame(self, new_span: Span) -> Self {
|
||||
let Err(e) = &self else {
|
||||
return self
|
||||
};
|
||||
match e {
|
||||
ShErr::Simple { kind, msg, notes } => Err(ShErr::Full { kind: kind.clone(), msg: msg.clone(), notes: notes.clone(), span: new_span }),
|
||||
ShErr::Full { kind: _, msg: _, span: _, notes: _ } => self
|
||||
}
|
||||
}
|
||||
impl<T> ShResultExt for Result<T, ShErr> {
|
||||
/// Blame a span for an error
|
||||
fn blame(self, new_span: Span) -> Self {
|
||||
let Err(e) = self else { return self };
|
||||
match e {
|
||||
ShErr::Simple { kind, msg, notes }
|
||||
| ShErr::Full {
|
||||
kind,
|
||||
msg,
|
||||
notes,
|
||||
span: _,
|
||||
} => Err(ShErr::Full {
|
||||
kind: kind.clone(),
|
||||
msg: msg.clone(),
|
||||
notes: notes.clone(),
|
||||
span: new_span,
|
||||
}),
|
||||
}
|
||||
}
|
||||
/// Blame a span if no blame has been assigned yet
|
||||
fn try_blame(self, new_span: Span) -> Self {
|
||||
let Err(e) = &self else { return self };
|
||||
match e {
|
||||
ShErr::Simple { kind, msg, notes } => Err(ShErr::Full {
|
||||
kind: kind.clone(),
|
||||
msg: msg.clone(),
|
||||
notes: notes.clone(),
|
||||
span: new_span,
|
||||
}),
|
||||
ShErr::Full {
|
||||
kind: _,
|
||||
msg: _,
|
||||
span: _,
|
||||
notes: _,
|
||||
} => self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Note {
|
||||
main: String,
|
||||
sub_notes: Vec<Note>,
|
||||
depth: usize
|
||||
main: String,
|
||||
sub_notes: Vec<Note>,
|
||||
depth: usize,
|
||||
}
|
||||
|
||||
impl Note {
|
||||
pub fn new(main: impl Into<String>) -> Self {
|
||||
Self {
|
||||
main: main.into(),
|
||||
sub_notes: vec![],
|
||||
depth: 0
|
||||
}
|
||||
}
|
||||
pub fn new(main: impl Into<String>) -> Self {
|
||||
Self {
|
||||
main: main.into(),
|
||||
sub_notes: vec![],
|
||||
depth: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_sub_notes(self, new_sub_notes: Vec<impl Into<String>>) -> Self {
|
||||
let Self { main, mut sub_notes, depth } = self;
|
||||
for raw_note in new_sub_notes {
|
||||
let mut note = Note::new(raw_note);
|
||||
note.depth = self.depth + 1;
|
||||
sub_notes.push(note);
|
||||
}
|
||||
Self { main, sub_notes, depth }
|
||||
}
|
||||
pub fn with_sub_notes(self, new_sub_notes: Vec<impl Into<String>>) -> Self {
|
||||
let Self {
|
||||
main,
|
||||
mut sub_notes,
|
||||
depth,
|
||||
} = self;
|
||||
for raw_note in new_sub_notes {
|
||||
let mut note = Note::new(raw_note);
|
||||
note.depth = self.depth + 1;
|
||||
sub_notes.push(note);
|
||||
}
|
||||
Self {
|
||||
main,
|
||||
sub_notes,
|
||||
depth,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Note {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let note = "note".styled(Style::Green);
|
||||
let main = &self.main;
|
||||
if self.depth == 0 {
|
||||
writeln!(f, "{note}: {main}")?;
|
||||
} else {
|
||||
let bar_break = "-".styled(Style::Cyan | Style::Bold);
|
||||
let indent = " ".repeat(self.depth);
|
||||
writeln!(f, " {indent}{bar_break} {main}")?;
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let note = "note".styled(Style::Green);
|
||||
let main = &self.main;
|
||||
if self.depth == 0 {
|
||||
writeln!(f, "{note}: {main}")?;
|
||||
} else {
|
||||
let bar_break = "-".styled(Style::Cyan | Style::Bold);
|
||||
let indent = " ".repeat(self.depth);
|
||||
writeln!(f, " {indent}{bar_break} {main}")?;
|
||||
}
|
||||
|
||||
for sub_note in &self.sub_notes {
|
||||
write!(f, "{sub_note}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
for sub_note in &self.sub_notes {
|
||||
write!(f, "{sub_note}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ShErr {
|
||||
Simple { kind: ShErrKind, msg: String, notes: Vec<Note> },
|
||||
Full { kind: ShErrKind, msg: String, notes: Vec<Note>, span: Span }
|
||||
Simple {
|
||||
kind: ShErrKind,
|
||||
msg: String,
|
||||
notes: Vec<Note>,
|
||||
},
|
||||
Full {
|
||||
kind: ShErrKind,
|
||||
msg: String,
|
||||
notes: Vec<Note>,
|
||||
span: Span,
|
||||
},
|
||||
}
|
||||
|
||||
impl ShErr {
|
||||
pub fn simple(kind: ShErrKind, msg: impl Into<String>) -> Self {
|
||||
let msg = msg.into();
|
||||
Self::Simple { kind, msg, notes: vec![] }
|
||||
}
|
||||
pub fn full(kind: ShErrKind, msg: impl Into<String>, span: Span) -> Self {
|
||||
let msg = msg.into();
|
||||
Self::Full { kind, msg, span, notes: vec![] }
|
||||
}
|
||||
pub fn unpack(self) -> (ShErrKind,String,Vec<Note>,Option<Span>) {
|
||||
match self {
|
||||
ShErr::Simple { kind, msg, notes } => (kind,msg,notes,None),
|
||||
ShErr::Full { kind, msg, notes, span } => (kind,msg,notes,Some(span))
|
||||
}
|
||||
}
|
||||
pub fn with_note(self, note: Note) -> Self {
|
||||
let (kind,msg,mut notes,span) = self.unpack();
|
||||
notes.push(note);
|
||||
if let Some(span) = span {
|
||||
Self::Full { kind, msg, notes, span }
|
||||
} else {
|
||||
Self::Simple { kind, msg, notes }
|
||||
}
|
||||
}
|
||||
pub fn with_span(sherr: ShErr, span: Span) -> Self {
|
||||
let (kind,msg,notes,_) = sherr.unpack();
|
||||
Self::Full { kind, msg, notes, span }
|
||||
}
|
||||
pub fn kind(&self) -> &ShErrKind {
|
||||
match self {
|
||||
ShErr::Simple { kind, msg: _, notes: _ } |
|
||||
ShErr::Full { kind, msg: _, notes: _, span: _ } => kind
|
||||
}
|
||||
}
|
||||
pub fn get_window(&self) -> Vec<(usize,String)> {
|
||||
let ShErr::Full { kind: _, msg: _, notes: _, span } = self else {
|
||||
unreachable!()
|
||||
};
|
||||
let mut total_len: usize = 0;
|
||||
let mut total_lines: usize = 1;
|
||||
let mut lines = vec![];
|
||||
let mut cur_line = String::new();
|
||||
pub fn simple(kind: ShErrKind, msg: impl Into<String>) -> Self {
|
||||
let msg = msg.into();
|
||||
Self::Simple {
|
||||
kind,
|
||||
msg,
|
||||
notes: vec![],
|
||||
}
|
||||
}
|
||||
pub fn full(kind: ShErrKind, msg: impl Into<String>, span: Span) -> Self {
|
||||
let msg = msg.into();
|
||||
Self::Full {
|
||||
kind,
|
||||
msg,
|
||||
span,
|
||||
notes: vec![],
|
||||
}
|
||||
}
|
||||
pub fn unpack(self) -> (ShErrKind, String, Vec<Note>, Option<Span>) {
|
||||
match self {
|
||||
ShErr::Simple { kind, msg, notes } => (kind, msg, notes, None),
|
||||
ShErr::Full {
|
||||
kind,
|
||||
msg,
|
||||
notes,
|
||||
span,
|
||||
} => (kind, msg, notes, Some(span)),
|
||||
}
|
||||
}
|
||||
pub fn with_note(self, note: Note) -> Self {
|
||||
let (kind, msg, mut notes, span) = self.unpack();
|
||||
notes.push(note);
|
||||
if let Some(span) = span {
|
||||
Self::Full {
|
||||
kind,
|
||||
msg,
|
||||
notes,
|
||||
span,
|
||||
}
|
||||
} else {
|
||||
Self::Simple { kind, msg, notes }
|
||||
}
|
||||
}
|
||||
pub fn with_span(sherr: ShErr, span: Span) -> Self {
|
||||
let (kind, msg, notes, _) = sherr.unpack();
|
||||
Self::Full {
|
||||
kind,
|
||||
msg,
|
||||
notes,
|
||||
span,
|
||||
}
|
||||
}
|
||||
pub fn kind(&self) -> &ShErrKind {
|
||||
match self {
|
||||
ShErr::Simple {
|
||||
kind,
|
||||
msg: _,
|
||||
notes: _,
|
||||
}
|
||||
| ShErr::Full {
|
||||
kind,
|
||||
msg: _,
|
||||
notes: _,
|
||||
span: _,
|
||||
} => kind,
|
||||
}
|
||||
}
|
||||
pub fn get_window(&self) -> Vec<(usize, String)> {
|
||||
let ShErr::Full {
|
||||
kind: _,
|
||||
msg: _,
|
||||
notes: _,
|
||||
span,
|
||||
} = self
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
let mut total_len: usize = 0;
|
||||
let mut total_lines: usize = 1;
|
||||
let mut lines = vec![];
|
||||
let mut cur_line = String::new();
|
||||
|
||||
let src = span.get_source();
|
||||
let mut chars = src.chars();
|
||||
let src = span.get_source();
|
||||
let mut chars = src.chars();
|
||||
|
||||
while let Some(ch) = chars.next() {
|
||||
total_len += ch.len_utf8();
|
||||
cur_line.push(ch);
|
||||
if ch == '\n' {
|
||||
if total_len > span.start {
|
||||
let line = (
|
||||
total_lines,
|
||||
mem::take(&mut cur_line)
|
||||
);
|
||||
lines.push(line);
|
||||
}
|
||||
if total_len >= span.end {
|
||||
break
|
||||
}
|
||||
total_lines += 1;
|
||||
while let Some(ch) = chars.next() {
|
||||
total_len += ch.len_utf8();
|
||||
cur_line.push(ch);
|
||||
if ch == '\n' {
|
||||
if total_len > span.start {
|
||||
let line = (total_lines, mem::take(&mut cur_line));
|
||||
lines.push(line);
|
||||
}
|
||||
if total_len >= span.end {
|
||||
break;
|
||||
}
|
||||
total_lines += 1;
|
||||
|
||||
cur_line.clear();
|
||||
}
|
||||
}
|
||||
cur_line.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if !cur_line.is_empty() {
|
||||
let line = (
|
||||
total_lines,
|
||||
mem::take(&mut cur_line)
|
||||
);
|
||||
lines.push(line);
|
||||
}
|
||||
if !cur_line.is_empty() {
|
||||
let line = (total_lines, mem::take(&mut cur_line));
|
||||
lines.push(line);
|
||||
}
|
||||
|
||||
lines
|
||||
}
|
||||
pub fn get_line_col(&self) -> (usize,usize) {
|
||||
let ShErr::Full { kind: _, msg: _, notes: _, span } = self else {
|
||||
unreachable!()
|
||||
};
|
||||
lines
|
||||
}
|
||||
pub fn get_line_col(&self) -> (usize, usize) {
|
||||
let ShErr::Full {
|
||||
kind: _,
|
||||
msg: _,
|
||||
notes: _,
|
||||
span,
|
||||
} = self
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let mut lineno = 1;
|
||||
let mut colno = 1;
|
||||
let src = span.get_source();
|
||||
let mut chars = src.chars().enumerate();
|
||||
while let Some((pos,ch)) = chars.next() {
|
||||
if pos >= span.start {
|
||||
break
|
||||
}
|
||||
if ch == '\n' {
|
||||
lineno += 1;
|
||||
colno = 1;
|
||||
} else {
|
||||
colno += 1;
|
||||
}
|
||||
}
|
||||
(lineno,colno)
|
||||
}
|
||||
pub fn get_indicator_lines(&self) -> Option<Vec<String>> {
|
||||
match self {
|
||||
ShErr::Simple { kind: _, msg: _, notes: _ } => None,
|
||||
ShErr::Full { kind: _, msg: _, notes: _, span } => {
|
||||
let text = span.as_str();
|
||||
let lines = text.lines();
|
||||
let mut indicator_lines = vec![];
|
||||
let mut lineno = 1;
|
||||
let mut colno = 1;
|
||||
let src = span.get_source();
|
||||
let mut chars = src.chars().enumerate();
|
||||
while let Some((pos, ch)) = chars.next() {
|
||||
if pos >= span.start {
|
||||
break;
|
||||
}
|
||||
if ch == '\n' {
|
||||
lineno += 1;
|
||||
colno = 1;
|
||||
} else {
|
||||
colno += 1;
|
||||
}
|
||||
}
|
||||
(lineno, colno)
|
||||
}
|
||||
pub fn get_indicator_lines(&self) -> Option<Vec<String>> {
|
||||
match self {
|
||||
ShErr::Simple {
|
||||
kind: _,
|
||||
msg: _,
|
||||
notes: _,
|
||||
} => None,
|
||||
ShErr::Full {
|
||||
kind: _,
|
||||
msg: _,
|
||||
notes: _,
|
||||
span,
|
||||
} => {
|
||||
let text = span.as_str();
|
||||
let lines = text.lines();
|
||||
let mut indicator_lines = vec![];
|
||||
|
||||
for line in lines {
|
||||
let indicator_line = "^".repeat(line.trim().len()).styled(Style::Red | Style::Bold);
|
||||
indicator_lines.push(indicator_line);
|
||||
}
|
||||
for line in lines {
|
||||
let indicator_line = "^"
|
||||
.repeat(line.trim().len())
|
||||
.styled(Style::Red | Style::Bold);
|
||||
indicator_lines.push(indicator_line);
|
||||
}
|
||||
|
||||
Some(indicator_lines)
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(indicator_lines)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ShErr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Simple { msg, kind: _, notes } => {
|
||||
let mut all_strings = vec![msg.to_string()];
|
||||
let mut notes_fmt = vec![];
|
||||
for note in notes {
|
||||
let fmt = format!("{note}");
|
||||
notes_fmt.push(fmt);
|
||||
}
|
||||
all_strings.append(&mut notes_fmt);
|
||||
let mut output = all_strings.join("\n");
|
||||
output.push('\n');
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Simple {
|
||||
msg,
|
||||
kind: _,
|
||||
notes,
|
||||
} => {
|
||||
let mut all_strings = vec![msg.to_string()];
|
||||
let mut notes_fmt = vec![];
|
||||
for note in notes {
|
||||
let fmt = format!("{note}");
|
||||
notes_fmt.push(fmt);
|
||||
}
|
||||
all_strings.append(&mut notes_fmt);
|
||||
let mut output = all_strings.join("\n");
|
||||
output.push('\n');
|
||||
|
||||
writeln!(f, "{}", output)
|
||||
}
|
||||
writeln!(f, "{}", output)
|
||||
}
|
||||
|
||||
Self::Full { msg, kind, notes, span: _ } => {
|
||||
let window = self.get_window();
|
||||
let mut indicator_lines = self.get_indicator_lines().unwrap().into_iter();
|
||||
let mut lineno_pad_count = 0;
|
||||
for (lineno,_) in window.clone() {
|
||||
if lineno.to_string().len() > lineno_pad_count {
|
||||
lineno_pad_count = lineno.to_string().len() + 1
|
||||
}
|
||||
}
|
||||
let padding = " ".repeat(lineno_pad_count);
|
||||
writeln!(f)?;
|
||||
Self::Full {
|
||||
msg,
|
||||
kind,
|
||||
notes,
|
||||
span: _,
|
||||
} => {
|
||||
let window = self.get_window();
|
||||
let mut indicator_lines = self.get_indicator_lines().unwrap().into_iter();
|
||||
let mut lineno_pad_count = 0;
|
||||
for (lineno, _) in window.clone() {
|
||||
if lineno.to_string().len() > lineno_pad_count {
|
||||
lineno_pad_count = lineno.to_string().len() + 1
|
||||
}
|
||||
}
|
||||
let padding = " ".repeat(lineno_pad_count);
|
||||
writeln!(f)?;
|
||||
|
||||
let (line, col) = self.get_line_col();
|
||||
let line_fmt = line.styled(Style::Cyan | Style::Bold);
|
||||
let col_fmt = col.styled(Style::Cyan | Style::Bold);
|
||||
let kind = kind.styled(Style::Red | Style::Bold);
|
||||
let arrow = "->".styled(Style::Cyan | Style::Bold);
|
||||
writeln!(f, "{kind} - {msg}",)?;
|
||||
writeln!(f, "{padding}{arrow} [{line_fmt};{col_fmt}]",)?;
|
||||
|
||||
let (line,col) = self.get_line_col();
|
||||
let line_fmt = line.styled(Style::Cyan | Style::Bold);
|
||||
let col_fmt = col.styled(Style::Cyan | Style::Bold);
|
||||
let kind = kind.styled(Style::Red | Style::Bold);
|
||||
let arrow = "->".styled(Style::Cyan | Style::Bold);
|
||||
writeln!(f,
|
||||
"{kind} - {msg}",
|
||||
)?;
|
||||
writeln!(f,
|
||||
"{padding}{arrow} [{line_fmt};{col_fmt}]",
|
||||
)?;
|
||||
let bar = format!("{padding}|").styled(Style::Cyan | Style::Bold);
|
||||
writeln!(f, "{bar}")?;
|
||||
|
||||
let bar = format!("{padding}|").styled(Style::Cyan | Style::Bold);
|
||||
writeln!(f,"{bar}")?;
|
||||
let mut first_ind_ln = true;
|
||||
for (lineno, line) in window {
|
||||
let lineno = lineno.to_string();
|
||||
let line = line.trim();
|
||||
let mut prefix = format!("{padding}|");
|
||||
prefix.replace_range(0..lineno.len(), &lineno);
|
||||
prefix = prefix.styled(Style::Cyan | Style::Bold);
|
||||
writeln!(f, "{prefix} {line}")?;
|
||||
|
||||
let mut first_ind_ln = true;
|
||||
for (lineno,line) in window {
|
||||
let lineno = lineno.to_string();
|
||||
let line = line.trim();
|
||||
let mut prefix = format!("{padding}|");
|
||||
prefix.replace_range(0..lineno.len(), &lineno);
|
||||
prefix = prefix.styled(Style::Cyan | Style::Bold);
|
||||
writeln!(f,"{prefix} {line}")?;
|
||||
if let Some(ind_ln) = indicator_lines.next() {
|
||||
if first_ind_ln {
|
||||
let ind_ln_padding = " ".repeat(col);
|
||||
let ind_ln = format!("{ind_ln_padding}{ind_ln}");
|
||||
writeln!(f, "{bar}{ind_ln}")?;
|
||||
first_ind_ln = false;
|
||||
} else {
|
||||
writeln!(f, "{bar} {ind_ln}")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ind_ln) = indicator_lines.next() {
|
||||
if first_ind_ln {
|
||||
let ind_ln_padding = " ".repeat(col);
|
||||
let ind_ln = format!("{ind_ln_padding}{ind_ln}");
|
||||
writeln!(f, "{bar}{ind_ln}")?;
|
||||
first_ind_ln = false;
|
||||
} else {
|
||||
writeln!(f, "{bar} {ind_ln}")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
write!(f, "{bar}")?;
|
||||
|
||||
write!(f,"{bar}")?;
|
||||
|
||||
|
||||
let bar_break = "-".styled(Style::Cyan | Style::Bold);
|
||||
if !notes.is_empty() {
|
||||
writeln!(f)?;
|
||||
}
|
||||
for note in notes {
|
||||
|
||||
write!(f,
|
||||
"{padding}{bar_break} {note}"
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
let bar_break = "-".styled(Style::Cyan | Style::Bold);
|
||||
if !notes.is_empty() {
|
||||
writeln!(f)?;
|
||||
}
|
||||
for note in notes {
|
||||
write!(f, "{padding}{bar_break} {note}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for ShErr {
|
||||
fn from(_: std::io::Error) -> Self {
|
||||
let msg = std::io::Error::last_os_error();
|
||||
ShErr::simple(ShErrKind::IoErr, msg.to_string())
|
||||
}
|
||||
fn from(_: std::io::Error) -> Self {
|
||||
let msg = std::io::Error::last_os_error();
|
||||
ShErr::simple(ShErrKind::IoErr, msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::env::VarError> for ShErr {
|
||||
fn from(value: std::env::VarError) -> Self {
|
||||
ShErr::simple(ShErrKind::InternalErr, value.to_string())
|
||||
}
|
||||
fn from(value: std::env::VarError) -> Self {
|
||||
ShErr::simple(ShErrKind::InternalErr, value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Errno> for ShErr {
|
||||
fn from(value: Errno) -> Self {
|
||||
ShErr::simple(ShErrKind::Errno, value.to_string())
|
||||
}
|
||||
fn from(value: Errno) -> Self {
|
||||
ShErr::simple(ShErrKind::Errno, value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ShErrKind {
|
||||
IoErr,
|
||||
SyntaxErr,
|
||||
ParseErr,
|
||||
InternalErr,
|
||||
ExecFail,
|
||||
HistoryReadErr,
|
||||
ResourceLimitExceeded,
|
||||
BadPermission,
|
||||
Errno,
|
||||
FileNotFound(String),
|
||||
CmdNotFound(String),
|
||||
CleanExit(i32),
|
||||
FuncReturn(i32),
|
||||
LoopContinue(i32),
|
||||
LoopBreak(i32),
|
||||
ReadlineErr,
|
||||
Null
|
||||
IoErr,
|
||||
SyntaxErr,
|
||||
ParseErr,
|
||||
InternalErr,
|
||||
ExecFail,
|
||||
HistoryReadErr,
|
||||
ResourceLimitExceeded,
|
||||
BadPermission,
|
||||
Errno,
|
||||
FileNotFound(String),
|
||||
CmdNotFound(String),
|
||||
CleanExit(i32),
|
||||
FuncReturn(i32),
|
||||
LoopContinue(i32),
|
||||
LoopBreak(i32),
|
||||
ReadlineErr,
|
||||
Null,
|
||||
}
|
||||
|
||||
impl Display for ShErrKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let output = match self {
|
||||
Self::IoErr => "I/O Error",
|
||||
Self::SyntaxErr => "Syntax Error",
|
||||
Self::ParseErr => "Parse Error",
|
||||
Self::InternalErr => "Internal Error",
|
||||
Self::HistoryReadErr => "History Parse Error",
|
||||
Self::ExecFail => "Execution Failed",
|
||||
Self::ResourceLimitExceeded => "Resource Limit Exceeded",
|
||||
Self::BadPermission => "Bad Permissions",
|
||||
Self::Errno => "ERRNO",
|
||||
Self::FileNotFound(file) => &format!("File not found: {file}"),
|
||||
Self::CmdNotFound(cmd) => &format!("Command not found: {cmd}"),
|
||||
Self::CleanExit(_) => "",
|
||||
Self::FuncReturn(_) => "",
|
||||
Self::LoopContinue(_) => "",
|
||||
Self::LoopBreak(_) => "",
|
||||
Self::ReadlineErr => "Line Read Error",
|
||||
Self::Null => "",
|
||||
};
|
||||
write!(f,"{output}")
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let output = match self {
|
||||
Self::IoErr => "I/O Error",
|
||||
Self::SyntaxErr => "Syntax Error",
|
||||
Self::ParseErr => "Parse Error",
|
||||
Self::InternalErr => "Internal Error",
|
||||
Self::HistoryReadErr => "History Parse Error",
|
||||
Self::ExecFail => "Execution Failed",
|
||||
Self::ResourceLimitExceeded => "Resource Limit Exceeded",
|
||||
Self::BadPermission => "Bad Permissions",
|
||||
Self::Errno => "ERRNO",
|
||||
Self::FileNotFound(file) => &format!("File not found: {file}"),
|
||||
Self::CmdNotFound(cmd) => &format!("Command not found: {cmd}"),
|
||||
Self::CleanExit(_) => "",
|
||||
Self::FuncReturn(_) => "",
|
||||
Self::LoopContinue(_) => "",
|
||||
Self::LoopBreak(_) => "",
|
||||
Self::ReadlineErr => "Line Read Error",
|
||||
Self::Null => "",
|
||||
};
|
||||
write!(f, "{output}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,55 +2,57 @@ use std::fmt::Display;
|
||||
|
||||
use super::term::{Style, Styled};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Ord, Eq , Debug)]
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum FernLogLevel {
|
||||
NONE = 0,
|
||||
ERROR = 1,
|
||||
WARN = 2,
|
||||
INFO = 3,
|
||||
DEBUG = 4,
|
||||
TRACE = 5
|
||||
NONE = 0,
|
||||
ERROR = 1,
|
||||
WARN = 2,
|
||||
INFO = 3,
|
||||
DEBUG = 4,
|
||||
TRACE = 5,
|
||||
}
|
||||
|
||||
impl Display for FernLogLevel {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use FernLogLevel::*;
|
||||
match self {
|
||||
ERROR => write!(f,"{}","ERROR".styled(Style::Red | Style::Bold)),
|
||||
WARN => write!(f,"{}","WARN".styled(Style::Yellow | Style::Bold)),
|
||||
INFO => write!(f,"{}","INFO".styled(Style::Green | Style::Bold)),
|
||||
DEBUG => write!(f,"{}","DEBUG".styled(Style::Magenta | Style::Bold)),
|
||||
TRACE => write!(f,"{}","TRACE".styled(Style::Blue | Style::Bold)),
|
||||
NONE => write!(f,"")
|
||||
}
|
||||
}
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use FernLogLevel::*;
|
||||
match self {
|
||||
ERROR => write!(f, "{}", "ERROR".styled(Style::Red | Style::Bold)),
|
||||
WARN => write!(f, "{}", "WARN".styled(Style::Yellow | Style::Bold)),
|
||||
INFO => write!(f, "{}", "INFO".styled(Style::Green | Style::Bold)),
|
||||
DEBUG => write!(f, "{}", "DEBUG".styled(Style::Magenta | Style::Bold)),
|
||||
TRACE => write!(f, "{}", "TRACE".styled(Style::Blue | Style::Bold)),
|
||||
NONE => write!(f, ""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn log_level() -> FernLogLevel {
|
||||
use FernLogLevel::*;
|
||||
let level = std::env::var("FERN_LOG_LEVEL").unwrap_or_default();
|
||||
match level.to_lowercase().as_str() {
|
||||
"error" => ERROR,
|
||||
"warn" => WARN,
|
||||
"info" => INFO,
|
||||
"debug" => DEBUG,
|
||||
"trace" => TRACE,
|
||||
_ => NONE
|
||||
}
|
||||
use FernLogLevel::*;
|
||||
let level = std::env::var("FERN_LOG_LEVEL").unwrap_or_default();
|
||||
match level.to_lowercase().as_str() {
|
||||
"error" => ERROR,
|
||||
"warn" => WARN,
|
||||
"info" => INFO,
|
||||
"debug" => DEBUG,
|
||||
"trace" => TRACE,
|
||||
_ => NONE,
|
||||
}
|
||||
}
|
||||
|
||||
/// A structured logging macro designed for `fern`.
|
||||
///
|
||||
/// `flog!` was implemented because `rustyline` uses `env_logger`, which clutters the debug output.
|
||||
/// This macro prints log messages in a structured format, including the log level, filename, and line number.
|
||||
/// `flog!` was implemented because `rustyline` uses `env_logger`, which
|
||||
/// clutters the debug output. This macro prints log messages in a structured
|
||||
/// format, including the log level, filename, and line number.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// The macro supports three types of arguments:
|
||||
///
|
||||
/// ## 1. **Formatted Messages**
|
||||
/// Similar to `println!` or `format!`, allows embedding values inside a formatted string.
|
||||
/// Similar to `println!` or `format!`, allows embedding values inside a
|
||||
/// formatted string.
|
||||
///
|
||||
/// ```rust
|
||||
/// flog!(ERROR, "foo is {}", foo);
|
||||
@@ -73,7 +75,8 @@ pub fn log_level() -> FernLogLevel {
|
||||
/// ```
|
||||
///
|
||||
/// ## 3. **Expressions**
|
||||
/// Logs the evaluated result of each given expression, displaying both the expression and its value.
|
||||
/// Logs the evaluated result of each given expression, displaying both the
|
||||
/// expression and its value.
|
||||
///
|
||||
/// ```rust
|
||||
/// flog!(INFO, 1.min(2));
|
||||
@@ -84,8 +87,10 @@ pub fn log_level() -> FernLogLevel {
|
||||
/// ```
|
||||
///
|
||||
/// # Considerations
|
||||
/// - This macro uses `eprintln!()` internally, so its formatting rules must be followed.
|
||||
/// - **Literals and formatted messages** require arguments that implement [`std::fmt::Display`].
|
||||
/// - This macro uses `eprintln!()` internally, so its formatting rules must be
|
||||
/// followed.
|
||||
/// - **Literals and formatted messages** require arguments that implement
|
||||
/// [`std::fmt::Display`].
|
||||
/// - **Expressions** require arguments that implement [`std::fmt::Debug`].
|
||||
#[macro_export]
|
||||
macro_rules! flog {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
pub mod error;
|
||||
pub mod term;
|
||||
pub mod flog;
|
||||
pub mod sys;
|
||||
pub mod term;
|
||||
pub mod utils;
|
||||
|
||||
109
src/libsh/sys.rs
109
src/libsh/sys.rs
@@ -4,70 +4,91 @@ use crate::{prelude::*, state::write_jobs};
|
||||
///
|
||||
/// The previous state of the terminal options.
|
||||
///
|
||||
/// This variable stores the terminal settings at the start of the program and restores them when the program exits.
|
||||
/// It is initialized exactly once at the start of the program and accessed exactly once at the end of the program.
|
||||
/// It will not be mutated or accessed under any other circumstances.
|
||||
/// This variable stores the terminal settings at the start of the program and
|
||||
/// restores them when the program exits. It is initialized exactly once at the
|
||||
/// start of the program and accessed exactly once at the end of the program. It
|
||||
/// will not be mutated or accessed under any other circumstances.
|
||||
///
|
||||
/// This ended up being necessary because wrapping Termios in a thread-safe way was unreasonably tricky.
|
||||
/// This ended up being necessary because wrapping Termios in a thread-safe way
|
||||
/// was unreasonably tricky.
|
||||
///
|
||||
/// The possible states of this variable are:
|
||||
/// - `None`: The terminal options have not been set yet (before initialization).
|
||||
/// - `Some(None)`: There were no terminal options to save (i.e., no terminal input detected).
|
||||
/// - `Some(Some(Termios))`: The terminal options (as `Termios`) have been saved.
|
||||
/// - `None`: The terminal options have not been set yet (before
|
||||
/// initialization).
|
||||
/// - `Some(None)`: There were no terminal options to save (i.e., no terminal
|
||||
/// input detected).
|
||||
/// - `Some(Some(Termios))`: The terminal options (as `Termios`) have been
|
||||
/// saved.
|
||||
///
|
||||
/// **Important:** This static variable is mutable and accessed via unsafe code. It is only safe to use because:
|
||||
/// - It is set once during program startup and accessed once during program exit.
|
||||
/// **Important:** This static variable is mutable and accessed via unsafe code.
|
||||
/// It is only safe to use because:
|
||||
/// - It is set once during program startup and accessed once during program
|
||||
/// exit.
|
||||
/// - It is not mutated or accessed after the initial setup and final read.
|
||||
///
|
||||
/// **Caution:** Future changes to this code should respect these constraints to ensure safety. Modifying or accessing this variable outside the defined lifecycle could lead to undefined behavior.
|
||||
/// **Caution:** Future changes to this code should respect these constraints to
|
||||
/// ensure safety. Modifying or accessing this variable outside the defined
|
||||
/// lifecycle could lead to undefined behavior.
|
||||
pub(crate) static mut SAVED_TERMIOS: Option<Option<Termios>> = None;
|
||||
|
||||
pub fn save_termios() {
|
||||
unsafe {
|
||||
SAVED_TERMIOS = Some(if isatty(std::io::stdin().as_raw_fd()).unwrap() {
|
||||
let mut termios = termios::tcgetattr(std::io::stdin()).unwrap();
|
||||
termios.local_flags &= !LocalFlags::ECHOCTL;
|
||||
termios::tcsetattr(std::io::stdin(), nix::sys::termios::SetArg::TCSANOW, &termios).unwrap();
|
||||
Some(termios)
|
||||
} else {
|
||||
None
|
||||
});
|
||||
}
|
||||
unsafe {
|
||||
SAVED_TERMIOS = Some(if isatty(std::io::stdin().as_raw_fd()).unwrap() {
|
||||
let mut termios = termios::tcgetattr(std::io::stdin()).unwrap();
|
||||
termios.local_flags &= !LocalFlags::ECHOCTL;
|
||||
termios::tcsetattr(
|
||||
std::io::stdin(),
|
||||
nix::sys::termios::SetArg::TCSANOW,
|
||||
&termios,
|
||||
)
|
||||
.unwrap();
|
||||
Some(termios)
|
||||
} else {
|
||||
None
|
||||
});
|
||||
}
|
||||
}
|
||||
#[allow(static_mut_refs)]
|
||||
///Access the saved termios
|
||||
///
|
||||
///# Safety
|
||||
///This function is unsafe because it accesses a public mutable static value. This function should only ever be called after save_termios() has already been called.
|
||||
///This function is unsafe because it accesses a public mutable static value.
|
||||
/// This function should only ever be called after save_termios() has already
|
||||
/// been called.
|
||||
pub unsafe fn get_saved_termios() -> Option<Termios> {
|
||||
// SAVED_TERMIOS should *only ever* be set once and accessed once
|
||||
// Set at the start of the program, and accessed during the exit of the program to reset the termios.
|
||||
// Do not use this variable anywhere else
|
||||
SAVED_TERMIOS.clone().flatten()
|
||||
// SAVED_TERMIOS should *only ever* be set once and accessed once
|
||||
// Set at the start of the program, and accessed during the exit of the program
|
||||
// to reset the termios. Do not use this variable anywhere else
|
||||
SAVED_TERMIOS.clone().flatten()
|
||||
}
|
||||
|
||||
/// Set termios to not echo control characters, like ^Z for instance
|
||||
pub fn set_termios() {
|
||||
if isatty(std::io::stdin().as_raw_fd()).unwrap() {
|
||||
let mut termios = termios::tcgetattr(std::io::stdin()).unwrap();
|
||||
termios.local_flags &= !LocalFlags::ECHOCTL;
|
||||
termios::tcsetattr(std::io::stdin(), nix::sys::termios::SetArg::TCSANOW, &termios).unwrap();
|
||||
}
|
||||
if isatty(std::io::stdin().as_raw_fd()).unwrap() {
|
||||
let mut termios = termios::tcgetattr(std::io::stdin()).unwrap();
|
||||
termios.local_flags &= !LocalFlags::ECHOCTL;
|
||||
termios::tcsetattr(
|
||||
std::io::stdin(),
|
||||
nix::sys::termios::SetArg::TCSANOW,
|
||||
&termios,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sh_quit(code: i32) -> ! {
|
||||
write_jobs(|j| {
|
||||
for job in j.jobs_mut().iter_mut().flatten() {
|
||||
job.killpg(Signal::SIGTERM).ok();
|
||||
}
|
||||
});
|
||||
if let Some(termios) = unsafe { get_saved_termios() } {
|
||||
termios::tcsetattr(std::io::stdin(), termios::SetArg::TCSANOW, &termios).unwrap();
|
||||
}
|
||||
if code == 0 {
|
||||
eprintln!("exit");
|
||||
} else {
|
||||
eprintln!("exit {code}");
|
||||
}
|
||||
exit(code);
|
||||
write_jobs(|j| {
|
||||
for job in j.jobs_mut().iter_mut().flatten() {
|
||||
job.killpg(Signal::SIGTERM).ok();
|
||||
}
|
||||
});
|
||||
if let Some(termios) = unsafe { get_saved_termios() } {
|
||||
termios::tcsetattr(std::io::stdin(), termios::SetArg::TCSANOW, &termios).unwrap();
|
||||
}
|
||||
if code == 0 {
|
||||
eprintln!("exit");
|
||||
} else {
|
||||
eprintln!("exit {code}");
|
||||
}
|
||||
exit(code);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use std::{fmt::Display, ops::BitOr};
|
||||
|
||||
pub trait Styled: Sized + Display {
|
||||
fn styled<S: Into<StyleSet>>(self, style: S) -> String {
|
||||
let styles: StyleSet = style.into();
|
||||
let reset = Style::Reset;
|
||||
format!("{styles}{self}{reset}")
|
||||
}
|
||||
fn styled<S: Into<StyleSet>>(self, style: S) -> String {
|
||||
let styles: StyleSet = style.into();
|
||||
let reset = Style::Reset;
|
||||
format!("{styles}{self}{reset}")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Display> Styled for T {}
|
||||
@@ -13,157 +13,157 @@ impl<T: Display> Styled for T {}
|
||||
/// Enum representing a single ANSI style
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Style {
|
||||
// Undoes all styles
|
||||
Reset,
|
||||
// Foreground Colors
|
||||
Black,
|
||||
Red,
|
||||
Green,
|
||||
Yellow,
|
||||
Blue,
|
||||
Magenta,
|
||||
Cyan,
|
||||
White,
|
||||
BrightBlack,
|
||||
BrightRed,
|
||||
BrightGreen,
|
||||
BrightYellow,
|
||||
BrightBlue,
|
||||
BrightMagenta,
|
||||
BrightCyan,
|
||||
BrightWhite,
|
||||
RGB(u8, u8, u8), // Custom foreground color
|
||||
// Undoes all styles
|
||||
Reset,
|
||||
// Foreground Colors
|
||||
Black,
|
||||
Red,
|
||||
Green,
|
||||
Yellow,
|
||||
Blue,
|
||||
Magenta,
|
||||
Cyan,
|
||||
White,
|
||||
BrightBlack,
|
||||
BrightRed,
|
||||
BrightGreen,
|
||||
BrightYellow,
|
||||
BrightBlue,
|
||||
BrightMagenta,
|
||||
BrightCyan,
|
||||
BrightWhite,
|
||||
RGB(u8, u8, u8), // Custom foreground color
|
||||
|
||||
// Background Colors
|
||||
BgBlack,
|
||||
BgRed,
|
||||
BgGreen,
|
||||
BgYellow,
|
||||
BgBlue,
|
||||
BgMagenta,
|
||||
BgCyan,
|
||||
BgWhite,
|
||||
BgBrightBlack,
|
||||
BgBrightRed,
|
||||
BgBrightGreen,
|
||||
BgBrightYellow,
|
||||
BgBrightBlue,
|
||||
BgBrightMagenta,
|
||||
BgBrightCyan,
|
||||
BgBrightWhite,
|
||||
BgRGB(u8, u8, u8), // Custom background color
|
||||
// Background Colors
|
||||
BgBlack,
|
||||
BgRed,
|
||||
BgGreen,
|
||||
BgYellow,
|
||||
BgBlue,
|
||||
BgMagenta,
|
||||
BgCyan,
|
||||
BgWhite,
|
||||
BgBrightBlack,
|
||||
BgBrightRed,
|
||||
BgBrightGreen,
|
||||
BgBrightYellow,
|
||||
BgBrightBlue,
|
||||
BgBrightMagenta,
|
||||
BgBrightCyan,
|
||||
BgBrightWhite,
|
||||
BgRGB(u8, u8, u8), // Custom background color
|
||||
|
||||
// Text Attributes
|
||||
Bold,
|
||||
Dim,
|
||||
Italic,
|
||||
Underline,
|
||||
Strikethrough,
|
||||
Reversed,
|
||||
// Text Attributes
|
||||
Bold,
|
||||
Dim,
|
||||
Italic,
|
||||
Underline,
|
||||
Strikethrough,
|
||||
Reversed,
|
||||
}
|
||||
|
||||
impl Display for Style {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Style::Reset => write!(f, "\x1b[0m"),
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Style::Reset => write!(f, "\x1b[0m"),
|
||||
|
||||
// Foreground colors
|
||||
Style::Black => write!(f, "\x1b[30m"),
|
||||
Style::Red => write!(f, "\x1b[31m"),
|
||||
Style::Green => write!(f, "\x1b[32m"),
|
||||
Style::Yellow => write!(f, "\x1b[33m"),
|
||||
Style::Blue => write!(f, "\x1b[34m"),
|
||||
Style::Magenta => write!(f, "\x1b[35m"),
|
||||
Style::Cyan => write!(f, "\x1b[36m"),
|
||||
Style::White => write!(f, "\x1b[37m"),
|
||||
Style::BrightBlack => write!(f, "\x1b[90m"),
|
||||
Style::BrightRed => write!(f, "\x1b[91m"),
|
||||
Style::BrightGreen => write!(f, "\x1b[92m"),
|
||||
Style::BrightYellow => write!(f, "\x1b[93m"),
|
||||
Style::BrightBlue => write!(f, "\x1b[94m"),
|
||||
Style::BrightMagenta => write!(f, "\x1b[95m"),
|
||||
Style::BrightCyan => write!(f, "\x1b[96m"),
|
||||
Style::BrightWhite => write!(f, "\x1b[97m"),
|
||||
Style::RGB(r, g, b) => write!(f, "\x1b[38;2;{r};{g};{b}m"),
|
||||
// Foreground colors
|
||||
Style::Black => write!(f, "\x1b[30m"),
|
||||
Style::Red => write!(f, "\x1b[31m"),
|
||||
Style::Green => write!(f, "\x1b[32m"),
|
||||
Style::Yellow => write!(f, "\x1b[33m"),
|
||||
Style::Blue => write!(f, "\x1b[34m"),
|
||||
Style::Magenta => write!(f, "\x1b[35m"),
|
||||
Style::Cyan => write!(f, "\x1b[36m"),
|
||||
Style::White => write!(f, "\x1b[37m"),
|
||||
Style::BrightBlack => write!(f, "\x1b[90m"),
|
||||
Style::BrightRed => write!(f, "\x1b[91m"),
|
||||
Style::BrightGreen => write!(f, "\x1b[92m"),
|
||||
Style::BrightYellow => write!(f, "\x1b[93m"),
|
||||
Style::BrightBlue => write!(f, "\x1b[94m"),
|
||||
Style::BrightMagenta => write!(f, "\x1b[95m"),
|
||||
Style::BrightCyan => write!(f, "\x1b[96m"),
|
||||
Style::BrightWhite => write!(f, "\x1b[97m"),
|
||||
Style::RGB(r, g, b) => write!(f, "\x1b[38;2;{r};{g};{b}m"),
|
||||
|
||||
// Background colors
|
||||
Style::BgBlack => write!(f, "\x1b[40m"),
|
||||
Style::BgRed => write!(f, "\x1b[41m"),
|
||||
Style::BgGreen => write!(f, "\x1b[42m"),
|
||||
Style::BgYellow => write!(f, "\x1b[43m"),
|
||||
Style::BgBlue => write!(f, "\x1b[44m"),
|
||||
Style::BgMagenta => write!(f, "\x1b[45m"),
|
||||
Style::BgCyan => write!(f, "\x1b[46m"),
|
||||
Style::BgWhite => write!(f, "\x1b[47m"),
|
||||
Style::BgBrightBlack => write!(f, "\x1b[100m"),
|
||||
Style::BgBrightRed => write!(f, "\x1b[101m"),
|
||||
Style::BgBrightGreen => write!(f, "\x1b[102m"),
|
||||
Style::BgBrightYellow => write!(f, "\x1b[103m"),
|
||||
Style::BgBrightBlue => write!(f, "\x1b[104m"),
|
||||
Style::BgBrightMagenta => write!(f, "\x1b[105m"),
|
||||
Style::BgBrightCyan => write!(f, "\x1b[106m"),
|
||||
Style::BgBrightWhite => write!(f, "\x1b[107m"),
|
||||
Style::BgRGB(r, g, b) => write!(f, "\x1b[48;2;{r};{g};{b}m"),
|
||||
// Background colors
|
||||
Style::BgBlack => write!(f, "\x1b[40m"),
|
||||
Style::BgRed => write!(f, "\x1b[41m"),
|
||||
Style::BgGreen => write!(f, "\x1b[42m"),
|
||||
Style::BgYellow => write!(f, "\x1b[43m"),
|
||||
Style::BgBlue => write!(f, "\x1b[44m"),
|
||||
Style::BgMagenta => write!(f, "\x1b[45m"),
|
||||
Style::BgCyan => write!(f, "\x1b[46m"),
|
||||
Style::BgWhite => write!(f, "\x1b[47m"),
|
||||
Style::BgBrightBlack => write!(f, "\x1b[100m"),
|
||||
Style::BgBrightRed => write!(f, "\x1b[101m"),
|
||||
Style::BgBrightGreen => write!(f, "\x1b[102m"),
|
||||
Style::BgBrightYellow => write!(f, "\x1b[103m"),
|
||||
Style::BgBrightBlue => write!(f, "\x1b[104m"),
|
||||
Style::BgBrightMagenta => write!(f, "\x1b[105m"),
|
||||
Style::BgBrightCyan => write!(f, "\x1b[106m"),
|
||||
Style::BgBrightWhite => write!(f, "\x1b[107m"),
|
||||
Style::BgRGB(r, g, b) => write!(f, "\x1b[48;2;{r};{g};{b}m"),
|
||||
|
||||
// Text attributes
|
||||
Style::Bold => write!(f, "\x1b[1m"),
|
||||
Style::Dim => write!(f, "\x1b[2m"), // New
|
||||
Style::Italic => write!(f, "\x1b[3m"),
|
||||
Style::Underline => write!(f, "\x1b[4m"),
|
||||
Style::Strikethrough => write!(f, "\x1b[9m"), // New
|
||||
Style::Reversed => write!(f, "\x1b[7m"),
|
||||
}
|
||||
}
|
||||
// Text attributes
|
||||
Style::Bold => write!(f, "\x1b[1m"),
|
||||
Style::Dim => write!(f, "\x1b[2m"), // New
|
||||
Style::Italic => write!(f, "\x1b[3m"),
|
||||
Style::Underline => write!(f, "\x1b[4m"),
|
||||
Style::Strikethrough => write!(f, "\x1b[9m"), // New
|
||||
Style::Reversed => write!(f, "\x1b[7m"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct representing a **set** of styles
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct StyleSet {
|
||||
styles: Vec<Style>,
|
||||
styles: Vec<Style>,
|
||||
}
|
||||
|
||||
impl StyleSet {
|
||||
pub fn new() -> Self {
|
||||
Self { styles: vec![] }
|
||||
}
|
||||
pub fn new() -> Self {
|
||||
Self { styles: vec![] }
|
||||
}
|
||||
|
||||
pub fn add_style(mut self, style: Style) -> Self {
|
||||
if !self.styles.contains(&style) {
|
||||
self.styles.push(style);
|
||||
}
|
||||
self
|
||||
}
|
||||
pub fn add_style(mut self, style: Style) -> Self {
|
||||
if !self.styles.contains(&style) {
|
||||
self.styles.push(style);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for StyleSet {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for style in &self.styles {
|
||||
style.fmt(f)?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for style in &self.styles {
|
||||
style.fmt(f)?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Allow OR (`|`) operator to combine multiple `Style` values into a `StyleSet`
|
||||
impl BitOr for Style {
|
||||
type Output = StyleSet;
|
||||
type Output = StyleSet;
|
||||
|
||||
fn bitor(self, rhs: Self) -> Self::Output {
|
||||
StyleSet::new().add_style(self).add_style(rhs)
|
||||
}
|
||||
fn bitor(self, rhs: Self) -> Self::Output {
|
||||
StyleSet::new().add_style(self).add_style(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
/// Allow OR (`|`) operator to combine `StyleSet` with `Style`
|
||||
impl BitOr<Style> for StyleSet {
|
||||
type Output = StyleSet;
|
||||
type Output = StyleSet;
|
||||
|
||||
fn bitor(self, rhs: Style) -> Self::Output {
|
||||
self.add_style(rhs)
|
||||
}
|
||||
fn bitor(self, rhs: Style) -> Self::Output {
|
||||
self.add_style(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Style> for StyleSet {
|
||||
fn from(style: Style) -> Self {
|
||||
StyleSet::new().add_style(style)
|
||||
}
|
||||
fn from(style: Style) -> Self {
|
||||
StyleSet::new().add_style(style)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,107 +5,105 @@ use crate::parse::{Redir, RedirType};
|
||||
use crate::prelude::*;
|
||||
|
||||
pub trait VecDequeExt<T> {
|
||||
fn to_vec(self) -> Vec<T>;
|
||||
fn to_vec(self) -> Vec<T>;
|
||||
}
|
||||
|
||||
pub trait CharDequeUtils {
|
||||
fn to_string(self) -> String;
|
||||
fn ends_with(&self, pat: &str) -> bool;
|
||||
fn starts_with(&self, pat: &str) -> bool;
|
||||
fn to_string(self) -> String;
|
||||
fn ends_with(&self, pat: &str) -> bool;
|
||||
fn starts_with(&self, pat: &str) -> bool;
|
||||
}
|
||||
|
||||
pub trait TkVecUtils<Tk> {
|
||||
fn get_span(&self) -> Option<Span>;
|
||||
fn debug_tokens(&self);
|
||||
fn get_span(&self) -> Option<Span>;
|
||||
fn debug_tokens(&self);
|
||||
}
|
||||
|
||||
pub trait RedirVecUtils<Redir> {
|
||||
/// Splits the vector of redirections into two vectors
|
||||
///
|
||||
/// One vector contains input redirs, the other contains output redirs
|
||||
fn split_by_channel(self) -> (Vec<Redir>,Vec<Redir>);
|
||||
/// Splits the vector of redirections into two vectors
|
||||
///
|
||||
/// One vector contains input redirs, the other contains output redirs
|
||||
fn split_by_channel(self) -> (Vec<Redir>, Vec<Redir>);
|
||||
}
|
||||
|
||||
impl<T> VecDequeExt<T> for VecDeque<T> {
|
||||
fn to_vec(self) -> Vec<T> {
|
||||
self.into_iter().collect::<Vec<T>>()
|
||||
}
|
||||
fn to_vec(self) -> Vec<T> {
|
||||
self.into_iter().collect::<Vec<T>>()
|
||||
}
|
||||
}
|
||||
|
||||
impl CharDequeUtils for VecDeque<char> {
|
||||
fn to_string(mut self) -> String {
|
||||
let mut result = String::with_capacity(self.len());
|
||||
while let Some(ch) = self.pop_front() {
|
||||
result.push(ch);
|
||||
}
|
||||
result
|
||||
}
|
||||
fn to_string(mut self) -> String {
|
||||
let mut result = String::with_capacity(self.len());
|
||||
while let Some(ch) = self.pop_front() {
|
||||
result.push(ch);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn ends_with(&self, pat: &str) -> bool {
|
||||
let pat_chars = pat.chars();
|
||||
let self_len = self.len();
|
||||
fn ends_with(&self, pat: &str) -> bool {
|
||||
let pat_chars = pat.chars();
|
||||
let self_len = self.len();
|
||||
|
||||
// If pattern is longer than self, return false
|
||||
if pat_chars.clone().count() > self_len {
|
||||
return false;
|
||||
}
|
||||
// If pattern is longer than self, return false
|
||||
if pat_chars.clone().count() > self_len {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare from the back
|
||||
self.iter().rev().zip(pat_chars.rev()).all(|(c1, c2)| c1 == &c2)
|
||||
}
|
||||
// Compare from the back
|
||||
self
|
||||
.iter()
|
||||
.rev()
|
||||
.zip(pat_chars.rev())
|
||||
.all(|(c1, c2)| c1 == &c2)
|
||||
}
|
||||
|
||||
fn starts_with(&self, pat: &str) -> bool {
|
||||
let pat_chars = pat.chars();
|
||||
let self_len = self.len();
|
||||
fn starts_with(&self, pat: &str) -> bool {
|
||||
let pat_chars = pat.chars();
|
||||
let self_len = self.len();
|
||||
|
||||
// If pattern is longer than self, return false
|
||||
if pat_chars.clone().count() > self_len {
|
||||
return false;
|
||||
}
|
||||
// If pattern is longer than self, return false
|
||||
if pat_chars.clone().count() > self_len {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare from the front
|
||||
self.iter().zip(pat_chars).all(|(c1, c2)| c1 == &c2)
|
||||
}
|
||||
// Compare from the front
|
||||
self.iter().zip(pat_chars).all(|(c1, c2)| c1 == &c2)
|
||||
}
|
||||
}
|
||||
|
||||
impl TkVecUtils<Tk> for Vec<Tk> {
|
||||
fn get_span(&self) -> Option<Span> {
|
||||
if let Some(first_tk) = self.first() {
|
||||
self.last().map(|last_tk| {
|
||||
Span::new(
|
||||
first_tk.span.start..last_tk.span.end,
|
||||
first_tk.source()
|
||||
)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn debug_tokens(&self) {
|
||||
for token in self {
|
||||
flog!(DEBUG, "token: {}",token)
|
||||
}
|
||||
}
|
||||
fn get_span(&self) -> Option<Span> {
|
||||
if let Some(first_tk) = self.first() {
|
||||
self
|
||||
.last()
|
||||
.map(|last_tk| Span::new(first_tk.span.start..last_tk.span.end, first_tk.source()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn debug_tokens(&self) {
|
||||
for token in self {
|
||||
flog!(DEBUG, "token: {}", token)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RedirVecUtils<Redir> for Vec<Redir> {
|
||||
fn split_by_channel(self) -> (Vec<Redir>,Vec<Redir>) {
|
||||
let mut input = vec![];
|
||||
let mut output = vec![];
|
||||
for redir in self {
|
||||
match redir.class {
|
||||
RedirType::Input => input.push(redir),
|
||||
RedirType::Pipe => {
|
||||
match redir.io_mode.tgt_fd() {
|
||||
STDIN_FILENO => input.push(redir),
|
||||
STDOUT_FILENO |
|
||||
STDERR_FILENO => output.push(redir),
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
_ => output.push(redir)
|
||||
}
|
||||
}
|
||||
(input,output)
|
||||
}
|
||||
fn split_by_channel(self) -> (Vec<Redir>, Vec<Redir>) {
|
||||
let mut input = vec![];
|
||||
let mut output = vec![];
|
||||
for redir in self {
|
||||
match redir.class {
|
||||
RedirType::Input => input.push(redir),
|
||||
RedirType::Pipe => match redir.io_mode.tgt_fd() {
|
||||
STDIN_FILENO => input.push(redir),
|
||||
STDOUT_FILENO | STDERR_FILENO => output.push(redir),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => output.push(redir),
|
||||
}
|
||||
}
|
||||
(input, output)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user