1

Given a raw input stream of &[u16] how can I use nom to parse it taking into account that nom expects &str as input?

For instance, given the following data:

pub const RAW_INPUT: &[u16] = &[102, 111, 111];

I want to parse it into the string "foo".

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Jesuspc
  • 1,664
  • 10
  • 25

2 Answers2

0

There are a couple of ways you could go about doing this. I don't know anything about modbus, so I'm assuming that the input looks like your RAW_INPUT above. First, you can use as to cast the u16 to u8. This will silently truncate values larger than 255. The other, safer approach is to use std::convert::TryFrom:

Simple and safe type conversions that may fail in a controlled way under some circumstances. It is the reciprocal of TryInto.

This is useful when you are doing a type conversion that may trivially succeed but may also need special handling. For example, there is no way to convert an i64 into an i32 using the From trait, because an i64 may contain a value that an i32 cannot represent and so the conversion would lose data. This might be handled by truncating the i64 to an i32 (essentially giving the i64's value modulo i32::MAX) or by simply returning i32::MAX, or by some other method. The From trait is intended for perfect conversions, so the TryFrom trait informs the programmer when a type conversion could go bad and lets them decide how to handle it.

Some illustrative code you can play around with on the Rust Playground:

#[cfg(test)]
mod tests {
    use std::convert::TryFrom;
    use std::num::TryFromIntError;
    use std::str;

    pub const RAW_BAD_INPUT: &[u16] = &[102, 111, 111, 300];
    pub const RAW_GOOD_INPUT: &[u16] = &[102, 111, 111];

    /// Converts using `as`. Demonstrates truncation.
    #[test]
    fn test_truncating() {
        let expected = vec![102, 111, 111, 44];  // Note: 44
        let actual = RAW_BAD_INPUT
            .iter()
            .map(|val| *val as u8)
            .collect::<Vec<u8>>();
        assert_eq!(expected, actual);
    }

    /// Demonstrates conversion using `TryFrom` on input with values that
    /// would be truncated
    #[test]
    fn test_try_from_bad() {
        let actual: Vec<Result<u8, TryFromIntError>> =
            RAW_BAD_INPUT.iter().map(|val| u8::try_from(*val)).collect();

        assert_eq!(actual[0].unwrap(), 102u8);
        assert_eq!(actual[1].unwrap(), 111u8);
        assert_eq!(actual[2].unwrap(), 111u8);
        assert!(actual[3].is_err());
    }

    /// Demonstrates conversion using `TryFrom` on input with values
    /// that would not be truncated. Also parses the Vec<u8> as a UTF-8
    /// encoded string
    #[test]
    fn test_try_from_ok() {
        let intermediate: Vec<u8> = RAW_GOOD_INPUT
            .iter()
            .map(|val| u8::try_from(*val).unwrap())
            .collect();
        let actual = match str::from_utf8(&intermediate) {
            Ok(s) => s,
            Err(e) => panic!("Invalid UTF-8: {}", e),
        };
        assert_eq!("foo", actual);
    }
}

Using the code in test_try_from_ok, you should now have a String containing the data you wish to parse with nom.

Community
  • 1
  • 1
chucksmash
  • 5,777
  • 1
  • 32
  • 41
  • This... doesn't appear to use [nom](https://crates.io/crates/nom), as the OP requests. – Shepmaster Apr 15 '19 at 02:32
  • I'd be interesting in understanding the reason for the down vote here. My reading of the OP's question was that they were not able to use the library they wanted to use because they were dealing with type `&[u16]`. I've shown a few ways they can convert to `Vec` or `String`, which they then should be able to use with `nom`. – chucksmash Apr 15 '19 at 09:55
0

Given:

pub const RAW_INPUT: &[u16] = &[102, 111, 111];

I ended up converting the input to u8 first:

let xs = RAW_INPUT
    .iter()
    .flat_map(|x| x.to_be_bytes().to_vec())
    .collect::<Vec<u8>>();

And then parsing it normally with nom.

Jesuspc
  • 1,664
  • 10
  • 25