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
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
//! Rust access library to read/write iChen<sup>®</sup> 4 Open Protocol<sup>™</sup> messages.
//!
//! Details on the protocol can be found [here](https://github.com/chenhsong/OpenProtocol/blob/master/cs/doc/messages_reference.md).
//!
//! Notes on Usage
//! ==============
//!
//! Beware that all data types defined in this crate use borrowed string slices (i.e. `&str`) extensively.
//! This is because the most common usage pattern is to create a data variable, set fields, immediately
//! serialize it into JSON, then dispose of the data variable.  The deserialization story is similar.
//!
//! Error values also borrow heavily from the input fields as these errors are expected to be handled
//! as soon as possible.
//!
//! The result is minimal allocations and copying, but at the cost of stricter lifetime management,
//! especially when deserializing -- the message struct cannot out-live the original JSON text string as
//! fields are borrowed extensively from the original JSON string.
//!
//! Another implication due to extensive usage of borrowed string slices is that strings literals with
//! escape sequences will cause parsing errors because the actual string cannot be simply borrowed from
//! the original JSON string.  Luckily this is extremely rare for most fields holding names, ID's etc.
//! For this reason, only certain user-defined text fields (such as `job_card_id`) that may contain
//! escaped characters (especially the double-quote) and therefore are modeled using `Cow<&str>` instead.
//!

use lazy_static;
use std::borrow::Cow;

// Modules
mod controller;
mod enums;
mod filters;
mod messages;
mod utils;

/// Result error type.
///
#[derive(Debug)]
pub enum OpenProtocolError<'a> {
    /// The value of a field is the empty string "" or all white-spaces,
    /// which is not allowed as value of that field.
    EmptyField(Cow<'a, str>),
    /// The value (second parameter) of a field (first parameter) is not valid for that field.
    ///
    /// The strings are `Box`'ed to make the enum small.
    InvalidField { field: Cow<'a, str>, value: Cow<'a, str>, description: Cow<'a, str> },
    /// An enfored constraint is broken.
    ///
    /// The string is `Box`'ed to make the enum small.
    ConstraintViolated(Cow<'a, str>),
    /// Error when serializing/deserializing JSON.
    JsonError(serde_json::Error),
}

impl std::error::Error for OpenProtocolError<'_> {
    fn description(&self) -> &str {
        match self {
            OpenProtocolError::JsonError(err) => err.description(),
            OpenProtocolError::InvalidField { description, .. } => {
                if description.is_empty() {
                    "Invalid field value."
                } else {
                    description
                }
            }
            OpenProtocolError::ConstraintViolated(err) => err,
            OpenProtocolError::EmptyField(_) => "Field cannot be empty or all white-space.",
        }
    }

    fn cause(&self) -> Option<&dyn std::error::Error> {
        match self {
            OpenProtocolError::JsonError(err) => Some(err),
            _ => None,
        }
    }
}

impl std::fmt::Display for OpenProtocolError<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
        match self {
            OpenProtocolError::JsonError(err) => err.fmt(f),
            OpenProtocolError::InvalidField { field, value, description } => {
                if description.is_empty() {
                    f.write_fmt(format_args!("Value [{}] is invalid for the field {}", value, field))
                } else {
                    f.write_fmt(format_args!("Value [{}] is invalid for the field {}: {}.", value, field, description))
                }
            }
            OpenProtocolError::ConstraintViolated(err) => err.fmt(f),
            OpenProtocolError::EmptyField(field) => {
                f.write_fmt(format_args!("Field {} cannot be empty or all white-space.", field))
            }
        }
    }
}

/// Result type.
///
pub type Result<'a, T> = std::result::Result<T, OpenProtocolError<'a>>;

// Re-exports
pub use controller::{Controller, GeoLocation, Operator};
pub use enums::{JobMode, Language, OpMode};
pub use filters::{Filter, Filters};
pub use messages::*;