Writing / Reading in a REPL like Command

Hi !
I'm working on a tauri project which must use "mongosh" command to dialog with a mongodb database.

The idea is that de main thread will receive the mongodb command from the UI, then send this command to a thread in which a mongosh Command has been spawned.

Writing in the mongosh Command seems to work, but reading the stdout don't work. I tried several things but none worked :frowning: ...

Here is the code of the function that create the mongosh thread :

let (command_sender, command_receiver) = mpsc::channel();

    let thread_uri = uri.clone();

    thread::spawn(move || {
        let mut mongosh = process::Command::new("mongosh")
            .stdin(process::Stdio::piped())
            .stdout(process::Stdio::piped())
            .stderr(process::Stdio::piped())
            .arg(thread_uri)
            .arg("--quiet")
            .spawn()
            .expect("Failed to start mongosh");

        let mut mongosh_stdin = mongosh.stdin.take().expect("Failed to open stdin");
        let mut mongosh_stdout = mongosh.stdout.take().expect("Failed to open stdout");

        let mut reader = BufReader::new(mongosh_stdout);

        loop {
            println!("Waiting for a command ...");

            let mut command: String = command_receiver
                .recv()
                .expect("Unable to receive the command");

            command.push('\n');

            println!("Executing command '{command}'...");

            mongosh_stdin
                .write_all(command.as_bytes())
                .expect("Failed to write command to mongosh stdin");
            let _ = mongosh_stdin.flush();

            let mut output = vec![];

            loop {
                match reader.read_until(b'>', &mut output) {
                    Ok(0) => break,
                    Ok(_) => {
                        if !output.ends_with(&[b'>']) {
                           // an attempt to fix the code, not working obviously
                            println!("Continue !");
                            continue;
                        }
                    }
                    Err(err) => {
                        println!("Error while reading lines from stdout: {:?}", err);
                        break;
                    }
                }
            }

            println!("{:?}", String::from_utf8(output));
        }
    });

The main problem reading the stdout of the mongosh command is that I don't want to wait the end of the command because it's a REPL, it never ends.
The problem is that the read operation is blocking, waiting for EOF, so my thread is stucked in the read loop :frowning:

I'm a total beginner in Rust, I don't fully understand ownership and other key concepts, that's why i'm stucked :grimacing: ...

Can you explain to me how I can have a REPL like command like mongosh running in a thread ? And be able to read & write into stdin/stdout of the command ?

Tnahk you !

Create a thread whose only job is reading and parsing the output of the child process. Then either:

  • that thread can also have a channel receiver by which it is told β€œwhat to expect” β€” what to do with that output, which is determined by some other thread which sent the command to the child REPL β€” and it consults that channel after it has read a whole response from the REPL, or
  • that thread can send messages elsewhere when it has read an entire response from the REPL.

(It's possible to do this sort of thing with fewer threads by using async programming, because async tasks can wait on multiple sources of input at once, but even there it's often wise to have a task dedicated to reading and parsing the data stream.)

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.