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
mod host;

use std::time::SystemTime;

use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::host::HostInfo;

#[derive(Debug, PartialEq, Deserialize, Serialize)]
pub struct Heartbeat {
    /// Heartbeat metadata
    pub heartbeat_info: HeartbeatInfo,
    /// Current AgentStatus
    pub status: AgentStatus,
    /// Host information
    pub host_info: Option<HostInfo>,
    /// Build information
    pub build_info: Option<BuildInfo>,
}

impl Heartbeat {
    pub fn new(hbtype: HeartbeatType, agent_status: Option<AgentStatus>) -> Heartbeat {
        let mut status_or_undefined = AgentStatus::Undefined;
        if let Some(value) = agent_status {
            status_or_undefined = value;
        }
        match hbtype {
            HeartbeatType::Full => Heartbeat {
                heartbeat_info: HeartbeatInfo::new(hbtype),
                status: status_or_undefined,
                host_info: Some(HostInfo::new()),
                build_info: Some(BuildInfo::new()),
            },
            HeartbeatType::Compact => {
                // TODO: Needs logic to actually determine what to include or not.
                // See https://linear.app/netzwork/issue/NET-30/api-support-sending-compact-heartbeats-to-save-bandwidth
                todo!()
            }
        }
    }
}

#[derive(Debug, PartialEq, Deserialize, Serialize)]
pub struct HeartbeatInfo {
    /// Heartbeat type
    pub hbtype: HeartbeatType,
    /// UUID (v4) of this heartbeat
    pub heartbeat_uuid: Uuid,
    /// Current timestamp
    pub timestamp: SystemTime,
}
impl HeartbeatInfo {
    fn new(hbtype: HeartbeatType) -> HeartbeatInfo {
        HeartbeatInfo {
            hbtype,
            heartbeat_uuid: Uuid::new_v4(),
            timestamp: SystemTime::now(),
        }
    }
}

#[derive(Debug, PartialEq, Deserialize, Serialize)]
pub enum HeartbeatType {
    /// A full heartbeat contains information in all data structures
    Full,
    /// A compact heartbeat contains a minimal amount of information,
    /// indicating that information is unchanged compared to the previous heartbeat.
    Compact,
}

#[derive(Debug, PartialEq, Deserialize, Serialize)]
pub enum AgentStatus {
    Undefined,
    Initializing,
    AwaitingJoin,
    Running,
    Orphaned,
    Exiting,
}

#[derive(Debug, PartialEq, Deserialize, Serialize)]
pub struct BuildInfo {
    // TODO: Include API crate version here
    // See: https://linear.app/netzwork/issue/NET-31/api-include-api-crate-version-in-the-buildinfo-struct
    build_timestamp: String,
    build_date: String,
    git_branch: String,
    git_timestamp: String,
    git_date: String,
    git_hash: String,
    git_describe: String,
    rustc_host_triple: String,
    rustc_version: String,
    cargo_target_triple: String,
}

impl BuildInfo {
    fn new() -> BuildInfo {
        BuildInfo {
            build_timestamp: String::from(env!("VERGEN_BUILD_TIMESTAMP")),
            build_date: String::from(env!("VERGEN_BUILD_DATE")),
            git_branch: String::from(env!("VERGEN_GIT_BRANCH")),
            git_timestamp: String::from(env!("VERGEN_GIT_COMMIT_TIMESTAMP")),
            git_date: String::from(env!("VERGEN_GIT_COMMIT_DATE")),
            git_hash: String::from(env!("VERGEN_GIT_SHA")),
            git_describe: String::from(env!("VERGEN_GIT_DESCRIBE")),
            rustc_host_triple: String::from(env!("VERGEN_RUSTC_HOST_TRIPLE")),
            rustc_version: String::from(env!("VERGEN_RUSTC_SEMVER")),
            cargo_target_triple: String::from(env!("VERGEN_CARGO_TARGET_TRIPLE")),
        }
    }
}