1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
use std::str::FromStr;

use crate::{Error, Result};

/// A Modbus register kind.
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub enum Kind {
    Coil,
    Discrete,
    Input,
    Holding,
}

/// A Modbus register type, contains the kind and the offset.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct Register {
    pub kind: Kind,
    pub offset: u16,
}

impl Register {
    pub fn new(kind: Kind, offset: u16) -> Self {
        Self { kind, offset }
    }
}

fn parse_kind_offset(r: &str) -> Result<(Kind, u16)> {
    macro_rules! err {
        () => {
            Error::invalid_data(format!("invalid register: {}", r))
        };
    }
    let mut chars = r.chars();
    let kind = match chars.next().ok_or_else(|| err!())? {
        'c' => Kind::Coil,
        'd' => Kind::Discrete,
        'i' => Kind::Input,
        'h' => Kind::Holding,
        _ => return Err(Error::invalid_data(format!("invalid register kind: {}", r))),
    };
    let o = if chars.next().ok_or_else(|| err!())? == '@' {
        2
    } else {
        1
    };
    let offset = r[o..].parse().map_err(|_| err!())?;
    Ok((kind, offset))
}

impl FromStr for Register {
    type Err = Error;
    fn from_str(s: &str) -> Result<Self> {
        let (kind, offset) = parse_kind_offset(s)?;
        Ok(Register { kind, offset })
    }
}

impl TryFrom<&str> for Register {
    type Error = Error;
    fn try_from(s: &str) -> Result<Self> {
        s.parse()
    }
}