Javascript like callbacks on streams


#1

Is there any way in rust to achieve javascript like callbacks on a stream? I am quite new to rust and am working on a simple console based IRC client. I need to be able to monitor for both messages and keypresses and respond to both. Currently, I am using the second thread to get the messages and then pass them into the main thread through a stream. I need to have a continuous loop to get a keypress and respond to it, but I also need to be able to add new messages at the same time. It would seem like using the and_then method on the stream would add a javascript like callback and work for this purpose, but I cannot seem to figure out how to configure it. Any help would be greatly appreciated!

The code I am working on looks like this:

extern crate irc;
extern crate pancurses;
extern crate futures;

use futures::prelude::*;
use futures::sync::mpsc;

use std::default::Default;
use std::thread;
use irc::client::prelude::*;
use pancurses::{initscr, endwin};

fn main() {
    /* Stream between chat message thread and ui thread */
    let (tx, rx) = mpsc::channel::<irc::proto::Message>(20);
    let window = initscr();

    thread::spawn(move || {
        let cfg = Config {
            nickname: Some(format!("name")),
            server: Some(format!("irc")),
            channels: Some(vec![format!("#channel")]),
            .. Default::default()
        };
        let client = IrcClient::from_config(cfg).unwrap();
        client.identify().unwrap();
        client.for_each_incoming(|message| {
            tx.try_send(message);
        }).unwrap()
    });

    let rx = rx.and_then(|message| { /* Not quite sure if this is what I want or how to configure it */
        window.printw(&format!("{}", message)[..]);
    });

    loop {
        window.getch();
    }
}

#2

You probably want rx.for_each(...) to handle each message on the receiver.

I’m not sure what the loop at the end is for. Perhaps you want to run a tokio reactor that drives the rx ForEach future to completion?

I guess I don’t quite understand the “how to configure it” portion. Can you expand a bit?


#3

Thanks for the reply!
The loop at the end is the main application loop for the GUI. I’ll definitely try using .for_each in place of the .and_then and see how it goes. When I mentioned configuring the callback earlier, it just seemed like it needed a return value and I was confused as to what it was expecting for that. Thanks again.


#4

Just tried using .for_each() in place of .and_then(). I seem to have the same error as before, where It seems to expect a return, but I am not sure what it should be or why? Perhaps I am taking the wrong approach for this, as I just need the program to asynchronously putevery message sent on the stream to the GUI and run the main loop. The exact error message is:

error: src\main.rs:33: the trait bound `(): futures::Future` is not satisfied
error: src\main.rs:33: the trait `futures::Future` is not implemented for `()`
note: src\main.rs:33: required because of the requirements on the impl of `futures::IntoFuture` for `()`

and the code I am using is:

rx.for_each(|message| {
    window.printw(&format!("{}", message)[..]);
});

#5

Ah I see. So I don’t think you’ll be using futures at all here. You probably need a channel (std::sync::mpsc one, not the one from the futures crate) to send a message from the background thread to the UI thread and have that message put on the UI event loop for display/handling. I’m not familiar with pancurses so I don’t know if it has a way to easily do that.


#6

Is there any way to have two loops running at once? Pancurses seems to need a dedicated loop for handling user input. I seem to need another loop for handling input from the other thread. I’ll implement the std::sync::mpsc channel and see if I can find a way to have both loops running at once.


#7

Found the solution! Looking in the pancurses docs again I found a method that allows getch() to be asynchronous, making it only necessary to have one event loop. Thanks so much for your help!