6
\$\begingroup\$

I am new to programming in Rust and as an exercise I was making a simple Lottery checker program. It has a simple menu, the user enters a number for the corresponding option such as 'Add a line', 'Check if I am a winner' etc.

So all the user needs to do is enter either a number or a 'y' or 'n'. So I created a simple Token to see if they have either entered a number or a char and return a token.

This works fine. But as I am on my own I was looking for feedback so that I can improve my skills.

use std::io::{self, Write};

#[derive(Debug, PartialEq)]
enum Token {
    Number(i32),
    Decision(char),
    // Error(String),
}

fn get_number(val: &str) -> Option<Token> {
    if let Some(x) = val.split_whitespace().next() {
        let y: i32 = match x.parse() {
            Ok(val) => val,
            Err(_error) => return None,
        };
        Some(Token::Number(y))
    } else {
        None
    }
}

fn get_char(val: &str) -> Option<Token> {
    if let Some(x) = val.split_whitespace().next() {
        let y: char = match x.parse() {
            Ok(val) => val,
            Err(_error) => return None,
        };
        Some(Token::Decision(y))
    } else {
        None
    }
}

fn get_input(msg: &str, input: &str) -> Option<Token> {
    print!("{}\n{}", msg, ">> ");
    io::stdout().flush().unwrap();

    let buffer = input;
    //let mut buffer = String::new();
    // while buffer.is_empty() {
    // io::stdin()
    // .read_line(&mut buffer)
    // .expect("GET_INPUT: error reading line.");

    //  buffer = buffer
    // .trim()
    // .parse()
    //  .expect("GET_INPUT: error parsing buffer");

    // if buffer.is_empty() {
    //     println!("You didn't enter anything.\n{}", &msg)
    // };
    // }

    if let Some(token) = get_number(&buffer) {
        if let Token::Number(val) = token {
            return Some(Token::Number(val));
        }
    } else if let Some(token) = get_char(&buffer) {
        if let Token::Decision(val) = token {
            return Some(Token::Decision(val));
        }
    }
    None
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn get_input_test() {
        let chars1 = "@  n 23";
        let chars2 = "34 n 23";
        let chars3 = "$ ££$^\"3234y n 23";
        let chars4 = "";
        let chars5 = "      ";

        assert_eq!(Some(Token::Decision('@')), get_input("", &chars1));
        assert_eq!(Some(Token::Number(34)), get_input("", &chars2));
        assert_eq!(Some(Token::Decision('$')), get_input("", &chars3));
        assert_eq!(None, get_input("", &chars4));
        assert_eq!(None, get_input("", &chars5));
    }

}

To get some more fine grain control I understand I will need to use an Iterator, is this correct?

\$\endgroup\$
  • \$\begingroup\$ Please include the necessary code to fully reproduce the program. Fetching std::io and std::io::Write is needed to compile. The definition of PROMPT is also missing. \$\endgroup\$ – E_net4 May 18 at 16:21
  • \$\begingroup\$ it spread over several files\modules what would be best way to upload. either upload the files or just paste all of it with annotations between code blocks. Thank you for your response \$\endgroup\$ – robinellis May 19 at 19:09
  • \$\begingroup\$ Like on Stack Overflow, you are expected to include all of the necessary code to reproduce the program. Here are a few more tips on making an MCVE for Rust code. In particular, your code should work on the Rust Playground. \$\endgroup\$ – E_net4 May 19 at 21:52
  • \$\begingroup\$ I have updated the code so that it will work on Rust playground. I had problems with Rust playground and terminal input, so I took out the iostdin call and simulated the input. \$\endgroup\$ – robinellis May 21 at 9:11
1
\$\begingroup\$

It's not clear whether you want to support only y, n and numbers as your question states, or any characters besides y & n as your code implies :)

Basically you can divide the problem to two sub problems:

  1. Split the input string to words I'm using the split methods from std but if you have more complex requirements, you can use the regex crate
  2. Map the words to tokens. A simple equality check (==) or match is enough. There is no need to parse() everything.

As you've not specified what's allowed as a delimiter, I'll assume that it's whitespace.

#[derive(Debug)]
enum InputToken {
    Number(i64),
    Boolean(bool),
    Error(String),
}

fn main() {
    let input = "1 -2 +4 y n & abc";
    let tokens = input.split_whitespace();

    for x in tokens {
        println!("{:?}", parse_token(x));
    }
}

fn parse_token(input: &str) -> InputToken {
    match input {
        "y" => InputToken::Boolean(true),
        "n" => InputToken::Boolean(false),
        token => match token.parse::<i64>() {
            Ok(num) => InputToken::Number(num),
            Err(e) => InputToken::Error(format!("Invalid input token [{}]: {:?}", token, e))
    }
}

}

\$\endgroup\$
  • \$\begingroup\$ your assumptions are correct :) I don't find learning easy, so purpose of th epost is to get some feedback so I am not learning in a vacuum. Thank you for your post it has been very helpful. I didn't know you could nest matchs \$\endgroup\$ – robinellis May 28 at 10:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.