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
use glob::glob;
use slack_hook::{PayloadBuilder, Slack};
use std::fs;
use std::fs::OpenOptions;
use std::io::prelude::*;
use std::io::Error;
use std::path::Path;

use crate::*;


/// Sends generic notification over Slack
pub fn notify(webhook: &str, channel: &str, message: &str, icon: &str) {
    Slack::new(webhook)
        .and_then(|slack| {
            PayloadBuilder::new()
                .text(message)
                .channel(channel)
                .username(DEFAULT_SLACK_NAME)
                .icon_emoji(icon)
                .build()
                .and_then(|payload| {
                    debug!("Sending notification with payload: {:?}", &payload);
                    slack.send(&payload)
                })
        })
        .unwrap_or_default(); // just ignore case when notification throws an error
}


/// Sends success notification to Slack
pub fn notify_success(webhook: &str, channel: &str, message: &str) {
    notify(webhook, channel, message, ":white_check_mark:")
}


/// Sends failure notification to Slack
pub fn notify_failure(webhook: &str, channel: &str, message: &str) {
    notify(webhook, channel, message, DEFAULT_SLACK_FAILURE_ICON)
}


/// Produce list of dirs/files matching given glob pattern:
pub fn produce_list(glob_pattern: &str) -> Vec<String> {
    let mut list = vec![];
    for entry in glob(&glob_pattern).unwrap() {
        match entry {
            Ok(path) => {
                if let Some(element) = path.file_name() {
                    element.to_str().and_then(|elem| {
                        list.push(elem.to_string());
                        Some(elem.to_string())
                    });
                }
            }
            Err(err) => {
                error!("Error: produce_list(): {}", err);
            }
        }
    }
    debug!("produce_list(): Elements: {:?}", list);
    list
}


/// List all check files found in default checks dir
pub fn list_check_files() -> Vec<String> {
    list_check_files_from(CHECKS_DIR)
}


/// List all check files from given dir
pub fn list_check_files_from(checks_dir: &str) -> Vec<String> {
    let glob_pattern = format!("{}/*.json", checks_dir);
    debug!("list_check_files(): {}", glob_pattern);
    produce_list(&glob_pattern)
}


/// Read text file
pub fn read_text_file(name: &str) -> Result<String, Error> {
    fs::read_to_string(name)
}


/// Write-once-and-atomic to a file
pub fn write_append(file_path: &str, contents: &str) {
    // NOTE: since file is written in "write only, all at once" mode, we have to be sure not to write empty buffer
    if !contents.is_empty() {
        let mut options = OpenOptions::new();
        match options.create(true).append(true).open(&file_path) {
            Ok(mut file) => {
                file.write_all(contents.as_bytes()).unwrap_or_else(|_| {
                    panic!("Access denied? File can't be written: {}", &file_path)
                });
                debug!("Atomically written data to file: {}", &file_path);
            }

            Err(err) => {
                error!(
                    "Atomic write to: {} has failed! Cause: {}",
                    &file_path,
                    err.to_string()
                )
            }
        }
    }
}


/// Extracts file name from full path
pub fn file_name_from_path(path: &str) -> String {
    let path = Path::new(path);
    path.file_name()
        .unwrap_or_default()
        .to_os_string()
        .into_string()
        .unwrap_or_default()
}