callpass 1.0.2

Generate APRS passcodes
Documentation
#![cfg_attr(not(feature = "std"), no_std)]
#![deny(missing_docs)]

//! An APRS-IS passcode generator and type.
//!
//! # Usage
//!
//! The `Callpass` type is used for representing an APRS-IS passcode.
//! It will transform a callsign into a passcode, and can be used in
//! place of integers where expecting a callpass.
//!
//! ```
//! let given_callsign = "x2yz";
//! let given_callpass = 29322i64;
//! ```
//!
//! We can generate a `Callpass` like so, using the From trait:
//!
//! ```
//! # use callpass::Callpass;
//! # let given_callsign = "x2yz";
//! // This step will generate an APRS-IS passcode.
//! let callpass: Callpass = given_callsign.into();
//! ```
//!
//! If we already have an APRS-IS passcode as an integer, we can
//! make a `Callpass` from that as well to gain the benefits of type
//! checking:
//!
//! ```
//! # use callpass::Callpass;
//! # let given_callpass = 29322i64;
//! let their_callpass: Callpass = given_callpass.into();
//!
//! assert!(their_callpass == given_callpass);
//! ```

extern crate ascii;

#[macro_use]
mod impl_macro;

#[cfg(feature = "std")]
use ascii::AsAsciiStr;
use ascii::AsciiStr;
#[cfg(feature = "std")]
use std::fmt::{Display, Formatter, Result};

/// This type will generate and represent an APRS-IS passcode.
///
/// # Callpass generation
///
/// When given a callsign, an APRS-IS passcode will be generated.
///
/// ```
/// # use callpass::Callpass;
/// let callpass: Callpass = "x2yz".into();
/// ```
///
/// # Comparisons
///
/// Its value can be directly compared to other numbers.
///
/// ```
/// # use callpass::Callpass;
/// # let callpass: Callpass = "x2yz".into();
/// assert!(callpass == 29322);
/// ```
///
/// # Representation
///
/// A `Callpass` can be used in place of integers.
///
/// ```
/// # use callpass::Callpass;
/// # let callpass: Callpass = "x2yz".into();
/// let given_passcode: Callpass = 29322.into();
/// assert!(callpass == given_passcode);
/// ```
///
/// A `Callpass` can also be used as an integer where required.
///
/// ```
/// # use callpass::Callpass;
/// # let callpass: Callpass = "x2yz".into();
/// fn i64_eater(lunch: i64) {}
///
/// i64_eater(callpass.into())
/// ```
///
/// # Modification
///
/// A `Callpass` will not allow modification (without being casted).
///
/// ```compile_fail
/// # use callpass::Callpass;
/// # let callpass: Callpass = "x2yz".into();
/// let changed_callpass = callpass + 4;
/// ```
///
/// ```compile_fail
/// # use callpass::Callpass;
/// # let mut callpass: Callpass = "x2yz".into();
/// callpass.0 = 12345;
/// ```
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Callpass(u16);

impl Callpass {
    fn new(callsign: &AsciiStr) -> Callpass {
        Callpass({
            let (hash, _) = callsign.into_iter()
                // Callsigns are always uppercase,
                // and their u16 value is used for the hash
                .map(|c| {
                    let mut d = c.clone();
                    d.make_ascii_uppercase();
                    d.as_byte() as u16
                })
                // Seed value provided, and a loop switch
                .fold((0x73e2 as u16, true), |(hash, switch), input| {
                    let hash = match switch {
                        true => hash ^ input << 8,
                        false => hash ^ input,
                    };
                    (hash, !switch)
                });
            hash & 0x7fff
        })
    }
}

generate_impl_into!(u16, i16, u32, i32, u64, i64);

generate_impl_from!(u16, i16, u32, i32, u64, i64);

generate_impl_partialeq!(u16, i16, u32, i32, u64, i64);

#[cfg(feature = "std")]
impl Display for Callpass {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(f, "{:05}", self.0)
    }
}

#[cfg(feature = "std")]
impl Into<String> for Callpass {
    fn into(self) -> String {
        format!("{:05}", self.0)
    }
}

#[cfg(feature = "std")]
impl From<String> for Callpass {
    fn from(callsign: String) -> Self {
        match callsign.as_ascii_str() {
            Ok(ascii_call) => Callpass::new(&ascii_call),
            Err(err) => panic!("{}", &err),
        }
    }
}

#[cfg(feature = "std")]
impl<'a> From<&'a String> for Callpass {
    fn from(callsign: &'a String) -> Self {
        match callsign.as_ascii_str() {
            Ok(ascii_call) => Callpass::new(&ascii_call),
            Err(err) => panic!("{}", &err),
        }
    }
}

impl<'a> From<&'a str> for Callpass {
    fn from(callsign: &'a str) -> Self {
        match AsciiStr::from_ascii(callsign) {
            Ok(ascii_str) => Callpass::new(&ascii_str),
            Err(err) => panic!("{}", &err),
        }
    }
}