Sort out string problems

This commit is contained in:
chmanie 2023-05-26 14:57:31 +02:00
parent 13ff6a741d
commit 0731f61c41
2 changed files with 119 additions and 74 deletions

View File

@ -28,7 +28,7 @@ use std::{
time::{Duration, Instant}, time::{Duration, Instant},
vec, vec,
}; };
use tokio::{select, task}; use tokio::{select, signal, task};
use tokio_stream::StreamExt; use tokio_stream::StreamExt;
// use // use
use tonic::{transport::Channel, Request, Streaming}; use tonic::{transport::Channel, Request, Streaming};
@ -89,49 +89,110 @@ impl<T> StatefulList<T> {
} }
self.state.select(None); self.state.select(None);
} }
fn get_selected(&self) -> Option<&T> {
if let Some(idx) = self.state.selected() {
return Some(&self.items[idx]);
}
None
}
}
struct UiItem {
uuid: String,
title: String,
}
struct LibraryView {
title: String,
uuid: String,
list: StatefulList<UiItem>,
parent: Option<String>,
}
impl LibraryView {
fn update(&mut self, node: LibraryNode) {
if node.tracks.is_empty() && node.children.is_empty() {
return;
}
// if children empty and tracks empty return
self.uuid = node.uuid;
self.title = node.name;
self.parent = node.parent;
if !node.tracks.is_empty() {
self.list.items = node
.tracks
.iter()
.map(|t| UiItem {
uuid: t.uuid.clone(),
title: t.title.clone(),
})
.collect();
} else {
// if tracks not empty use tracks instead
self.list.items = node
.children
.iter()
.map(|c| UiItem {
uuid: c.to_string(),
title: c.to_string(),
})
.collect();
}
}
} }
struct App { struct App {
library: StatefulList<LibraryNode>, library: LibraryView,
queue: StatefulList<LibraryNode>, queue: StatefulList<UiItem>,
} }
impl App { impl App {
fn new() -> App { fn new() -> App {
let mut library = StatefulList::default(); let mut library = LibraryView {
library.focus(); title: "Library".to_string(),
uuid: "/".to_string(),
list: StatefulList::default(),
parent: None,
};
library.list.focus();
let mut queue = StatefulList::default(); let mut queue = StatefulList::default();
App { library, queue } App { library, queue }
} }
fn cycle_active(&mut self) { fn cycle_active(&mut self) {
if self.library.is_focused() { if self.library.list.is_focused() {
self.library.blur(); self.library.list.blur();
self.queue.focus(); self.queue.focus();
} else { } else {
self.library.focus(); self.library.list.focus();
self.queue.blur(); self.queue.blur();
} }
} }
} }
enum Message<'a> { // FIXME: Rename this
Quit, enum MessageToUi {
// FIXME: Is String OK here? ReplaceLibraryNode(LibraryNode),
GetLibraryNode(&'a str),
LibraryNodeReceived(LibraryNode),
QueueStreamUpdate(QueueUpdateResult), QueueStreamUpdate(QueueUpdateResult),
TrackStreamUpdate(GetTrackUpdatesResponse), TrackStreamUpdate(GetTrackUpdatesResponse),
} }
// FIXME: Rename this
enum MessageFromUi {
Quit,
GetLibraryNode(String),
}
async fn orchestrate<'a>( async fn orchestrate<'a>(
(tx, rx): (Sender<Message<'a>>, Receiver<Message<'a>>), (tx, rx): (Sender<MessageToUi>, Receiver<MessageFromUi>),
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
let mut rpc_client = rpc::RpcClient::connect("http://[::1]:50051").await?; let mut rpc_client = rpc::RpcClient::connect("http://[::1]:50051").await?;
if let Some(root_node) = rpc_client.get_library_node("/").await? { if let Some(root_node) = rpc_client.get_library_node("/").await? {
// FIXME: Is it ok to clone here? // FIXME: Is it ok to clone here?
tx.send(Message::LibraryNodeReceived(root_node.clone())); tx.send(MessageToUi::ReplaceLibraryNode(root_node.clone()));
} }
// FIXME: stream failures, do we need to re-establish the stream? // FIXME: stream failures, do we need to re-establish the stream?
@ -142,27 +203,23 @@ async fn orchestrate<'a>(
select! { select! {
Ok(msg) = &mut rx.recv_async() => { Ok(msg) = &mut rx.recv_async() => {
match msg { match msg {
// FIXME: How can I make sure I have all match arms implmenented? MessageFromUi::Quit => {
// (Some messages are not applicable here)
Message::Quit => {
break Ok(()); break Ok(());
}, },
Message::GetLibraryNode(uuid) => { MessageFromUi::GetLibraryNode(uuid) => {
if let Some(node) = rpc_client.get_library_node(uuid).await? { if let Some(node) = rpc_client.get_library_node(&uuid).await? {
// FIXME: Is it ok to clone here? tx.send(MessageToUi::ReplaceLibraryNode(node.clone()));
tx.send(Message::LibraryNodeReceived(node.clone()));
} }
} }
_ => {},
} }
} }
Some(Ok(resp)) = queue_update_stream.next() => { Some(Ok(resp)) = queue_update_stream.next() => {
if let Some(res) = resp.queue_update_result { if let Some(res) = resp.queue_update_result {
tx.send_async(Message::QueueStreamUpdate(res)).await; tx.send_async(MessageToUi::QueueStreamUpdate(res)).await;
} }
} }
Some(Ok(resp)) = track_update_stream.next() => { Some(Ok(resp)) = track_update_stream.next() => {
tx.send(Message::TrackStreamUpdate(resp)); tx.send(MessageToUi::TrackStreamUpdate(resp));
} }
} }
} }
@ -170,8 +227,8 @@ async fn orchestrate<'a>(
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (ui_tx, rx): (Sender<Message>, Receiver<Message>) = flume::unbounded(); let (ui_tx, rx): (Sender<MessageFromUi>, Receiver<MessageFromUi>) = flume::unbounded();
let (tx, ui_rx): (Sender<Message>, Receiver<Message>) = flume::unbounded(); let (tx, ui_rx): (Sender<MessageToUi>, Receiver<MessageToUi>) = flume::unbounded();
thread::spawn(|| { thread::spawn(|| {
run_ui(ui_tx, ui_rx); run_ui(ui_tx, ui_rx);
@ -180,19 +237,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// FIXME: unwrap // FIXME: unwrap
tokio::spawn(async move { orchestrate((tx, rx)).await.unwrap() }); tokio::spawn(async move { orchestrate((tx, rx)).await.unwrap() });
// loop { signal::ctrl_c().await.unwrap();
// match rx.recv() {
// Ok(Message::Quit) => {
// break;
// }
// _ => {}
// }
// }
Ok(()) Ok(())
} }
fn run_ui(tx: Sender<Message>, rx: Receiver<Message>) { fn run_ui(tx: Sender<MessageFromUi>, rx: Receiver<MessageToUi>) {
// setup terminal // setup terminal
enable_raw_mode().unwrap(); enable_raw_mode().unwrap();
let mut stdout = io::stdout(); let mut stdout = io::stdout();
@ -208,32 +258,15 @@ fn run_ui(tx: Sender<Message>, rx: Receiver<Message>) {
loop { loop {
for message in rx.try_iter() { for message in rx.try_iter() {
match message { match message {
Message::LibraryNodeReceived(node) => { MessageToUi::ReplaceLibraryNode(node) => {
// FIXME: this is obviously bullshit app.library.update(node);
// FIXME: DO NOT PUSH LIBRARY_NODES ONTO THE UI, IT SHOULD GET ITS OWN TYPE
app.library.items.push(LibraryNode {
uuid: node.uuid,
name: node.name,
children: Vec::new(),
is_queable: false,
parent: None,
state: LibraryNodeState::Done.into(),
tracks: Vec::new(),
});
} }
Message::QueueStreamUpdate(queue_update) => match queue_update { MessageToUi::QueueStreamUpdate(queue_update) => match queue_update {
QueueUpdateResult::Full(queue) => {} QueueUpdateResult::Full(queue) => {}
QueueUpdateResult::PositionChange(pos) => { QueueUpdateResult::PositionChange(pos) => {
// FIXME: this is obviously bullshit app.queue.items.push(UiItem {
// FIXME: DO NOT PUSH LIBRARY_NODES ONTO THE UI, IT SHOULD GET ITS OWN TYPE
app.queue.items.push(LibraryNode {
uuid: pos.timestamp.to_string(), uuid: pos.timestamp.to_string(),
name: pos.timestamp.to_string(), title: pos.timestamp.to_string(),
children: Vec::new(),
is_queable: false,
parent: None,
state: LibraryNodeState::Done.into(),
tracks: Vec::new(),
}); });
} }
}, },
@ -252,24 +285,34 @@ fn run_ui(tx: Sender<Message>, rx: Receiver<Message>) {
if key.kind == KeyEventKind::Press { if key.kind == KeyEventKind::Press {
match key.code { match key.code {
KeyCode::Char('q') => { KeyCode::Char('q') => {
tx.send(Message::Quit); tx.send(MessageFromUi::Quit);
break; break;
} }
KeyCode::Char('j') => { KeyCode::Char('j') => {
if app.library.is_focused() { if app.library.list.is_focused() {
app.library.next(); app.library.list.next();
} else { } else {
app.queue.next(); app.queue.next();
} }
} }
KeyCode::Char('k') => { KeyCode::Char('k') => {
if app.library.is_focused() { if app.library.list.is_focused() {
app.library.prev(); app.library.list.prev();
} else { } else {
app.queue.prev(); app.queue.prev();
} }
} }
KeyCode::Tab => app.cycle_active(), KeyCode::Tab => app.cycle_active(),
KeyCode::Char('h') => {
if let Some(parent) = app.library.parent.as_ref() {
tx.send(MessageFromUi::GetLibraryNode(parent.clone()));
}
}
KeyCode::Char('l') => {
if let Some(item) = app.library.list.get_selected() {
tx.send(MessageFromUi::GetLibraryNode(item.uuid.clone()));
}
}
_ => {} _ => {}
} }
} }
@ -302,21 +345,22 @@ fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) {
let library_items: Vec<ListItem> = app let library_items: Vec<ListItem> = app
.library .library
.list
.items .items
.iter() .iter()
// FIXME: why to_string() ?? // FIXME: why to_string() ??
.map(|i| ListItem::new(Span::from(i.name.to_string()))) .map(|i| ListItem::new(Span::from(i.title.to_string())))
.collect(); .collect();
let library_list = List::new(library_items) let library_list = List::new(library_items)
.block(Block::default().borders(Borders::ALL).title("Library")) .block(Block::default().borders(Borders::ALL).title(app.library.title.clone()))
.highlight_style( .highlight_style(
Style::default() Style::default()
.bg(Color::LightBlue) .bg(Color::LightBlue)
.add_modifier(Modifier::BOLD), .add_modifier(Modifier::BOLD),
); );
f.render_stateful_widget(library_list, main[0], &mut app.library.state); f.render_stateful_widget(library_list, main[0], &mut app.library.list.state);
let now_playing = Layout::default() let now_playing = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
@ -328,7 +372,7 @@ fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) {
.items .items
.iter() .iter()
// FIXME: why to_string() ?? // FIXME: why to_string() ??
.map(|i| ListItem::new(Span::from(i.name.to_string()))) .map(|i| ListItem::new(Span::from(i.title.to_string())))
.collect(); .collect();
let queue_list = List::new(queue_items) let queue_list = List::new(queue_items)

View File

@ -30,15 +30,15 @@ impl fmt::Display for RpcClientError {
impl Error for RpcClientError {} impl Error for RpcClientError {}
pub struct RpcClient<'a> { pub struct RpcClient {
library_node_cache: HashMap<&'a str, LibraryNode>, library_node_cache: HashMap<String, LibraryNode>,
client: CrabidyServiceClient<Channel>, client: CrabidyServiceClient<Channel>,
} }
impl<'a> RpcClient<'a> { impl RpcClient {
pub async fn connect(addr: &'static str) -> Result<RpcClient<'a>, tonic::transport::Error> { pub async fn connect(addr: &'static str) -> Result<RpcClient, tonic::transport::Error> {
let client = CrabidyServiceClient::connect(addr).await?; let client = CrabidyServiceClient::connect(addr).await?;
let library_node_cache: HashMap<&str, LibraryNode> = HashMap::new(); let library_node_cache: HashMap<String, LibraryNode> = HashMap::new();
Ok(RpcClient { Ok(RpcClient {
client, client,
library_node_cache, library_node_cache,
@ -46,7 +46,7 @@ impl<'a> RpcClient<'a> {
} }
pub async fn get_library_node( pub async fn get_library_node(
&mut self, &mut self,
uuid: &'a str, uuid: &str,
) -> Result<Option<&LibraryNode>, Box<dyn Error>> { ) -> Result<Option<&LibraryNode>, Box<dyn Error>> {
if self.library_node_cache.contains_key(uuid) { if self.library_node_cache.contains_key(uuid) {
return Ok(self.library_node_cache.get(uuid)); return Ok(self.library_node_cache.get(uuid));
@ -62,7 +62,8 @@ impl<'a> RpcClient<'a> {
.await?; .await?;
if let Some(library_node) = response.into_inner().node { if let Some(library_node) = response.into_inner().node {
self.library_node_cache.insert(uuid, library_node); self.library_node_cache
.insert(uuid.to_string(), library_node);
// FIXME: is that necessary? // FIXME: is that necessary?
return Ok(self.library_node_cache.get(uuid)); return Ok(self.library_node_cache.get(uuid));
} }