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
107
108
109
110
111
112
113
114
use std::{fmt::Display, str::FromStr};

use crate::ParseError;

#[derive(Debug)]
pub struct Event {
    pub kind: EventKind,
    pub params: Vec<String>,
    pub text: Option<String>,
}

#[derive(Debug)]
pub enum EventKind {
    /// Generic event.
    Message,

    /// Bookmarks are highlighted in the time line and in the event log. They are easy to spot and
    /// handy to highlight parts of the flight, like a bombing run, or when the trainee was in her
    /// final approach for landing.
    Bookmark,

    /// Debug events are highlighted and easy to spot in the timeline and event log. Because they
    /// must be used for development purposes, they are displayed only when launching Tacview with
    /// the command line argument /Debug:on
    Debug,

    /// This event is useful to specify when an aircraft (or any object) is cleanly removed from the
    /// battlefield (not destroyed). This prevents Tacview from generating a Destroyed event by
    /// error.
    LeftArea,

    /// When an object has been officially destroyed.
    Destroyed,

    /// Because Tacview may not always properly auto-detect take-off events, it can be useful to
    /// manually inject this event in the flight recording.
    TakenOff,

    /// Because Tacview may not always properly auto-detect landing events, it can be useful to
    /// manually inject this event in the flight recording.
    Landed,

    /// Mainly used for real-life training debriefing to specify when a weapon (typically a
    /// missile) reaches or misses its target. Tacview will report in the shot log as well as in the
    /// 3D view the result of the shot. Most parameters are optional. SourceId designates the object
    /// which has fired the weapon, while TargetId designates the target. Even if the displayed
    /// result may be in nautical miles, bullseye coordinates must be specified in meters. The
    /// target must be explicitly (manually) destroyed or disabled using the appropriate properties
    /// independently from this event.
    Timeout,

    /// Unknown event. This only exists for forward compatibility and using it is not recommended
    /// as the event you are using could be move to the known event in a future release.
    Unknown(String),
}

impl FromStr for Event {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut parts = s.split('|');
        let kind = parts.next().ok_or(ParseError::InvalidEvent)?;
        let kind = match kind {
            "Message" => EventKind::Message,
            "Bookmark" => EventKind::Bookmark,
            "Debug" => EventKind::Debug,
            "LeftArea" => EventKind::LeftArea,
            "Destroyed" => EventKind::Destroyed,
            "TakenOff" => EventKind::TakenOff,
            "Landed" => EventKind::Landed,
            "Timeout" => EventKind::Timeout,
            name => EventKind::Unknown(name.to_string()),
        };

        let mut params = parts.map(String::from).collect::<Vec<_>>();
        let text = if params.is_empty() {
            None
        } else {
            Some(params.remove(params.len() - 1)).filter(|s| !s.is_empty())
        };

        Ok(Event { kind, params, text })
    }
}

impl Display for Event {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "0,Event={}", self.kind.as_str())?;
        for param in &self.params {
            write!(f, "|{}", param)?;
        }
        if let Some(text) = &self.text {
            write!(f, "|{}", text)?;
        }
        Ok(())
    }
}

impl EventKind {
    fn as_str(&self) -> &str {
        use EventKind::*;
        match self {
            Message => "Message",
            Bookmark => "Bookmark",
            Debug => "Debug",
            LeftArea => "LeftArea",
            Destroyed => "Destroyed",
            TakenOff => "TakenOff",
            Landed => "Landed",
            Timeout => "Timeout",
            Unknown(name) => name,
        }
    }
}