Flesh out store details

This commit is contained in:
Joscha 2022-06-13 16:20:44 +02:00
parent e72fd60d16
commit fb7e504f2c
5 changed files with 183 additions and 88 deletions

View file

@ -2,7 +2,6 @@
mod chat; mod chat;
mod store; mod store;
mod traits;
mod ui; mod ui;
use toss::terminal::Terminal; use toss::terminal::Terminal;

View file

@ -1,86 +1,89 @@
pub mod dummy;
use std::collections::HashMap; use std::collections::HashMap;
use std::hash::Hash;
use async_trait::async_trait; use async_trait::async_trait;
use chrono::{DateTime, TimeZone, Utc}; use chrono::{DateTime, Utc};
use crate::traits::{Msg, MsgStore}; pub trait Msg {
type Id: Hash + Eq;
fn id(&self) -> Self::Id;
fn parent(&self) -> Option<Self::Id>;
pub struct DummyMsg { fn time(&self) -> DateTime<Utc>;
id: usize, fn nick(&self) -> String;
parent: Option<usize>, fn content(&self) -> String;
time: DateTime<Utc>, }
nick: String,
content: String, pub struct Path<I>(Vec<I>);
impl<I> Path<I> {
pub fn new(segments: Vec<I>) -> Self {
assert!(!segments.is_empty(), "segments must not be empty");
Self(segments)
}
pub fn segments(&self) -> &[I] {
&self.0
}
pub fn first(&self) -> &I {
self.0.first().expect("path is not empty")
}
pub fn first_mut(&mut self) -> &mut I {
self.0.first_mut().expect("path is not empty")
}
pub fn last(&self) -> &I {
self.0.last().expect("path is not empty")
}
pub fn last_mut(&mut self) -> &mut I {
self.0.last_mut().expect("path is not empty")
}
}
pub struct Tree<M: Msg> {
root: M::Id,
msgs: HashMap<M::Id, M>,
children: HashMap<M::Id, Vec<M::Id>>,
}
impl<M: Msg> Tree<M> {
pub fn new(root: M::Id, msgs: Vec<M>) -> Self {
let msgs: HashMap<M::Id, M> = msgs.into_iter().map(|m| (m.id(), m)).collect();
let mut children: HashMap<M::Id, Vec<M::Id>> = HashMap::new();
for msg in msgs.values() {
if let Some(parent) = msg.parent() {
children.entry(parent).or_default().push(msg.id());
}
} }
impl DummyMsg {
pub fn new<S>(id: usize, nick: S, content: S) -> Self
where
S: Into<String>,
{
Self { Self {
id, root,
parent: None, msgs,
time: Utc.timestamp(0, 0), children,
nick: nick.into(),
content: content.into(),
} }
} }
pub fn parent(mut self, parent: usize) -> Self { pub fn root(&self) -> &M::Id {
self.parent = Some(parent); &self.root
self
}
} }
impl Msg for DummyMsg { pub fn msg(&self, id: &M::Id) -> Option<&M> {
type Id = usize; self.msgs.get(id)
fn id(&self) -> Self::Id {
self.id
} }
fn time(&self) -> DateTime<Utc> { pub fn children(&self, id: &M::Id) -> Option<&[M::Id]> {
self.time self.children.get(id).map(|c| c as &[M::Id])
}
fn nick(&self) -> String {
self.nick.clone()
}
fn content(&self) -> String {
self.content.clone()
}
}
pub struct DummyStore {
msgs: HashMap<usize, DummyMsg>,
}
impl DummyStore {
pub fn new() -> Self {
Self {
msgs: HashMap::new(),
}
}
pub fn msg(mut self, msg: DummyMsg) -> Self {
self.msgs.insert(msg.id(), msg);
self
} }
} }
#[async_trait] #[async_trait]
impl MsgStore<DummyMsg> for DummyStore { pub trait MsgStore<M: Msg> {
async fn path(&self, _room: &str, mut id: usize) -> Vec<usize> { async fn path(&self, room: &str, id: M::Id) -> Path<M::Id>;
let mut path = vec![id]; async fn thread(&self, room: &str, root: M::Id) -> Tree<M>;
while let Some(parent) = self.msgs.get(&id).and_then(|msg| msg.parent) {
path.push(parent);
id = parent;
}
path.reverse();
path
}
} }

112
cove-tui/src/store/dummy.rs Normal file
View file

@ -0,0 +1,112 @@
use std::collections::HashMap;
use std::thread::Thread;
use async_trait::async_trait;
use chrono::{DateTime, TimeZone, Utc};
use super::{Msg, MsgStore, Path, Tree};
#[derive(Clone)]
pub struct DummyMsg {
id: usize,
parent: Option<usize>,
time: DateTime<Utc>,
nick: String,
content: String,
}
impl DummyMsg {
pub fn new<S>(id: usize, nick: S, content: S) -> Self
where
S: Into<String>,
{
Self {
id,
parent: None,
time: Utc.timestamp(0, 0),
nick: nick.into(),
content: content.into(),
}
}
pub fn parent(mut self, parent: usize) -> Self {
self.parent = Some(parent);
self
}
}
impl Msg for DummyMsg {
type Id = usize;
fn id(&self) -> Self::Id {
self.id
}
fn parent(&self) -> Option<Self::Id> {
self.parent
}
fn time(&self) -> DateTime<Utc> {
self.time
}
fn nick(&self) -> String {
self.nick.clone()
}
fn content(&self) -> String {
self.content.clone()
}
}
pub struct DummyStore {
msgs: HashMap<usize, DummyMsg>,
children: HashMap<usize, Vec<usize>>,
}
impl DummyStore {
pub fn new() -> Self {
Self {
msgs: HashMap::new(),
children: HashMap::new(),
}
}
pub fn msg(mut self, msg: DummyMsg) -> Self {
if let Some(parent) = msg.parent {
self.children.entry(parent).or_default().push(msg.id());
}
self.msgs.insert(msg.id(), msg);
self
}
fn tree(&self, id: usize, result: &mut Vec<DummyMsg>) {
if let Some(msg) = self.msgs.get(&id) {
result.push(msg.clone());
if let Some(children) = self.children.get(&id) {
for child in children {
self.tree(*child, result);
}
}
}
}
}
#[async_trait]
impl MsgStore<DummyMsg> for DummyStore {
async fn path(&self, _room: &str, mut id: usize) -> Path<usize> {
let mut segments = vec![id];
while let Some(parent) = self.msgs.get(&id).and_then(|msg| msg.parent) {
segments.push(parent);
id = parent;
}
segments.reverse();
Path::new(segments)
}
async fn thread(&self, _room: &str, root: usize) -> Tree<DummyMsg> {
let mut msgs = vec![];
Tree::new(root, msgs)
}
}

View file

@ -1,16 +0,0 @@
use async_trait::async_trait;
use chrono::{DateTime, Utc};
pub trait Msg {
type Id;
fn id(&self) -> Self::Id;
fn time(&self) -> DateTime<Utc>;
fn nick(&self) -> String;
fn content(&self) -> String;
}
#[async_trait]
pub trait MsgStore<M: Msg> {
async fn path(&self, room: &str, id: M::Id) -> Vec<M::Id>;
}

View file

@ -1,10 +1,7 @@
use std::collections::hash_map::Entry;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use std::time::Duration; use std::time::Duration;
use crossterm::event::{Event, EventStream, KeyCode, KeyEvent, MouseEvent}; use crossterm::event::{Event, KeyCode, KeyEvent, MouseEvent};
use crossterm::style::ContentStyle;
use futures::StreamExt;
use parking_lot::FairMutex; use parking_lot::FairMutex;
use tokio::sync::mpsc::error::TryRecvError; use tokio::sync::mpsc::error::TryRecvError;
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
@ -13,7 +10,7 @@ use toss::frame::{Frame, Pos, Size};
use toss::terminal::Terminal; use toss::terminal::Terminal;
use crate::chat::Chat; use crate::chat::Chat;
use crate::store::{DummyMsg, DummyStore}; use crate::store::dummy::{DummyMsg, DummyStore};
#[derive(Debug)] #[derive(Debug)]
pub enum UiEvent { pub enum UiEvent {