Hello world!
This commit is contained in:
commit
76c771b747
4 changed files with 534 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
name = "poezio"
|
||||||
|
version = "0.15.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
chrono = { version = "0.4.23", default-features = false, features = ["clock"] }
|
||||||
|
crossterm = { version = "0.26", default-features = false }
|
||||||
|
image = { version = "0.24", default-features = false, features = ["png", "jpeg"] }
|
||||||
|
nom = { version = "7.1.3", default-features = false, features = ["alloc"] }
|
||||||
|
tokio = { version = "1.25", default-features = false, features = ["rt", "rt-multi-thread"] }
|
||||||
|
unicode-width = { version = "0.1.10", default-features = false }
|
230
src/logger.rs
Normal file
230
src/logger.rs
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
|
use nom;
|
||||||
|
use nom::{
|
||||||
|
bytes::complete::{tag, take, take_until},
|
||||||
|
combinator::{map, map_res},
|
||||||
|
multi::many_m_n,
|
||||||
|
sequence::tuple,
|
||||||
|
IResult,
|
||||||
|
};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
pub trait LogItem {
|
||||||
|
fn get_time(&self) -> &DateTime<Utc>;
|
||||||
|
fn get_message(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct LogInfo<'a> {
|
||||||
|
pub time: DateTime<Utc>,
|
||||||
|
pub message: Vec<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LogItem for LogInfo<'a> {
|
||||||
|
fn get_time(&self) -> &DateTime<Utc> {
|
||||||
|
&self.time
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_message(&self) -> String {
|
||||||
|
self.message.join("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct LogMessage<'a> {
|
||||||
|
pub time: DateTime<Utc>,
|
||||||
|
pub nick: &'a str,
|
||||||
|
pub message: Vec<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LogMessage<'a> {
|
||||||
|
pub fn get_nick(&self) -> &str {
|
||||||
|
self.nick
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LogItem for LogMessage<'a> {
|
||||||
|
fn get_time(&self) -> &DateTime<Utc> {
|
||||||
|
&self.time
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_message(&self) -> String {
|
||||||
|
self.message.join("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_datetime(i: &str) -> IResult<&str, DateTime<Utc>> {
|
||||||
|
let (i, (year, month, day, _, hour, _, minute, _, second, _)) = tuple((
|
||||||
|
map_res(take(4usize), i32::from_str),
|
||||||
|
map_res(take(2usize), u32::from_str),
|
||||||
|
map_res(take(2usize), u32::from_str),
|
||||||
|
tag("T"),
|
||||||
|
map_res(take(2usize), u32::from_str),
|
||||||
|
tag(":"),
|
||||||
|
map_res(take(2usize), u32::from_str),
|
||||||
|
tag(":"),
|
||||||
|
map_res(take(2usize), u32::from_str),
|
||||||
|
tag("Z"),
|
||||||
|
))(i)?;
|
||||||
|
Ok((
|
||||||
|
i,
|
||||||
|
Utc.with_ymd_and_hms(year, month, day, hour, minute, second)
|
||||||
|
.unwrap(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_log_info(i: &str) -> IResult<&str, LogInfo> {
|
||||||
|
let (i, (_, time, _, nb_lines)) = tuple((
|
||||||
|
tag("MI "),
|
||||||
|
parse_datetime,
|
||||||
|
tag(" "),
|
||||||
|
map_res(take(3usize), usize::from_str),
|
||||||
|
))(i)?;
|
||||||
|
let (i, message) = many_m_n(
|
||||||
|
nb_lines + 1,
|
||||||
|
nb_lines + 1,
|
||||||
|
map(
|
||||||
|
tuple((tag(" "), take_until("\n"), tag("\n"))),
|
||||||
|
|(_, line, _)| line,
|
||||||
|
),
|
||||||
|
)(i)?;
|
||||||
|
Ok((i, LogInfo { time, message }))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_log_message(i: &str) -> IResult<&str, LogMessage> {
|
||||||
|
let (i, (_, time, _, nb_lines, _, nick, _, line0, _)) = tuple((
|
||||||
|
tag("MR "),
|
||||||
|
parse_datetime,
|
||||||
|
tag(" "),
|
||||||
|
map_res(take(3usize), usize::from_str),
|
||||||
|
tag(" <"),
|
||||||
|
take_until("> "),
|
||||||
|
tag("> "),
|
||||||
|
take_until("\n"),
|
||||||
|
tag("\n"),
|
||||||
|
))(i)?;
|
||||||
|
let (i, lines) = many_m_n(
|
||||||
|
nb_lines,
|
||||||
|
nb_lines,
|
||||||
|
map(
|
||||||
|
tuple((tag(" "), take_until("\n"), tag("\n"))),
|
||||||
|
|(_, line, _)| line,
|
||||||
|
),
|
||||||
|
)(i)?;
|
||||||
|
Ok((
|
||||||
|
i,
|
||||||
|
LogMessage {
|
||||||
|
time,
|
||||||
|
nick,
|
||||||
|
message: {
|
||||||
|
let mut message = lines;
|
||||||
|
message.insert(0, line0);
|
||||||
|
message
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum Item<'a> {
|
||||||
|
Message(LogMessage<'a>),
|
||||||
|
Info(LogInfo<'a>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_logs(mut logs: &str) -> IResult<&str, Vec<Item>> {
|
||||||
|
let mut items = vec![];
|
||||||
|
loop {
|
||||||
|
if logs.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if logs.starts_with("MR ") {
|
||||||
|
let message = parse_log_message(logs)?;
|
||||||
|
logs = message.0;
|
||||||
|
items.push(Item::Message(message.1));
|
||||||
|
} else if logs.starts_with("MI ") {
|
||||||
|
let info = parse_log_info(logs)?;
|
||||||
|
logs = info.0;
|
||||||
|
items.push(Item::Info(info.1));
|
||||||
|
} else {
|
||||||
|
return Err(nom::Err::Error(nom::error::Error::new(
|
||||||
|
logs,
|
||||||
|
nom::error::ErrorKind::Fail,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((logs, items))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_message() {
|
||||||
|
let log = "MR 20181016T14:10:08Z 000 <Link Mauve> Hello world!\n";
|
||||||
|
let message = LogMessage {
|
||||||
|
time: "2018-10-16T16:10:08+0200".parse().unwrap(),
|
||||||
|
nick: "Link Mauve",
|
||||||
|
message: vec!["Hello world!"],
|
||||||
|
};
|
||||||
|
let (_, message2) = parse_log_message(log).unwrap();
|
||||||
|
assert_eq!(message, message2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_messages() {
|
||||||
|
let log = "MR 20181016T14:10:08Z 000 <Link Mauve> Hello…\nMR 20181016T14:10:11Z 000 <Link Mauve> world!\n";
|
||||||
|
let messages = [
|
||||||
|
LogMessage {
|
||||||
|
time: "2018-10-16T16:10:08+0200".parse().unwrap(),
|
||||||
|
nick: "Link Mauve",
|
||||||
|
message: vec!["Hello…"],
|
||||||
|
},
|
||||||
|
LogMessage {
|
||||||
|
time: "2018-10-16T16:10:11+0200".parse().unwrap(),
|
||||||
|
nick: "Link Mauve",
|
||||||
|
message: vec!["world!"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let (i, message1) = parse_log_message(log).unwrap();
|
||||||
|
let (_, message2) = parse_log_message(i).unwrap();
|
||||||
|
assert_eq!(messages, [message1, message2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_all_logs() {
|
||||||
|
let log = "MR 20181016T14:10:08Z 000 <Link Mauve> Hello…\nMR 20181016T14:10:11Z 000 <Link Mauve> world!\n";
|
||||||
|
let messages = vec![
|
||||||
|
Item::Message(LogMessage {
|
||||||
|
time: "2018-10-16T16:10:08+0200".parse().unwrap(),
|
||||||
|
nick: "Link Mauve",
|
||||||
|
message: vec!["Hello…"],
|
||||||
|
}),
|
||||||
|
Item::Message(LogMessage {
|
||||||
|
time: "2018-10-16T16:10:11+0200".parse().unwrap(),
|
||||||
|
nick: "Link Mauve",
|
||||||
|
message: vec!["world!"],
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
let (_, messages1) = parse_logs(log).unwrap();
|
||||||
|
assert_eq!(messages, messages1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trailing_characters() {
|
||||||
|
let log = "MR 20181016T14:10:08Z 000 <Link Mauve> Hello…\nMR 20181016T14:10:11Z 000 <Link Mauve> world!\n\n";
|
||||||
|
parse_logs(log).unwrap_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiline_message() {
|
||||||
|
let log = "MR 20181016T14:10:08Z 001 <Link Mauve> Hello…\n world!\n";
|
||||||
|
let message = LogMessage {
|
||||||
|
time: "2018-10-16T16:10:08+0200".parse().unwrap(),
|
||||||
|
nick: "Link Mauve",
|
||||||
|
message: vec!["Hello…", "world!"],
|
||||||
|
};
|
||||||
|
let (_, message2) = parse_log_message(log).unwrap();
|
||||||
|
assert_eq!(message, message2);
|
||||||
|
}
|
||||||
|
}
|
289
src/main.rs
Normal file
289
src/main.rs
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
use crossterm::{
|
||||||
|
cursor,
|
||||||
|
event::{poll, read, Event, KeyCode, KeyEvent, KeyModifiers},
|
||||||
|
style::{self, Color, Stylize},
|
||||||
|
terminal, ExecutableCommand, QueueableCommand,
|
||||||
|
};
|
||||||
|
use image::imageops::FilterType;
|
||||||
|
use std::io::{self, stdout, Read, Write};
|
||||||
|
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
|
||||||
|
|
||||||
|
mod logger;
|
||||||
|
|
||||||
|
fn render_image(image: image::DynamicImage, width: u32, height: u32) -> io::Result<()> {
|
||||||
|
let image = image
|
||||||
|
.resize(width, height, FilterType::Triangle)
|
||||||
|
.into_rgb8();
|
||||||
|
let (width, height) = image.dimensions();
|
||||||
|
let mut stdout = stdout();
|
||||||
|
for y in (0..height).step_by(2) {
|
||||||
|
stdout.queue(cursor::MoveTo(0, y as u16 / 2))?;
|
||||||
|
for x in 0..width {
|
||||||
|
let image::Rgb([r, g, b]) = *image.get_pixel(x, y);
|
||||||
|
let top = Color::Rgb { r, g, b };
|
||||||
|
let image::Rgb([r, g, b]) = *image.get_pixel(x, y + 1);
|
||||||
|
let bottom = Color::Rgb { r, g, b };
|
||||||
|
stdout.queue(style::PrintStyledContent("▄".with(bottom).on(top)))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Terminal {
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Terminal {
|
||||||
|
fn new() -> Terminal {
|
||||||
|
let (width, height) = terminal::size().unwrap();
|
||||||
|
Terminal {
|
||||||
|
width: width as usize,
|
||||||
|
height: height as usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ChatTab<'a> {
|
||||||
|
jid: String,
|
||||||
|
logs: Vec<logger::Item<'a>>,
|
||||||
|
input: Input,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ChatTab<'a> {
|
||||||
|
fn do_redraw(&self, term: &Terminal) -> io::Result<()> {
|
||||||
|
let today = chrono::offset::Utc::now().date_naive();
|
||||||
|
let start_x = 0;
|
||||||
|
let mut start_y = 8;
|
||||||
|
let mut stdout = stdout();
|
||||||
|
for line in self.logs.iter() {
|
||||||
|
stdout.queue(cursor::MoveTo(start_x as u16, start_y))?;
|
||||||
|
match line {
|
||||||
|
logger::Item::Message(logger::LogMessage {
|
||||||
|
time,
|
||||||
|
nick,
|
||||||
|
message,
|
||||||
|
}) => {
|
||||||
|
let str_time = time
|
||||||
|
.format(if time.date_naive() == today {
|
||||||
|
"%H:%M:%S "
|
||||||
|
} else {
|
||||||
|
"%Y-%m-%d %H:%M:%S "
|
||||||
|
})
|
||||||
|
.to_string();
|
||||||
|
let left_pad = start_x + str_time.as_str().width() + nick.width() + 2;
|
||||||
|
stdout
|
||||||
|
.queue(style::Print(str_time))?
|
||||||
|
.queue(style::PrintStyledContent(nick.with(Color::DarkMagenta)))?
|
||||||
|
.queue(style::Print("> "))?;
|
||||||
|
for line in message {
|
||||||
|
let max = term.width - left_pad;
|
||||||
|
if line.width() <= max as usize {
|
||||||
|
// Happy path, the line fits in the terminal.
|
||||||
|
stdout
|
||||||
|
.queue(cursor::MoveTo(left_pad as u16, start_y))?
|
||||||
|
.queue(style::Print(line))?;
|
||||||
|
} else {
|
||||||
|
// TODO: cut text.
|
||||||
|
stdout.queue(style::PrintStyledContent("tl;dr".with(Color::Red)))?;
|
||||||
|
}
|
||||||
|
start_y += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: do something maybe?
|
||||||
|
logger::Item::Info(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let background = style::Colors {
|
||||||
|
foreground: None,
|
||||||
|
background: Some(Color::DarkGrey),
|
||||||
|
};
|
||||||
|
stdout
|
||||||
|
.queue(cursor::MoveTo(0, 51))?
|
||||||
|
.queue(style::SetColors(background))?
|
||||||
|
.queue(style::Print("["))?
|
||||||
|
.queue(style::PrintStyledContent(
|
||||||
|
self.jid.as_str().with(Color::Green),
|
||||||
|
))?
|
||||||
|
.queue(style::Print("] ["))?
|
||||||
|
.queue(style::PrintStyledContent(
|
||||||
|
"|".with(Color::Blue).on(Color::Reset),
|
||||||
|
))?
|
||||||
|
.queue(style::SetColors(background))?
|
||||||
|
.queue(style::Print("]"))?;
|
||||||
|
stdout.flush()?;
|
||||||
|
print!("[K");
|
||||||
|
stdout
|
||||||
|
.queue(style::ResetColor)?
|
||||||
|
.queue(cursor::MoveTo(0, 52))?
|
||||||
|
.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_events(&mut self, term: &mut Terminal) -> io::Result<()> {
|
||||||
|
let mut stdout = stdout();
|
||||||
|
loop {
|
||||||
|
if poll(core::time::Duration::from_secs(1))? {
|
||||||
|
let event = read()?;
|
||||||
|
match event {
|
||||||
|
Event::FocusGained => print!("FocusGained"),
|
||||||
|
Event::FocusLost => print!("FocusLost"),
|
||||||
|
Event::Key(event) => {
|
||||||
|
match self.input.handle_events(event) {
|
||||||
|
Ok(MyEvent::Exit) => break,
|
||||||
|
foo => foo?,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Event::Mouse(event) => print!("{:?}", event),
|
||||||
|
//Event::Paste(data) => print!("{:?}", data),
|
||||||
|
Event::Resize(width, height) => {
|
||||||
|
term.width = width as usize;
|
||||||
|
term.height = height as usize;
|
||||||
|
stdout.queue(terminal::Clear(terminal::ClearType::All))?;
|
||||||
|
self.do_redraw(&term)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MyEvent {
|
||||||
|
None,
|
||||||
|
Exit,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Input {
|
||||||
|
string: Vec<char>,
|
||||||
|
cursor: usize,
|
||||||
|
view: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Input {
|
||||||
|
fn new() -> Input {
|
||||||
|
Input {
|
||||||
|
string: Vec::new(),
|
||||||
|
cursor: 0,
|
||||||
|
view: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn char_len(&self) -> u16 {
|
||||||
|
self.string[self.cursor].width().unwrap() as u16
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_events(&mut self, event: KeyEvent) -> io::Result<MyEvent> {
|
||||||
|
let mut stdout = stdout();
|
||||||
|
let KeyEvent {
|
||||||
|
code,
|
||||||
|
modifiers,
|
||||||
|
..
|
||||||
|
} = event;
|
||||||
|
match (code, modifiers) {
|
||||||
|
(KeyCode::Esc, _) => return Ok(MyEvent::Exit),
|
||||||
|
(KeyCode::Char('d'), KeyModifiers::CONTROL) => return Ok(MyEvent::Exit),
|
||||||
|
(KeyCode::Left, KeyModifiers::NONE) => {
|
||||||
|
if self.cursor != 0 {
|
||||||
|
self.cursor -= 1;
|
||||||
|
let len = self.char_len();
|
||||||
|
stdout.execute(cursor::MoveLeft(len))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(KeyCode::Left, KeyModifiers::CONTROL) => {
|
||||||
|
let separators = [' ', '\n']; // TODO: add punctuation.
|
||||||
|
while self.cursor != 0 && separators.contains(&self.string[self.cursor - 1]) {
|
||||||
|
self.cursor -= 1;
|
||||||
|
let len = self.char_len();
|
||||||
|
stdout.queue(cursor::MoveLeft(len))?;
|
||||||
|
}
|
||||||
|
while self.cursor != 0 && !separators.contains(&self.string[self.cursor - 1]) {
|
||||||
|
self.cursor -= 1;
|
||||||
|
let len = self.char_len();
|
||||||
|
stdout.queue(cursor::MoveLeft(len))?;
|
||||||
|
}
|
||||||
|
stdout.flush()?;
|
||||||
|
}
|
||||||
|
(KeyCode::Right, KeyModifiers::NONE) => {
|
||||||
|
if self.cursor < self.string.len() {
|
||||||
|
let len = self.char_len();
|
||||||
|
self.cursor += 1;
|
||||||
|
stdout.execute(cursor::MoveRight(len))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(KeyCode::Right, KeyModifiers::CONTROL) => {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
(KeyCode::Home, KeyModifiers::NONE) => {
|
||||||
|
self.cursor = 0;
|
||||||
|
stdout.execute(cursor::MoveToColumn(0))?;
|
||||||
|
}
|
||||||
|
(KeyCode::End, KeyModifiers::NONE) => {
|
||||||
|
self.cursor = self.string.len();
|
||||||
|
stdout.execute(cursor::MoveToColumn(self.cursor as u16))?;
|
||||||
|
}
|
||||||
|
(KeyCode::Backspace, _) => {
|
||||||
|
if let Some(c) = self.string.pop() {
|
||||||
|
let len = c.width().unwrap() as u16;
|
||||||
|
self.cursor -= 1;
|
||||||
|
stdout
|
||||||
|
.queue(cursor::MoveLeft(len))?
|
||||||
|
.queue(style::Print(' '))?
|
||||||
|
.queue(cursor::MoveLeft(1))?
|
||||||
|
.flush()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(KeyCode::Char(c), KeyModifiers::NONE) | (KeyCode::Char(c), KeyModifiers::SHIFT) => {
|
||||||
|
self.string.insert(self.cursor, c);
|
||||||
|
stdout
|
||||||
|
.queue(terminal::Clear(terminal::ClearType::UntilNewLine))?
|
||||||
|
.queue(style::Print(
|
||||||
|
&self.string[self.cursor..].iter().collect::<String>(),
|
||||||
|
))?
|
||||||
|
.flush()?;
|
||||||
|
self.cursor += 1;
|
||||||
|
}
|
||||||
|
(KeyCode::Enter, _) => println!("{}", self.string.iter().collect::<String>()),
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
Ok(MyEvent::None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_main() -> io::Result<()> {
|
||||||
|
terminal::enable_raw_mode()?;
|
||||||
|
let mut term = Terminal::new();
|
||||||
|
let mut stdout = stdout();
|
||||||
|
stdout
|
||||||
|
.queue(terminal::SetTitle("poezio"))?
|
||||||
|
.queue(terminal::DisableLineWrap)?
|
||||||
|
.queue(terminal::Clear(terminal::ClearType::All))?;
|
||||||
|
let image = image::open("/home/linkmauve/avatar.png").unwrap();
|
||||||
|
render_image(image, 16, 16)?;
|
||||||
|
let mut file = std::fs::File::open("/home/linkmauve/logs.log")?;
|
||||||
|
let mut data = String::new();
|
||||||
|
file.read_to_string(&mut data)?;
|
||||||
|
let logs = logger::parse_logs(&data).unwrap().1;
|
||||||
|
let jid = String::from("linkmauve@jabberfr.org");
|
||||||
|
let input = Input::new();
|
||||||
|
let mut tab = ChatTab { jid, logs, input };
|
||||||
|
tab.do_redraw(&term)?;
|
||||||
|
stdout.queue(cursor::MoveTo(0, 52))?;
|
||||||
|
stdout.flush()?;
|
||||||
|
|
||||||
|
tab.handle_events(&mut term)?;
|
||||||
|
|
||||||
|
terminal::disable_raw_mode()?;
|
||||||
|
stdout.execute(terminal::EnableLineWrap)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
tokio::runtime::Builder::new_multi_thread()
|
||||||
|
.enable_all()
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
.block_on(async {
|
||||||
|
do_main().await.unwrap();
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in a new issue