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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! Common definitions for the client and server.

use std::collections::HashMap;

use chrono::{offset::Utc, DateTime};

use itertools::Itertools;

use log::error;

pub mod protocol {
    include!(concat!(env!("OUT_DIR"), "/jobserver_proto.rs"));

    impl From<&Vec<String>> for MatrixVarValues {
        fn from(vec: &Vec<String>) -> Self {
            MatrixVarValues {
                values: vec.clone(),
            }
        }
    }

    impl From<&MatrixVarValues> for Vec<String> {
        fn from(mvv: &MatrixVarValues) -> Self {
            mvv.values.clone()
        }
    }

    pub fn convert_map(
        map: &std::collections::HashMap<String, Vec<String>>,
    ) -> std::collections::HashMap<String, MatrixVarValues> {
        map.iter().map(|(k, v)| (k.clone(), v.into())).collect()
    }

    pub fn reverse_map(
        map: &std::collections::HashMap<String, MatrixVarValues>,
    ) -> std::collections::HashMap<String, Vec<String>> {
        map.into_iter()
            .map(|(k, v)| (k.clone(), v.into()))
            .collect()
    }
}

/// The address where the server listens.
pub const SERVER_ADDR: &str = "127.0.0.1:3030";

pub fn cmd_replace_vars(cmd: &str, vars: &HashMap<String, String>) -> String {
    vars.iter().fold(cmd.to_string(), |cmd, (key, value)| {
        cmd.replace(&format!("{{{}}}", key), &value)
    })
}

pub fn cmd_replace_machine(cmd: &str, machine: &str) -> String {
    cmd.replace("{MACHINE}", &machine)
}

pub fn cmd_to_path(jid: u64, cmd: &str, log_dir: &str) -> String {
    let mut name = format!(
        "{}/{}-{}",
        log_dir,
        jid,
        cmd.replace(" ", "_")
            .replace("{", "_")
            .replace("}", "_")
            .replace("/", "_")
    );
    name.truncate(200);
    name
}

// Gets the cartesian product of the given set of variables and their sets of possible values.
pub fn cartesian_product<'v>(
    vars: &'v HashMap<String, Vec<String>>,
) -> impl Iterator<Item = HashMap<String, String>> + 'v {
    vars.iter()
        .map(|(k, vs)| vs.iter().map(move |v| (k.clone(), v.clone())))
        .multi_cartesian_product()
        .map(|config: Vec<(String, String)>| config.into_iter().collect())
}

const TS_FORMAT: &str = "%+"; // ISO 8601 format

pub fn serialize_ts(ts: DateTime<Utc>) -> String {
    ts.format(TS_FORMAT).to_string()
}

pub fn deserialize_ts(s: String) -> DateTime<Utc> {
    DateTime::parse_from_str(&s, TS_FORMAT)
        .map(|dt| dt.with_timezone(&Utc))
        .unwrap_or_else(|err| {
            error!("Unable to deserialize timestamp: {:?}, {}", s, err);
            Utc::now()
        })
}

pub fn human_ts(d: chrono::Duration) -> String {
    let total_seconds = d.num_seconds();

    const SECONDS_PER_MINUTE: i64 = 60;
    const SECONDS_PER_HOUR: i64 = 60 * SECONDS_PER_MINUTE;
    const SECONDS_PER_DAY: i64 = 24 * SECONDS_PER_HOUR;

    let display_days = d.num_days();
    let display_hours = (total_seconds - display_days * SECONDS_PER_DAY) / SECONDS_PER_HOUR;
    let display_minutes =
        (total_seconds - display_days * SECONDS_PER_DAY - display_hours * SECONDS_PER_HOUR)
            / SECONDS_PER_MINUTE;
    let display_seconds = total_seconds
        - display_days * SECONDS_PER_DAY
        - display_hours * SECONDS_PER_HOUR
        - display_minutes * SECONDS_PER_MINUTE;

    let mut display_ts = String::new();

    if display_days > 0 {
        display_ts.push_str(&format!("{}d", display_days));
    }

    if display_hours > 0 {
        display_ts.push_str(&format!("{}h", display_hours));
    }

    if display_minutes > 0 && display_days == 0 {
        display_ts.push_str(&format!("{}m", display_minutes));
    }

    if display_seconds > 0 && display_days == 0 && display_hours == 0 {
        display_ts.push_str(&format!("{}s", display_seconds));
    }

    if total_seconds == 0 {
        display_ts.push_str("0s");
    }

    display_ts
}