sota 0.9.1

API crate for Summits on the Air
Documentation
//! Statements of planned activations.

use std::{
    hash::{Hash, Hasher},
    str::FromStr,
};

use serde::{Deserialize, Serialize};
use serde_with::{serde_as, NoneAsEmptyString};
use time::OffsetDateTime;

use crate::{
    callsign::Callsign,
    summit::{HasSummit, SummitCode},
    ParseError,
};

use super::Notice;

/// A SOTA alert.
///
/// An alert is something like a future spot: it states that the named operator
/// plans to activate a summit at the given time.
#[serde_as]
#[derive(Debug, Clone, Deserialize, Serialize, Eq)]
#[serde(rename_all = "camelCase")]
pub struct Alert {
    /// Activator's callsign.
    pub activating_callsign: Callsign,
    /// Activator's name.
    pub activator_name: String,
    /// Association code of the summit, e.g. "W6".
    pub association_code: String,
    /// Short code of the summit, e.g. "CC-063".
    pub summit_code: String,
    /// A description of the summit, e.g., "Mount Tamalpais, 786m, 2 pts".
    pub summit_details: String,
    /// Freeform description of planned bands/modes.
    pub frequency: String,
    /// Time when the alert was posted.
    #[serde(with = "time::serde::rfc3339")]
    pub time_stamp: OffsetDateTime,
    /// Time for which the activation is planned.
    #[serde(with = "time::serde::rfc3339")]
    pub date_activated: OffsetDateTime,
    /// Poster's callsign.
    pub poster_callsign: Callsign,
    #[serde_as(as = "NoneAsEmptyString")]
    /// Comments by the poster.
    pub comments: Option<String>,
    /// A UUID by which clients should cache responses.
    pub epoch: String,
}

impl Alert {
    /// Returns the full code of the associated summit.
    // Necessary because this struct, unlike all others,
    // separates the association and summit codes.
    pub fn full_summit_code(&self) -> String {
        format!("{}/{}", self.association_code, self.summit_code)
    }
}

impl Notice for Alert {
    fn callsign(&self) -> &Callsign {
        &self.activating_callsign
    }

    fn epoch(&self) -> &str {
        &self.epoch
    }
}

impl HasSummit for Alert {
    fn summit_code(&self) -> Result<SummitCode, ParseError> {
        SummitCode::from_str(&self.full_summit_code())
    }
}

impl Hash for Alert {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.activating_callsign.hash(state);
        self.association_code.hash(state);
        self.summit_code.hash(state);
        self.frequency.hash(state);
        self.date_activated.hash(state);
    }
}

impl PartialEq for Alert {
    fn eq(&self, other: &Self) -> bool {
        self.activating_callsign == other.activating_callsign
            && self.association_code == other.association_code
            && self.summit_code == other.summit_code
            && self.frequency == other.frequency
            && self.time_stamp == other.time_stamp
    }
}