Flesh out store details
This commit is contained in:
parent
e72fd60d16
commit
fb7e504f2c
5 changed files with 183 additions and 88 deletions
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
112
cove-tui/src/store/dummy.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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>;
|
|
||||||
}
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue