/*! This is a primitive command line chat over a shared file. Run multiple instances and exchange what is on your mind with all. Thanks to lozizol, history is automatically presereved for new chatters. */ use async_std::{ fs, io::{ self, prelude::{SeekExt, WriteExt}, stdin, stdout, }, prelude::FutureExt, stream::StreamExt, }; use log::*; use lozizol::model::{self, Sequence}; use std::{path::PathBuf, time::Duration}; use structopt::StructOpt; const CHAT_TYPE_URI: &'static str = "urn:example:chat"; #[async_std::main] pub async fn main() -> anyhow::Result<()> { env_logger::init(); let cmd = Cmd::from_args(); let mut file = fs::OpenOptions::new() .append(true) .read(true) .create(true) .open(cmd.file) .await?; let mut sequence = Sequence::new(); let input = stdin(); let mut output = stdout(); loop { file.seek(io::SeekFrom::Start(sequence.read() as u64)) .await?; { let mut entries = lozizol::task::decode::sequence(&mut sequence, &mut file); while let Some(mut entry) = entries.next().await.transpose()? { match entry.type_uri() { self::CHAT_TYPE_URI => { io::copy(entry, &mut output).await?; output.write_all(b"\n").await?; } model::HEADER_TYPE_URI | model::ASSIGN_TYPE_URI | model::DELETE_TYPE_URI => { entry.skip_read().await?; } unknown => { warn!("Unknown entry type {}", unknown); entry.skip_read().await?; } } } } sequence.set_write(sequence.read()); let mut line = String::new(); if let Ok(result) = input .read_line(&mut line) .timeout(Duration::from_millis(10)) .await { result?; if line.is_empty() { debug!("End of input"); break Ok(()); } debug!("You said: {}", line); let line = line.trim_end_matches(['\n', '\r'].as_ref()); lozizol::task::encode::entry(&mut sequence, &mut file, CHAT_TYPE_URI, &line.len()) .await? .write_all(line.as_bytes()) .await?; debug!("flushing."); file.flush().await?; } } } /// Loziziol CLI tool #[derive(StructOpt, Debug)] #[structopt(name = "chat")] pub struct Cmd { file: PathBuf, }