[solved] How to get stdout data out of std::process as iterator


#1

Basically I would like to push some data to the test.sh stdin and see what this process responds to stdout.

fn main() {
    let mut wolf = Wolf::new();

    loop {
        let data = get_some_data_from_mysterious_source();
        wolf.push(data);

        for str in wolf.packets {
            println!("{}", str);
        }
    }
}

Here is my first approach to the problem http://is.gd/xpXICH, however this has borrowing error and I am not sure how to overcome this situation. Maybe there is much cleaner solution to achieve this.

use std::error::Error;
use std::io::prelude::*;
use std::process::{Command, Stdio};

pub struct Wolf {
    process: std::process::Child,
}

impl Wolf {

    pub fn new() -> Wolf {
        let mut process = match Command::new("sh")
                                    .arg("test.sh")
                                    .stdin(Stdio::piped())
                                    .stdout(Stdio::inherit())
                                    .spawn() {
                                        Err(why) => panic!("couldn't spawn process {}", Error::description(&why)),
                                        Ok(process) => process,
                                    };

        Wolf {process: process}

        /*let mut rawdata = Vec::<u8>::new();
        let bytes = process.stdout.unwrap().bytes();
        for b in bytes {
            let val = b.unwrap();
            if val == 0x0A {
                println!("{}", String::from_utf8_lossy(&rawdata));
                rawdata.clear();
            }
            else {
                rawdata.push(val);
            }
        }*/
    }

    pub fn push(&self, buf: &[u8]) {

    }

    pub fn packets(&mut self) -> WolfIntoIterator {
        WolfIntoIterator {
            wolf: self,
        }
    }
}

pub struct WolfIntoIterator<'a> {
    wolf: &'a mut Wolf,
}

impl <'a>Iterator for WolfIntoIterator<'a> {
    type Item = String;
    fn next(&mut self) -> Option<String> {
        let mut rawdata = Vec::<u8>::new();
        
        let bytes = self.wolf.process.stdout.unwrap().bytes();
        for b in bytes {
            let val = b.unwrap();
            // wait for newline '\n' character
            if val == 0x0A {
                //println!("{}", String::from_utf8_lossy(&rawdata));

                break;
                //rawdata.clear();
            }
            else {
                rawdata.push(val);
            }
        }
        
        // just spit something out for testing
        Some(String::from_utf8(rawdata).unwrap())
    }
}

fn main() {

}

#2

I forgot that Option has methods like as_mut() and as_ref() that borrow the value instead of transferring ownership.
Here it is with corrections:

impl <'a>Iterator for WolfIntoIterator<'a> {
    type Item = String;
    fn next(&mut self) -> Option<String> {
        let mut rawdata = Vec::<u8>::new();
        let bytes = self.wolf.process.stdout.as_mut().expect("could not get stdout");

        for b in bytes.bytes() {
            let val = b.ok().expect("could not get byte from stdout");
            // check for newline '\n' character
            if val == 0x0A {
                break;
            }
            else {
                rawdata.push(val);
            }
        }

        if rawdata.len() > 0 {
            Some(String::from_utf8(rawdata).unwrap())
        }
        else {
            None
        }
    }
}

Anyway my question is now solved.


#3

You can wrap process’s output with BufReader and use BufRead::lines() method on it to get lines interator. That would be much more simpler solution than yours.


#4

Indeed:

    let stdout = self.wolf.process.stdout.as_mut().expect("could not get stdout");
    let reader = BufReader::new(stdout);

    for line in reader.lines() {
        Some(line.unwrap())
    }

    None