<commit_before>//
// Sysinfo
//
// Copyright (c) 2017 Guillaume Gomez
//

#![crate_type = "bin"]
#![allow(unused_must_use, non_upper_case_globals)]

extern crate sysinfo;

use std::io::{self, BufRead, Write};
use std::str::FromStr;
use sysinfo::Signal::*;
use sysinfo::{NetworkExt, NetworksExt, Pid, ProcessExt, ProcessorExt, Signal, System, SystemExt};

const signals: [Signal; 31] = [
    Hangup,
    Interrupt,
    Quit,
    Illegal,
    Trap,
    Abort,
    Bus,
    FloatingPointException,
    Kill,
    User1,
    Segv,
    User2,
    Pipe,
    Alarm,
    Term,
    Stklft,
    Child,
    Continue,
    Stop,
    TSTP,
    TTIN,
    TTOU,
    Urgent,
    XCPU,
    XFSZ,
    VirtualAlarm,
    Profiling,
    Winch,
    IO,
    Power,
    Sys,
];

fn print_help() {
    writeln!(&mut io::stdout(), "== Help menu ==");
    writeln!(&mut io::stdout(), "help               : show this menu");
    writeln!(
        &mut io::stdout(),
        "signals            : show the available signals"
    );
    writeln!(
        &mut io::stdout(),
        "refresh            : reloads all processes' information"
    );
    writeln!(
        &mut io::stdout(),
        "refresh [pid]      : reloads corresponding process' information"
    );
    writeln!(
        &mut io::stdout(),
        "refresh_disks      : reloads only disks' information"
    );
    writeln!(
        &mut io::stdout(),
        "show [pid | name]  : show information of the given process \
                                 corresponding to [pid | name]"
    );
    writeln!(
        &mut io::stdout(),
        "kill [pid] [signal]: send [signal] to the process with this \
                                 [pid]. 0 < [signal] < 32"
    );
    writeln!(
        &mut io::stdout(),
        "procd              : Displays processors state"
    );
    writeln!(
        &mut io::stdout(),
        "memory             : Displays memory state"
    );
    writeln!(
        &mut io::stdout(),
        "temperature        : Displays components' temperature"
    );
    writeln!(
        &mut io::stdout(),
        "disks              : Displays disks' information"
    );
    writeln!(
        &mut io::stdout(),
        "network            : Displays network' information"
    );
    writeln!(
        &mut io::stdout(),
        "all                : Displays all process name and pid"
    );
    writeln!(
        &mut io::stdout(),
        "uptime             : Displays system uptime"
    );
    writeln!(
        &mut io::stdout(),
        "vendor_id          : Displays processor vendor id"
    );
    writeln!(
        &mut io::stdout(),
        "brand              : Displays processor brand"
    );
    writeln!(
        &mut io::stdout(),
        "load_avg           : Displays system load average"
    );
    writeln!(
        &mut io::stdout(),
        "frequency          : Displays processor frequency"
    );
    writeln!(&mut io::stdout(), "quit               : exit the program");
}

fn interpret_input(input: &str, sys: &mut System) -> bool {
    match input.trim() {
        "help" => print_help(),
        "refresh_disks" => {
            writeln!(&mut io::stdout(), "Refreshing disk list...");
            sys.refresh_disk_list();
            writeln!(&mut io::stdout(), "Done.");
        }
        "signals" => {
            let mut nb = 1i32;

            for sig in &signals {
                writeln!(&mut io::stdout(), "{:2}:{:?}", nb, sig);
                nb += 1;
            }
        }
        "procs" => {
            // Note: you should refresh a few times before using this, so that usage statistics
            // can be ascertained
            let procs = sys.get_processor_list();

            writeln!(
                &mut io::stdout(),
                "total process usage: {}%",
                procs[0].get_cpu_usage()
            );
            for proc_ in procs.iter().skip(1) {
                writeln!(&mut io::stdout(), "{:?}", proc_);
            }
        }
        "memory" => {
            writeln!(
                &mut io::stdout(),
                "total memory: {} KiB",
                sys.get_total_memory()
            );
            writeln!(
                &mut io::stdout(),
                "used memory : {} KiB",
                sys.get_used_memory()
            );
            writeln!(
                &mut io::stdout(),
                "total swap  : {} KiB",
                sys.get_total_swap()
            );
            writeln!(
                &mut io::stdout(),
                "used swap   : {} KiB",
                sys.get_used_swap()
            );
        }
        "quit" | "exit" => return true,
        "all" => {
            for (pid, proc_) in sys.get_process_list() {
                writeln!(
                    &mut io::stdout(),
                    "{}:{} status={:?}",
                    pid,
                    proc_.name(),
                    proc_.status()
                );
            }
        }
        "frequency" => {
            let procs = sys.get_processor_list();
            // On windows, the first processor is the "all processors", so not interesting in here.
            writeln!(
                &mut io::stdout(),
                "{} MHz",
                procs[if procs.len() > 1 { 1 } else { 0 }].get_frequency()
            );
        }
        "vendor_id" => {
            writeln!(
                &mut io::stdout(),
                "vendor ID: {}",
                sys.get_processor_list()[0].get_vendor_id()
            );
        }
        "brand" => {
            writeln!(
                &mut io::stdout(),
                "brand: {}",
                sys.get_processor_list()[0].get_brand()
            );
        }
        "load_avg" => {
            let load_avg = sys.get_load_average();
            writeln!(&mut io::stdout(), "one minute     : {}%", load_avg.one);
            writeln!(&mut io::stdout(), "five minutes   : {}%", load_avg.five);
            writeln!(&mut io::stdout(), "fifteen minutes: {}%", load_avg.fifteen);
        }
        e if e.starts_with("show ") => {
            let tmp: Vec<&str> = e.split(' ').collect();

            if tmp.len() != 2 {
                writeln!(
                    &mut io::stdout(),
                    "show command takes a pid or a name in parameter!"
                );
                writeln!(&mut io::stdout(), "example: show 1254");
            } else if let Ok(pid) = Pid::from_str(tmp[1]) {
                match sys.get_process(pid) {
                    Some(p) => writeln!(&mut io::stdout(), "{:?}", *p),
                    None => writeln!(&mut io::stdout(), "pid \"{:?}\" not found", pid),
                };
            } else {
                let proc_name = tmp[1];
                for proc_ in sys.get_process_by_name(proc_name) {
                    writeln!(&mut io::stdout(), "==== {} ====", proc_.name());
                    writeln!(&mut io::stdout(), "{:?}", proc_);
                }
            }
        }
        "temperature" => {
            for component in sys.get_components_list() {
                writeln!(&mut io::stdout(), "{:?}", component);
            }
        }
        "network" => {
            for (interface_name, data) in sys.get_networks().iter() {
                writeln!(
                    &mut io::stdout(),
                    "{}:\n  input data:  {} B\n  output data: {} B",
                    interface_name,
                    data.get_income(),
                    data.get_outcome()
                );
            }
        }
        "show" => {
            writeln!(
                &mut io::stdout(),
                "'show' command expects a pid number or a process name"
            );
        }
        e if e.starts_with("kill ") => {
            let tmp: Vec<&str> = e.split(' ').collect();

            if tmp.len() != 3 {
                writeln!(
                    &mut io::stdout(),
                    "kill command takes the pid and a signal number in parameter !"
                );
                writeln!(&mut io::stdout(), "example: kill 1254 9");
            } else {
                let pid = Pid::from_str(tmp[1]).unwrap();
                let signal = i32::from_str(tmp[2]).unwrap();

                if signal < 1 || signal > 31 {
                    writeln!(
                        &mut io::stdout(),
                        "Signal must be between 0 and 32 ! See the signals list with the \
                              signals command"
                    );
                } else {
                    match sys.get_process(pid) {
                        Some(p) => {
                            writeln!(
                                &mut io::stdout(),
                                "kill: {}",
                                p.kill(*signals.get(signal as usize - 1).unwrap())
                            );
                        }
                        None => {
                            writeln!(&mut io::stdout(), "pid not found");
                        }
                    };
                }
            }
        }
        "disks" => {
            for disk in sys.get_disks() {
                writeln!(&mut io::stdout(), "{:?}", disk);
            }
        }
        "uptime" => {
            let mut uptime = sys.get_uptime();
            let days = uptime / 86400;
            uptime -= days * 86400;
            let hours = uptime / 3600;
            uptime -= hours * 3600;
            let minutes = uptime / 60;
            writeln!(
                &mut io::stdout(),
                "{} days {} hours {} minutes",
                days,
                hours,
                minutes
            );
        }
        x if x.starts_with("refresh") => {
            if x == "refresh" {
                writeln!(&mut io::stdout(), "Getting processes' information...");
                sys.refresh_all();
                writeln!(&mut io::stdout(), "Done.");
            } else if x.starts_with("refresh ") {
                writeln!(&mut io::stdout(), "Getting process' information...");
                if let Some(pid) = x
                    .split(' ')
                    .filter_map(|pid| pid.parse().ok())
                    .take(1)
                    .next()
                {
                    if sys.refresh_process(pid) {
                        writeln!(&mut io::stdout(), "Process `{}` updated successfully", pid);
                    } else {
                        writeln!(
                            &mut io::stdout(),
                            "Process `{}` couldn't be updated...",
                            pid
                        );
                    }
                } else {
                    writeln!(&mut io::stdout(), "Invalid [pid] received...");
                }
            } else {
                writeln!(
                    &mut io::stdout(),
                    "\"{}\": Unknown command. Enter 'help' if you want to get the commands' \
                      list.",
                    x
                );
            }
        }
        e => {
            writeln!(
                &mut io::stdout(),
                "\"{}\": Unknown command. Enter 'help' if you want to get the commands' \
                      list.",
                e
            );
        }
    }
    false
}

fn main() {
    println!("Getting processes' information...");
    let mut t = System::new();
    println!("Done.");
    let t_stin = io::stdin();
    let mut stin = t_stin.lock();
    let mut done = false;

    println!("To get the commands' list, enter 'help'.");
    while !done {
        let mut input = String::new();
        write!(&mut io::stdout(), "> ");
        io::stdout().flush();

        stin.read_line(&mut input);
        if input.is_empty() {
            // The string is empty, meaning there is no '\n', meaning
            // that the user used CTRL+D so we can just quit!
            println!("\nLeaving, bye!");
            break;
        }
        if (&input as &str).ends_with('\n') {
            input.pop();
        }
        done = interpret_input(input.as_ref(), &mut t);
    }
}
<commit_msg>Improve network example<commit_after>//
// Sysinfo
//
// Copyright (c) 2017 Guillaume Gomez
//

#![crate_type = "bin"]
#![allow(unused_must_use, non_upper_case_globals)]

extern crate sysinfo;

use std::io::{self, BufRead, Write};
use std::str::FromStr;
use sysinfo::Signal::*;
use sysinfo::{NetworkExt, NetworksExt, Pid, ProcessExt, ProcessorExt, Signal, System, SystemExt};

const signals: [Signal; 31] = [
    Hangup,
    Interrupt,
    Quit,
    Illegal,
    Trap,
    Abort,
    Bus,
    FloatingPointException,
    Kill,
    User1,
    Segv,
    User2,
    Pipe,
    Alarm,
    Term,
    Stklft,
    Child,
    Continue,
    Stop,
    TSTP,
    TTIN,
    TTOU,
    Urgent,
    XCPU,
    XFSZ,
    VirtualAlarm,
    Profiling,
    Winch,
    IO,
    Power,
    Sys,
];

fn print_help() {
    writeln!(&mut io::stdout(), "== Help menu ==");
    writeln!(&mut io::stdout(), "help               : show this menu");
    writeln!(
        &mut io::stdout(),
        "signals            : show the available signals"
    );
    writeln!(
        &mut io::stdout(),
        "refresh            : reloads all processes' information"
    );
    writeln!(
        &mut io::stdout(),
        "refresh [pid]      : reloads corresponding process' information"
    );
    writeln!(
        &mut io::stdout(),
        "refresh_disks      : reloads only disks' information"
    );
    writeln!(
        &mut io::stdout(),
        "show [pid | name]  : show information of the given process \
                                 corresponding to [pid | name]"
    );
    writeln!(
        &mut io::stdout(),
        "kill [pid] [signal]: send [signal] to the process with this \
                                 [pid]. 0 < [signal] < 32"
    );
    writeln!(
        &mut io::stdout(),
        "procd              : Displays processors state"
    );
    writeln!(
        &mut io::stdout(),
        "memory             : Displays memory state"
    );
    writeln!(
        &mut io::stdout(),
        "temperature        : Displays components' temperature"
    );
    writeln!(
        &mut io::stdout(),
        "disks              : Displays disks' information"
    );
    writeln!(
        &mut io::stdout(),
        "network            : Displays network' information"
    );
    writeln!(
        &mut io::stdout(),
        "all                : Displays all process name and pid"
    );
    writeln!(
        &mut io::stdout(),
        "uptime             : Displays system uptime"
    );
    writeln!(
        &mut io::stdout(),
        "vendor_id          : Displays processor vendor id"
    );
    writeln!(
        &mut io::stdout(),
        "brand              : Displays processor brand"
    );
    writeln!(
        &mut io::stdout(),
        "load_avg           : Displays system load average"
    );
    writeln!(
        &mut io::stdout(),
        "frequency          : Displays processor frequency"
    );
    writeln!(&mut io::stdout(), "quit               : exit the program");
}

fn interpret_input(input: &str, sys: &mut System) -> bool {
    match input.trim() {
        "help" => print_help(),
        "refresh_disks" => {
            writeln!(&mut io::stdout(), "Refreshing disk list...");
            sys.refresh_disk_list();
            writeln!(&mut io::stdout(), "Done.");
        }
        "signals" => {
            let mut nb = 1i32;

            for sig in &signals {
                writeln!(&mut io::stdout(), "{:2}:{:?}", nb, sig);
                nb += 1;
            }
        }
        "procs" => {
            // Note: you should refresh a few times before using this, so that usage statistics
            // can be ascertained
            let procs = sys.get_processor_list();

            writeln!(
                &mut io::stdout(),
                "total process usage: {}%",
                procs[0].get_cpu_usage()
            );
            for proc_ in procs.iter().skip(1) {
                writeln!(&mut io::stdout(), "{:?}", proc_);
            }
        }
        "memory" => {
            writeln!(
                &mut io::stdout(),
                "total memory: {} KiB",
                sys.get_total_memory()
            );
            writeln!(
                &mut io::stdout(),
                "used memory : {} KiB",
                sys.get_used_memory()
            );
            writeln!(
                &mut io::stdout(),
                "total swap  : {} KiB",
                sys.get_total_swap()
            );
            writeln!(
                &mut io::stdout(),
                "used swap   : {} KiB",
                sys.get_used_swap()
            );
        }
        "quit" | "exit" => return true,
        "all" => {
            for (pid, proc_) in sys.get_process_list() {
                writeln!(
                    &mut io::stdout(),
                    "{}:{} status={:?}",
                    pid,
                    proc_.name(),
                    proc_.status()
                );
            }
        }
        "frequency" => {
            let procs = sys.get_processor_list();
            // On windows, the first processor is the "all processors", so not interesting in here.
            writeln!(
                &mut io::stdout(),
                "{} MHz",
                procs[if procs.len() > 1 { 1 } else { 0 }].get_frequency()
            );
        }
        "vendor_id" => {
            writeln!(
                &mut io::stdout(),
                "vendor ID: {}",
                sys.get_processor_list()[0].get_vendor_id()
            );
        }
        "brand" => {
            writeln!(
                &mut io::stdout(),
                "brand: {}",
                sys.get_processor_list()[0].get_brand()
            );
        }
        "load_avg" => {
            let load_avg = sys.get_load_average();
            writeln!(&mut io::stdout(), "one minute     : {}%", load_avg.one);
            writeln!(&mut io::stdout(), "five minutes   : {}%", load_avg.five);
            writeln!(&mut io::stdout(), "fifteen minutes: {}%", load_avg.fifteen);
        }
        e if e.starts_with("show ") => {
            let tmp: Vec<&str> = e.split(' ').collect();

            if tmp.len() != 2 {
                writeln!(
                    &mut io::stdout(),
                    "show command takes a pid or a name in parameter!"
                );
                writeln!(&mut io::stdout(), "example: show 1254");
            } else if let Ok(pid) = Pid::from_str(tmp[1]) {
                match sys.get_process(pid) {
                    Some(p) => writeln!(&mut io::stdout(), "{:?}", *p),
                    None => writeln!(&mut io::stdout(), "pid \"{:?}\" not found", pid),
                };
            } else {
                let proc_name = tmp[1];
                for proc_ in sys.get_process_by_name(proc_name) {
                    writeln!(&mut io::stdout(), "==== {} ====", proc_.name());
                    writeln!(&mut io::stdout(), "{:?}", proc_);
                }
            }
        }
        "temperature" => {
            for component in sys.get_components_list() {
                writeln!(&mut io::stdout(), "{:?}", component);
            }
        }
        "network" => {
            for (interface_name, data) in sys.get_networks().iter() {
                writeln!(
                    &mut io::stdout(),
                    "{}:\n  input data  (new / total): {} / {} B\n  output data (new / total): {} / {} B",
                    interface_name,
                    data.get_income(),
                    data.get_total_income(),
                    data.get_outcome(),
                    data.get_total_outcome(),
                );
            }
        }
        "show" => {
            writeln!(
                &mut io::stdout(),
                "'show' command expects a pid number or a process name"
            );
        }
        e if e.starts_with("kill ") => {
            let tmp: Vec<&str> = e.split(' ').collect();

            if tmp.len() != 3 {
                writeln!(
                    &mut io::stdout(),
                    "kill command takes the pid and a signal number in parameter !"
                );
                writeln!(&mut io::stdout(), "example: kill 1254 9");
            } else {
                let pid = Pid::from_str(tmp[1]).unwrap();
                let signal = i32::from_str(tmp[2]).unwrap();

                if signal < 1 || signal > 31 {
                    writeln!(
                        &mut io::stdout(),
                        "Signal must be between 0 and 32 ! See the signals list with the \
                              signals command"
                    );
                } else {
                    match sys.get_process(pid) {
                        Some(p) => {
                            writeln!(
                                &mut io::stdout(),
                                "kill: {}",
                                p.kill(*signals.get(signal as usize - 1).unwrap())
                            );
                        }
                        None => {
                            writeln!(&mut io::stdout(), "pid not found");
                        }
                    };
                }
            }
        }
        "disks" => {
            for disk in sys.get_disks() {
                writeln!(&mut io::stdout(), "{:?}", disk);
            }
        }
        "uptime" => {
            let mut uptime = sys.get_uptime();
            let days = uptime / 86400;
            uptime -= days * 86400;
            let hours = uptime / 3600;
            uptime -= hours * 3600;
            let minutes = uptime / 60;
            writeln!(
                &mut io::stdout(),
                "{} days {} hours {} minutes",
                days,
                hours,
                minutes
            );
        }
        x if x.starts_with("refresh") => {
            if x == "refresh" {
                writeln!(&mut io::stdout(), "Getting processes' information...");
                sys.refresh_all();
                writeln!(&mut io::stdout(), "Done.");
            } else if x.starts_with("refresh ") {
                writeln!(&mut io::stdout(), "Getting process' information...");
                if let Some(pid) = x
                    .split(' ')
                    .filter_map(|pid| pid.parse().ok())
                    .take(1)
                    .next()
                {
                    if sys.refresh_process(pid) {
                        writeln!(&mut io::stdout(), "Process `{}` updated successfully", pid);
                    } else {
                        writeln!(
                            &mut io::stdout(),
                            "Process `{}` couldn't be updated...",
                            pid
                        );
                    }
                } else {
                    writeln!(&mut io::stdout(), "Invalid [pid] received...");
                }
            } else {
                writeln!(
                    &mut io::stdout(),
                    "\"{}\": Unknown command. Enter 'help' if you want to get the commands' \
                      list.",
                    x
                );
            }
        }
        e => {
            writeln!(
                &mut io::stdout(),
                "\"{}\": Unknown command. Enter 'help' if you want to get the commands' \
                      list.",
                e
            );
        }
    }
    false
}

fn main() {
    println!("Getting processes' information...");
    let mut t = System::new();
    println!("Done.");
    let t_stin = io::stdin();
    let mut stin = t_stin.lock();
    let mut done = false;

    println!("To get the commands' list, enter 'help'.");
    while !done {
        let mut input = String::new();
        write!(&mut io::stdout(), "> ");
        io::stdout().flush();

        stin.read_line(&mut input);
        if input.is_empty() {
            // The string is empty, meaning there is no '\n', meaning
            // that the user used CTRL+D so we can just quit!
            println!("\nLeaving, bye!");
            break;
        }
        if (&input as &str).ends_with('\n') {
            input.pop();
        }
        done = interpret_input(input.as_ref(), &mut t);
    }
}
<|endoftext|>


<commit_before>#![allow(deprecated)]
use config::{Config, File};
use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};
use std::collections::HashMap;
use std::sync::mpsc::channel;
use std::sync::RwLock;
use std::time::Duration;

lazy_static::lazy_static! {
    static ref SETTINGS: RwLock<Config> = RwLock::new({
        let mut settings = Config::default();
        settings.merge(File::with_name("examples/watch/Settings.toml")).unwrap();

        settings
    });
}

fn show() {
    println!(
        " * Settings :: \n\x1b[31m{:?}\x1b[0m",
        SETTINGS
            .read()
            .unwrap()
            .clone()
            .try_deserialize::<HashMap<String, String>>()
            .unwrap()
    );
}

fn watch() {
    // Create a channel to receive the events.
    let (tx, rx) = channel();

    // Automatically select the best implementation for your platform.
    // You can also access each implementation directly e.g. INotifyWatcher.
    let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2)).unwrap();

    // Add a path to be watched. All files and directories at that path and
    // below will be monitored for changes.
    watcher
        .watch("examples/watch/Settings.toml", RecursiveMode::NonRecursive)
        .unwrap();

    // This is a simple loop, but you may want to use more complex logic here,
    // for example to handle I/O.
    loop {
        match rx.recv() {
            Ok(DebouncedEvent::Write(_)) => {
                println!(" * Settings.toml written; refreshing configuration ...");
                SETTINGS.write().unwrap().refresh().unwrap();
                show();
            }

            Err(e) => println!("watch error: {:?}", e),

            _ => {
                // Ignore event
            }
        }
    }
}

fn main() {
    // This is just an example of what could be done, today
    // We do want this to be built-in to config-rs at some point
    // Feel free to take a crack at a PR

    show();
    watch();
}
<commit_msg>Adapt example to new notify interface<commit_after>#![allow(deprecated)]
use config::{Config, File};
use notify::{Event, RecommendedWatcher, RecursiveMode, Watcher};
use std::collections::HashMap;
use std::path::Path;
use std::sync::mpsc::channel;
use std::sync::RwLock;
use std::time::Duration;

lazy_static::lazy_static! {
    static ref SETTINGS: RwLock<Config> = RwLock::new({
        let mut settings = Config::default();
        settings.merge(File::with_name("examples/watch/Settings.toml")).unwrap();

        settings
    });
}

fn show() {
    println!(
        " * Settings :: \n\x1b[31m{:?}\x1b[0m",
        SETTINGS
            .read()
            .unwrap()
            .clone()
            .try_deserialize::<HashMap<String, String>>()
            .unwrap()
    );
}

fn watch() {
    // Create a channel to receive the events.
    let (tx, rx) = channel();

    // Automatically select the best implementation for your platform.
    // You can also access each implementation directly e.g. INotifyWatcher.
    let mut watcher: RecommendedWatcher = Watcher::new(
        tx,
        notify::Config::default().with_poll_interval(Duration::from_secs(2)),
    )
    .unwrap();

    // Add a path to be watched. All files and directories at that path and
    // below will be monitored for changes.
    watcher
        .watch(
            Path::new("examples/watch/Settings.toml"),
            RecursiveMode::NonRecursive,
        )
        .unwrap();

    // This is a simple loop, but you may want to use more complex logic here,
    // for example to handle I/O.
    loop {
        match rx.recv() {
            Ok(Ok(Event {
                kind: notify::event::EventKind::Modify(_),
                ..
            })) => {
                println!(" * Settings.toml written; refreshing configuration ...");
                SETTINGS.write().unwrap().refresh().unwrap();
                show();
            }

            Err(e) => println!("watch error: {:?}", e),

            _ => {
                // Ignore event
            }
        }
    }
}

fn main() {
    // This is just an example of what could be done, today
    // We do want this to be built-in to config-rs at some point
    // Feel free to take a crack at a PR

    show();
    watch();
}
<|endoftext|>


<commit_before>extern crate directories;

#[cfg(target_family = "unix")]
use directories::{BaseDirs, ProjectDirs};

use std::fs::{create_dir_all, File};
use std::io::{ErrorKind, Read};
use std::path::PathBuf;
use std::process::Command;

#[cfg(target_family = "unix")]
use std::fs::read_dir;
#[cfg(target_family = "unix")]
use std::io::Write;

fn generate_resources() {
    let target_path = PathBuf::from("target").join("resources");
    create_dir_all(&target_path).unwrap();

    // Icons
    let input_path = PathBuf::from("res").join("icons").join("hicolor");

    let mut compile_res = Command::new("glib-compile-resources");
    compile_res
        .arg("--generate")
        .arg(format!("--sourcedir={}", input_path.to_str().unwrap()))
        .arg(format!(
            "--target={}",
            target_path.join("icons.gresource").to_str().unwrap(),
        ))
        .arg(input_path.join("icons.gresource.xml").to_str().unwrap());

    match compile_res.status() {
        Ok(status) => {
            if !status.success() {
                panic!(format!(
                    "Failed to generate resources file for icons\n{:?}",
                    compile_res,
                ));
            }
        }
        Err(ref error) => match error.kind() {
            ErrorKind::NotFound => panic!(
                "Can't generate icon resources: command `glib-compile-resources` not available"
            ),
            _ => panic!("Error invoking `compile_res`: {}", error),
        },
    }

    // UI
    let input_path = PathBuf::from("res").join("ui");

    let mut compile_res = Command::new("glib-compile-resources");
    compile_res
        .arg("--generate")
        .arg(format!("--sourcedir={}", input_path.to_str().unwrap()))
        .arg(format!(
            "--target={}",
            target_path.join("ui.gresource").to_str().unwrap(),
        ))
        .arg(input_path.join("ui.gresource.xml").to_str().unwrap());

    match compile_res.status() {
        Ok(status) => {
            if !status.success() {
                panic!(format!(
                    "Failed to generate resources file for the UI\n{:?}",
                    compile_res,
                ));
            }
        }
        Err(ref error) => match error.kind() {
            ErrorKind::NotFound => panic!(
                "Can't generate UI resources: command `glib-compile-resources` not available"
            ),
            _ => panic!("Error invoking `compile_res`: {}", error),
        },
    }
}

fn generate_translations() {
    if let Ok(mut linguas_file) = File::open(&PathBuf::from("po").join("LINGUAS")) {
        let mut linguas = String::new();
        linguas_file
            .read_to_string(&mut linguas)
            .expect("Couldn't read po/LINGUAS as string");

        for lingua in linguas.lines() {
            let mo_path = PathBuf::from("target")
                .join("locale")
                .join(lingua)
                .join("LC_MESSAGES");
            create_dir_all(&mo_path).unwrap();

            let mut msgfmt = Command::new("msgfmt");
            msgfmt
                .arg(format!(
                    "--output-file={}",
                    mo_path.join("media-toc.mo").to_str().unwrap()
                ))
                .arg("--directory=po")
                .arg(format!("{}.po", lingua));

            match msgfmt.status() {
                Ok(status) => {
                    if !status.success() {
                        panic!(format!(
                            "Failed to generate mo file for lingua {}\n{:?}",
                            lingua, msgfmt,
                        ));
                    }
                }
                Err(ref error) => match error.kind() {
                    ErrorKind::NotFound => {
                        eprintln!("Can't generate translations: command `msgfmt` not available");
                        return;
                    }
                    _ => panic!("Error invoking `msgfmt`: {}", error),
                },
            }
        }
    }
}

// FIXME: figure out macOS conventions for icons & translations
#[cfg(target_family = "unix")]
fn generate_install_script() {
    let base_dirs = BaseDirs::new().unwrap();
    // Note: `base_dirs.executable_dir()` is `None` on macOS
    if let Some(exe_dir) = base_dirs.executable_dir() {
        let project_dirs = ProjectDirs::from("org", "fengalin", env!("CARGO_PKG_NAME")).unwrap();
        let app_data_dir = project_dirs.data_dir();
        let data_dir = app_data_dir.parent().unwrap();

        match File::create(&PathBuf::from("target").join("install")) {
            Ok(mut install_file) => {
                install_file
                    .write_all(
                        format!("# User install script for {}\n", env!("CARGO_PKG_NAME"))
                            .as_bytes(),
                    )
                    .unwrap();

                install_file.write_all(b"\n# Install executable\n").unwrap();
                let exe_source_path = PathBuf::from("..")
                    .join("target")
                    .join("release")
                    .join(env!("CARGO_PKG_NAME"));
                install_file
                    .write_all(format!("mkdir -p {}\n", exe_dir.to_str().unwrap()).as_bytes())
                    .unwrap();
                install_file
                    .write_all(
                        format!(
                            "cp {} {}\n",
                            exe_source_path.to_str().unwrap(),
                            exe_dir.join(env!("CARGO_PKG_NAME")).to_str().unwrap(),
                        )
                        .as_bytes(),
                    )
                    .unwrap();

                install_file.write_all(b"\n# Install icons\n").unwrap();
                let icon_target_dir = data_dir.join("icons").join("hicolor");
                let entry_iter =
                    read_dir(PathBuf::from("res").join("icons").join("hicolor")).unwrap();
                for entry in entry_iter {
                    let entry = entry.unwrap();
                    let entry_path = entry.path();
                    if entry_path.is_dir() {
                        let target_dir = icon_target_dir.join(&entry.file_name());
                        install_file
                            .write_all(
                                format!("mkdir -p {}\n", target_dir.to_str().unwrap()).as_bytes(),
                            )
                            .unwrap();

                        install_file
                            .write_all(
                                format!(
                                    "cp -r {:?}/* {:?}\n",
                                    entry_path.to_str().unwrap(),
                                    target_dir,
                                )
                                .as_bytes(),
                            )
                            .unwrap();
                    }
                }

                install_file
                    .write_all(b"\n# Install translations\n")
                    .unwrap();
                install_file
                    .write_all(format!("mkdir -p {}\n", data_dir.to_str().unwrap()).as_bytes())
                    .unwrap();
                install_file
                    .write_all(
                        format!(
                            "cp -r {} {}\n",
                            PathBuf::from("target").join("locale").to_str().unwrap(),
                            data_dir.to_str().unwrap(),
                        )
                        .as_bytes(),
                    )
                    .unwrap();

                install_file
                    .write_all(b"\n# Install desktop file\n")
                    .unwrap();
                let desktop_target_dir = data_dir.join("applications");
                install_file
                    .write_all(
                        format!("mkdir -p {}\n", desktop_target_dir.to_str().unwrap()).as_bytes(),
                    )
                    .unwrap();
                install_file
                    .write_all(
                        format!(
                            "cp {} {}\n",
                            PathBuf::from("res")
                                .join(&format!("org.fengalin.{}.desktop", env!("CARGO_PKG_NAME")))
                                .to_str()
                                .unwrap(),
                            desktop_target_dir.to_str().unwrap(),
                        )
                        .as_bytes(),
                    )
                    .unwrap();
            }
            Err(err) => panic!("Couldn't create file `target/install`: {:?}", err),
        }
    }
}

// FIXME: figure out macOS conventions for icons & translations
#[cfg(target_family = "unix")]
fn generate_uninstall_script() {
    let base_dirs = BaseDirs::new().unwrap();
    // Note: `base_dirs.executable_dir()` is `None` on macOS
    if let Some(exe_dir) = base_dirs.executable_dir() {
        let project_dirs = ProjectDirs::from("org", "fengalin", env!("CARGO_PKG_NAME")).unwrap();
        let app_data_dir = project_dirs.data_dir();
        let data_dir = app_data_dir.parent().unwrap();

        match File::create(&PathBuf::from("target").join("uninstall")) {
            Ok(mut install_file) => {
                install_file
                    .write_all(
                        format!("# User uninstall script for {}\n", env!("CARGO_PKG_NAME"))
                            .as_bytes(),
                    )
                    .unwrap();

                install_file
                    .write_all(b"\n# Uninstall executable\n")
                    .unwrap();
                install_file
                    .write_all(
                        format!(
                            "rm {}\n",
                            exe_dir.join(env!("CARGO_PKG_NAME")).to_str().unwrap(),
                        )
                        .as_bytes(),
                    )
                    .unwrap();
                install_file
                    .write_all(format!("rmdir -p {}\n", exe_dir.to_str().unwrap()).as_bytes())
                    .unwrap();

                install_file.write_all(b"\n# Uninstall icons\n").unwrap();
                let icon_target_dir = data_dir.join("icons").join("hicolor");
                let entry_iter =
                    read_dir(PathBuf::from("res").join("icons").join("hicolor")).unwrap();
                for entry in entry_iter {
                    let entry = entry.unwrap();
                    let entry_path = entry.path();
                    if entry_path.is_dir() {
                        let icon_target_dir = icon_target_dir
                            .join(entry_path.file_name().unwrap())
                            .join("apps");
                        let icons_subdir_iter = read_dir(entry_path.join("apps")).unwrap();
                        for icon_subdir_entry in icons_subdir_iter {
                            let icon_subdir_entry = icon_subdir_entry.unwrap();
                            install_file
                                .write_all(
                                    format!(
                                        "rm {}\n",
                                        icon_target_dir
                                            .join(icon_subdir_entry.file_name())
                                            .to_str()
                                            .unwrap(),
                                    )
                                    .as_bytes(),
                                )
                                .unwrap();
                        }

                        install_file
                            .write_all(
                                format!("rmdir -p {}\n", icon_target_dir.to_str().unwrap())
                                    .as_bytes(),
                            )
                            .unwrap();
                    }
                }

                if let Ok(mut linguas_file) = File::open(&PathBuf::from("po").join("LINGUAS")) {
                    let mut linguas = String::new();
                    linguas_file
                        .read_to_string(&mut linguas)
                        .expect("Couldn't read po/LINGUAS as string");

                    install_file
                        .write_all(b"\n# Uninstall translations\n")
                        .unwrap();
                    let locale_base_dir = data_dir.join("locale");
                    for lingua in linguas.lines() {
                        let lingua_dir = locale_base_dir.join(lingua).join("LC_MESSAGES");
                        install_file
                            .write_all(
                                format!(
                                    "rm {}\n",
                                    lingua_dir
                                        .join(&format!("{}.mo", env!("CARGO_PKG_NAME")))
                                        .to_str()
                                        .unwrap(),
                                )
                                .as_bytes(),
                            )
                            .unwrap();

                        install_file
                            .write_all(
                                format!("rmdir -p {}\n", lingua_dir.to_str().unwrap()).as_bytes(),
                            )
                            .unwrap();
                    }
                }

                install_file
                    .write_all(b"\n# Uninstall desktop file\n")
                    .unwrap();
                let desktop_target_dir = data_dir.join("applications");
                install_file
                    .write_all(
                        format!(
                            "rm {}\n",
                            desktop_target_dir
                                .join(&format!("org.fengalin.{}.desktop", env!("CARGO_PKG_NAME")))
                                .to_str()
                                .unwrap(),
                        )
                        .as_bytes(),
                    )
                    .unwrap();
                install_file
                    .write_all(
                        format!("rmdir -p {}\n", desktop_target_dir.to_str().unwrap()).as_bytes(),
                    )
                    .unwrap();
            }
            Err(err) => panic!("Couldn't create file `target/uninstall`: {:?}", err),
        }
    }
}

fn main() {
    generate_resources();
    generate_translations();

    #[cfg(target_family = "unix")]
    generate_install_script();

    #[cfg(target_family = "unix")]
    generate_uninstall_script();
}
<commit_msg>Fix binary path for install script<commit_after>extern crate directories;

#[cfg(target_family = "unix")]
use directories::{BaseDirs, ProjectDirs};

use std::fs::{create_dir_all, File};
use std::io::{ErrorKind, Read};
use std::path::PathBuf;
use std::process::Command;

#[cfg(target_family = "unix")]
use std::fs::read_dir;
#[cfg(target_family = "unix")]
use std::io::Write;

fn generate_resources() {
    let target_path = PathBuf::from("target").join("resources");
    create_dir_all(&target_path).unwrap();

    // Icons
    let input_path = PathBuf::from("res").join("icons").join("hicolor");

    let mut compile_res = Command::new("glib-compile-resources");
    compile_res
        .arg("--generate")
        .arg(format!("--sourcedir={}", input_path.to_str().unwrap()))
        .arg(format!(
            "--target={}",
            target_path.join("icons.gresource").to_str().unwrap(),
        ))
        .arg(input_path.join("icons.gresource.xml").to_str().unwrap());

    match compile_res.status() {
        Ok(status) => {
            if !status.success() {
                panic!(format!(
                    "Failed to generate resources file for icons\n{:?}",
                    compile_res,
                ));
            }
        }
        Err(ref error) => match error.kind() {
            ErrorKind::NotFound => panic!(
                "Can't generate icon resources: command `glib-compile-resources` not available"
            ),
            _ => panic!("Error invoking `compile_res`: {}", error),
        },
    }

    // UI
    let input_path = PathBuf::from("res").join("ui");

    let mut compile_res = Command::new("glib-compile-resources");
    compile_res
        .arg("--generate")
        .arg(format!("--sourcedir={}", input_path.to_str().unwrap()))
        .arg(format!(
            "--target={}",
            target_path.join("ui.gresource").to_str().unwrap(),
        ))
        .arg(input_path.join("ui.gresource.xml").to_str().unwrap());

    match compile_res.status() {
        Ok(status) => {
            if !status.success() {
                panic!(format!(
                    "Failed to generate resources file for the UI\n{:?}",
                    compile_res,
                ));
            }
        }
        Err(ref error) => match error.kind() {
            ErrorKind::NotFound => panic!(
                "Can't generate UI resources: command `glib-compile-resources` not available"
            ),
            _ => panic!("Error invoking `compile_res`: {}", error),
        },
    }
}

fn generate_translations() {
    if let Ok(mut linguas_file) = File::open(&PathBuf::from("po").join("LINGUAS")) {
        let mut linguas = String::new();
        linguas_file
            .read_to_string(&mut linguas)
            .expect("Couldn't read po/LINGUAS as string");

        for lingua in linguas.lines() {
            let mo_path = PathBuf::from("target")
                .join("locale")
                .join(lingua)
                .join("LC_MESSAGES");
            create_dir_all(&mo_path).unwrap();

            let mut msgfmt = Command::new("msgfmt");
            msgfmt
                .arg(format!(
                    "--output-file={}",
                    mo_path.join("media-toc.mo").to_str().unwrap()
                ))
                .arg("--directory=po")
                .arg(format!("{}.po", lingua));

            match msgfmt.status() {
                Ok(status) => {
                    if !status.success() {
                        panic!(format!(
                            "Failed to generate mo file for lingua {}\n{:?}",
                            lingua, msgfmt,
                        ));
                    }
                }
                Err(ref error) => match error.kind() {
                    ErrorKind::NotFound => {
                        eprintln!("Can't generate translations: command `msgfmt` not available");
                        return;
                    }
                    _ => panic!("Error invoking `msgfmt`: {}", error),
                },
            }
        }
    }
}

// FIXME: figure out macOS conventions for icons & translations
#[cfg(target_family = "unix")]
fn generate_install_script() {
    let base_dirs = BaseDirs::new().unwrap();
    // Note: `base_dirs.executable_dir()` is `None` on macOS
    if let Some(exe_dir) = base_dirs.executable_dir() {
        let project_dirs = ProjectDirs::from("org", "fengalin", env!("CARGO_PKG_NAME")).unwrap();
        let app_data_dir = project_dirs.data_dir();
        let data_dir = app_data_dir.parent().unwrap();

        match File::create(&PathBuf::from("target").join("install")) {
            Ok(mut install_file) => {
                install_file
                    .write_all(
                        format!("# User install script for {}\n", env!("CARGO_PKG_NAME"))
                            .as_bytes(),
                    )
                    .unwrap();

                install_file.write_all(b"\n# Install executable\n").unwrap();
                let exe_source_path = PathBuf::from("target")
                    .join("release")
                    .join(env!("CARGO_PKG_NAME"));
                install_file
                    .write_all(format!("mkdir -p {}\n", exe_dir.to_str().unwrap()).as_bytes())
                    .unwrap();
                install_file
                    .write_all(
                        format!(
                            "cp {} {}\n",
                            exe_source_path.to_str().unwrap(),
                            exe_dir.join(env!("CARGO_PKG_NAME")).to_str().unwrap(),
                        )
                        .as_bytes(),
                    )
                    .unwrap();

                install_file.write_all(b"\n# Install icons\n").unwrap();
                let icon_target_dir = data_dir.join("icons").join("hicolor");
                let entry_iter =
                    read_dir(PathBuf::from("res").join("icons").join("hicolor")).unwrap();
                for entry in entry_iter {
                    let entry = entry.unwrap();
                    let entry_path = entry.path();
                    if entry_path.is_dir() {
                        let target_dir = icon_target_dir.join(&entry.file_name());
                        install_file
                            .write_all(
                                format!("mkdir -p {}\n", target_dir.to_str().unwrap()).as_bytes(),
                            )
                            .unwrap();

                        install_file
                            .write_all(
                                format!(
                                    "cp -r {:?}/* {:?}\n",
                                    entry_path.to_str().unwrap(),
                                    target_dir,
                                )
                                .as_bytes(),
                            )
                            .unwrap();
                    }
                }

                install_file
                    .write_all(b"\n# Install translations\n")
                    .unwrap();
                install_file
                    .write_all(format!("mkdir -p {}\n", data_dir.to_str().unwrap()).as_bytes())
                    .unwrap();
                install_file
                    .write_all(
                        format!(
                            "cp -r {} {}\n",
                            PathBuf::from("target").join("locale").to_str().unwrap(),
                            data_dir.to_str().unwrap(),
                        )
                        .as_bytes(),
                    )
                    .unwrap();

                install_file
                    .write_all(b"\n# Install desktop file\n")
                    .unwrap();
                let desktop_target_dir = data_dir.join("applications");
                install_file
                    .write_all(
                        format!("mkdir -p {}\n", desktop_target_dir.to_str().unwrap()).as_bytes(),
                    )
                    .unwrap();
                install_file
                    .write_all(
                        format!(
                            "cp {} {}\n",
                            PathBuf::from("res")
                                .join(&format!("org.fengalin.{}.desktop", env!("CARGO_PKG_NAME")))
                                .to_str()
                                .unwrap(),
                            desktop_target_dir.to_str().unwrap(),
                        )
                        .as_bytes(),
                    )
                    .unwrap();
            }
            Err(err) => panic!("Couldn't create file `target/install`: {:?}", err),
        }
    }
}

// FIXME: figure out macOS conventions for icons & translations
#[cfg(target_family = "unix")]
fn generate_uninstall_script() {
    let base_dirs = BaseDirs::new().unwrap();
    // Note: `base_dirs.executable_dir()` is `None` on macOS
    if let Some(exe_dir) = base_dirs.executable_dir() {
        let project_dirs = ProjectDirs::from("org", "fengalin", env!("CARGO_PKG_NAME")).unwrap();
        let app_data_dir = project_dirs.data_dir();
        let data_dir = app_data_dir.parent().unwrap();

        match File::create(&PathBuf::from("target").join("uninstall")) {
            Ok(mut install_file) => {
                install_file
                    .write_all(
                        format!("# User uninstall script for {}\n", env!("CARGO_PKG_NAME"))
                            .as_bytes(),
                    )
                    .unwrap();

                install_file
                    .write_all(b"\n# Uninstall executable\n")
                    .unwrap();
                install_file
                    .write_all(
                        format!(
                            "rm {}\n",
                            exe_dir.join(env!("CARGO_PKG_NAME")).to_str().unwrap(),
                        )
                        .as_bytes(),
                    )
                    .unwrap();
                install_file
                    .write_all(format!("rmdir -p {}\n", exe_dir.to_str().unwrap()).as_bytes())
                    .unwrap();

                install_file.write_all(b"\n# Uninstall icons\n").unwrap();
                let icon_target_dir = data_dir.join("icons").join("hicolor");
                let entry_iter =
                    read_dir(PathBuf::from("res").join("icons").join("hicolor")).unwrap();
                for entry in entry_iter {
                    let entry = entry.unwrap();
                    let entry_path = entry.path();
                    if entry_path.is_dir() {
                        let icon_target_dir = icon_target_dir
                            .join(entry_path.file_name().unwrap())
                            .join("apps");
                        let icons_subdir_iter = read_dir(entry_path.join("apps")).unwrap();
                        for icon_subdir_entry in icons_subdir_iter {
                            let icon_subdir_entry = icon_subdir_entry.unwrap();
                            install_file
                                .write_all(
                                    format!(
                                        "rm {}\n",
                                        icon_target_dir
                                            .join(icon_subdir_entry.file_name())
                                            .to_str()
                                            .unwrap(),
                                    )
                                    .as_bytes(),
                                )
                                .unwrap();
                        }

                        install_file
                            .write_all(
                                format!("rmdir -p {}\n", icon_target_dir.to_str().unwrap())
                                    .as_bytes(),
                            )
                            .unwrap();
                    }
                }

                if let Ok(mut linguas_file) = File::open(&PathBuf::from("po").join("LINGUAS")) {
                    let mut linguas = String::new();
                    linguas_file
                        .read_to_string(&mut linguas)
                        .expect("Couldn't read po/LINGUAS as string");

                    install_file
                        .write_all(b"\n# Uninstall translations\n")
                        .unwrap();
                    let locale_base_dir = data_dir.join("locale");
                    for lingua in linguas.lines() {
                        let lingua_dir = locale_base_dir.join(lingua).join("LC_MESSAGES");
                        install_file
                            .write_all(
                                format!(
                                    "rm {}\n",
                                    lingua_dir
                                        .join(&format!("{}.mo", env!("CARGO_PKG_NAME")))
                                        .to_str()
                                        .unwrap(),
                                )
                                .as_bytes(),
                            )
                            .unwrap();

                        install_file
                            .write_all(
                                format!("rmdir -p {}\n", lingua_dir.to_str().unwrap()).as_bytes(),
                            )
                            .unwrap();
                    }
                }

                install_file
                    .write_all(b"\n# Uninstall desktop file\n")
                    .unwrap();
                let desktop_target_dir = data_dir.join("applications");
                install_file
                    .write_all(
                        format!(
                            "rm {}\n",
                            desktop_target_dir
                                .join(&format!("org.fengalin.{}.desktop", env!("CARGO_PKG_NAME")))
                                .to_str()
                                .unwrap(),
                        )
                        .as_bytes(),
                    )
                    .unwrap();
                install_file
                    .write_all(
                        format!("rmdir -p {}\n", desktop_target_dir.to_str().unwrap()).as_bytes(),
                    )
                    .unwrap();
            }
            Err(err) => panic!("Couldn't create file `target/uninstall`: {:?}", err),
        }
    }
}

fn main() {
    generate_resources();
    generate_translations();

    #[cfg(target_family = "unix")]
    generate_install_script();

    #[cfg(target_family = "unix")]
    generate_uninstall_script();
}
<|endoftext|>


<commit_before>use std::io::Write;
use std::env;

fn main(){
    let target_os = env::var("CARGO_CFG_TARGET_OS");
    match target_os.as_ref().map(|x| &**x) {
        Ok("linux") | Ok("android") => println!("cargo:rustc-link-lib=dl"),
        Ok("freebsd") | Ok("dragonfly") => println!("cargo:rustc-link-lib=c"),
        // netbsd claims dl* will be available to any dynamically linked binary, but I haven’t
        // found any libraries that have to be linked to on other platforms.
        // What happens if the executable is not linked up dynamically?
        Ok("openbsd") | Ok("bitrig") | Ok("netbsd") | Ok("macos") | Ok("ios") => {}
        // dependencies come with winapi
        Ok("windows") => {}
        tos => {
            writeln!(::std::io::stderr(),
                     "Building for an unknown target_os=`{:?}`!\nPlease report an issue ",
                     tos).expect("could not report the error");
            ::std::process::exit(0xfc);
        }
    }
    maybe_test_helpers();
}

fn maybe_test_helpers() {
    if env::var("OPT_LEVEL").ok().and_then(|v| v.parse().ok()).unwrap_or(0u64) != 0 {
        // certainly not for testing, just skip.
        return;
    }
    let mut outpath = if let Some(od) = env::var_os("OUT_DIR") { od } else { return };
    let target = if let Some(t) = env::var_os("TARGET") { t } else { return };
    let rustc = env::var_os("RUSTC").unwrap_or_else(|| { "rustc".into() });
    outpath.push("/libtest_helpers.dll"); // extension for windows required, POSIX does not care.
    let _ = ::std::process::Command::new(rustc)
        .arg("src/test_helpers.rs")
        .arg("-o")
        .arg(outpath)
        .arg("-O")
        .arg("--target")
        .arg(target)
        .output();
    // Ignore the failures here. We do not want failures of this thing to inhibit people from
    // building and using the library. Might make it hard to debug why tests fail in case this
    // library does not get built, though.
}
<commit_msg>Allow to build on Solaris.<commit_after>use std::io::Write;
use std::env;

fn main(){
    let target_os = env::var("CARGO_CFG_TARGET_OS");
    match target_os.as_ref().map(|x| &**x) {
        Ok("linux") | Ok("android") => println!("cargo:rustc-link-lib=dl"),
        Ok("freebsd") | Ok("dragonfly") => println!("cargo:rustc-link-lib=c"),
        // netbsd claims dl* will be available to any dynamically linked binary, but I haven’t
        // found any libraries that have to be linked to on other platforms.
        // What happens if the executable is not linked up dynamically?
        Ok("openbsd") | Ok("bitrig") | Ok("netbsd") | Ok("macos") | Ok("ios") => {}
        Ok("solaris") => {}
        // dependencies come with winapi
        Ok("windows") => {}
        tos => {
            writeln!(::std::io::stderr(),
                     "Building for an unknown target_os=`{:?}`!\nPlease report an issue ",
                     tos).expect("could not report the error");
            ::std::process::exit(0xfc);
        }
    }
    maybe_test_helpers();
}

fn maybe_test_helpers() {
    if env::var("OPT_LEVEL").ok().and_then(|v| v.parse().ok()).unwrap_or(0u64) != 0 {
        // certainly not for testing, just skip.
        return;
    }
    let mut outpath = if let Some(od) = env::var_os("OUT_DIR") { od } else { return };
    let target = if let Some(t) = env::var_os("TARGET") { t } else { return };
    let rustc = env::var_os("RUSTC").unwrap_or_else(|| { "rustc".into() });
    outpath.push("/libtest_helpers.dll"); // extension for windows required, POSIX does not care.
    let _ = ::std::process::Command::new(rustc)
        .arg("src/test_helpers.rs")
        .arg("-o")
        .arg(outpath)
        .arg("-O")
        .arg("--target")
        .arg(target)
        .output();
    // Ignore the failures here. We do not want failures of this thing to inhibit people from
    // building and using the library. Might make it hard to debug why tests fail in case this
    // library does not get built, though.
}
<|endoftext|>


<commit_before>use crate::sdk::{
    export::metrics::{
        self, Accumulation, Aggregator, AggregatorSelector, CheckpointSet, Checkpointer,
        ExportKind, ExportKindFor, LockedProcessor, Processor, Record, Subtractor,
    },
    metrics::aggregators::SumAggregator,
    Resource,
};
use crate::{
    labels::{hash_labels, LabelSet},
    metrics::{Descriptor, MetricsError, Result},
};
use fnv::FnvHasher;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::sync::{Arc, Mutex, MutexGuard};
use std::time::SystemTime;

/// Create a new basic processor
pub fn basic(
    aggregator_selector: Box<dyn AggregatorSelector + Send + Sync>,
    export_selector: Box<dyn ExportKindFor + Send + Sync>,
    memory: bool,
) -> BasicProcessor {
    BasicProcessor {
        aggregator_selector,
        export_selector,
        state: Mutex::new(BasicProcessorState::with_memory(memory)),
    }
}

/// Basic metric integration strategy
#[derive(Debug)]
pub struct BasicProcessor {
    aggregator_selector: Box<dyn AggregatorSelector + Send + Sync>,
    export_selector: Box<dyn ExportKindFor + Send + Sync>,
    state: Mutex<BasicProcessorState>,
}

impl BasicProcessor {
    /// Lock this processor to return a mutable locked processor
    pub fn lock(&self) -> Result<BasicLockedProcessor<'_>> {
        self.state
            .lock()
            .map_err(From::from)
            .map(|locked| BasicLockedProcessor {
                parent: self,
                state: locked,
            })
    }
}

impl Processor for BasicProcessor {
    fn aggregation_selector(&self) -> &dyn AggregatorSelector {
        self.aggregator_selector.as_ref()
    }
}

/// A locked representation of the processor used where mutable references are necessary.
#[derive(Debug)]
pub struct BasicLockedProcessor<'a> {
    parent: &'a BasicProcessor,
    state: MutexGuard<'a, BasicProcessorState>,
}

impl<'a> LockedProcessor for BasicLockedProcessor<'a> {
    fn process(&mut self, accumulation: Accumulation<'_>) -> Result<()> {
        if self.state.started_collection != self.state.finished_collection.wrapping_add(1) {
            return Err(MetricsError::InconsistentState);
        }

        let desc = accumulation.descriptor();
        let mut hasher = FnvHasher::default();
        desc.attribute_hash().hash(&mut hasher);
        hash_labels(&mut hasher, accumulation.labels().into_iter());
        hash_labels(&mut hasher, accumulation.resource().into_iter());
        let key = StateKey(hasher.finish());
        let agg = accumulation.aggregator();
        let finished_collection = self.state.finished_collection;
        if let Some(value) = self.state.values.get_mut(&key) {
            // Advance the update sequence number.
            let same_collection = finished_collection == value.updated;
            value.updated = finished_collection;

            // At this point in the code, we have located an existing
            // value for some stateKey.  This can be because:
            //
            // (a) stateful aggregation is being used, the entry was
            // entered during a prior collection, and this is the first
            // time processing an accumulation for this stateKey in the
            // current collection.  Since this is the first time
            // processing an accumulation for this stateKey during this
            // collection, we don't know yet whether there are multiple
            // accumulators at work.  If there are multiple accumulators,
            // they'll hit case (b) the second time through.
            //
            // (b) multiple accumulators are being used, whether stateful
            // or not.
            //
            // Case (a) occurs when the instrument and the exporter
            // require memory to work correctly, either because the
            // instrument reports a PrecomputedSum to a DeltaExporter or
            // the reverse, a non-PrecomputedSum instrument with a
            // CumulativeExporter.  This logic is encapsulated in
            // ExportKind.MemoryRequired(MetricKind).
            //
            // Case (b) occurs when the variable `sameCollection` is true,
            // indicating that the stateKey for Accumulation has already
            // been seen in the same collection.  When this happens, it
            // implies that multiple Accumulators are being used, or that
            // a single Accumulator has been configured with a label key
            // filter.

            if !same_collection {
                if !value.current_owned {
                    // This is the first Accumulation we've seen for this
                    // stateKey during this collection.  Just keep a
                    // reference to the Accumulator's Aggregator. All the other cases
                    // copy Aggregator state.
                    value.current = agg.clone();
                    return Ok(());
                }
                return agg.synchronized_move(&value.current, desc);
            }

            // If the current is not owned, take ownership of a copy
            // before merging below.
            if !value.current_owned {
                let tmp = value.current.clone();
                if let Some(current) = self.parent.aggregation_selector().aggregator_for(desc) {
                    value.current = current;
                    value.current_owned = true;
                    tmp.synchronized_move(&value.current, &desc)?;
                }
            }

            // Combine this `Accumulation` with the prior `Accumulation`.
            return value.current.merge(agg.as_ref(), desc);
        }

        let stateful = self
            .parent
            .export_selector
            .export_kind_for(&desc)
            .memory_required(desc.instrument_kind());

        let mut delta = None;
        let cumulative = if stateful {
            if desc.instrument_kind().precomputed_sum() {
                // If we know we need to compute deltas, allocate one.
                delta = self.parent.aggregation_selector().aggregator_for(desc);
            }
            // Always allocate a cumulative aggregator if stateful
            self.parent.aggregation_selector().aggregator_for(desc)
        } else {
            None
        };

        self.state.values.insert(
            key,
            StateValue {
                descriptor: desc.clone(),
                labels: accumulation.labels().clone(),
                resource: accumulation.resource().clone(),
                current_owned: false,
                current: agg.clone(),
                delta,
                cumulative,
                stateful,
                updated: finished_collection,
            },
        );

        Ok(())
    }
}

impl Checkpointer for BasicLockedProcessor<'_> {
    fn checkpoint_set(&mut self) -> &mut dyn CheckpointSet {
        &mut *self.state
    }

    fn start_collection(&mut self) {
        if self.state.started_collection != 0 {
            self.state.interval_start = self.state.interval_end;
        }
        self.state.started_collection = self.state.started_collection.wrapping_add(1);
    }

    fn finish_collection(&mut self) -> Result<()> {
        self.state.interval_end = crate::time::now();
        if self.state.started_collection != self.state.finished_collection.wrapping_add(1) {
            return Err(MetricsError::InconsistentState);
        }
        let finished_collection = self.state.finished_collection;
        self.state.finished_collection = self.state.finished_collection.wrapping_add(1);
        let has_memory = self.state.config.memory;

        let mut result = Ok(());

        self.state.values.retain(|_key, value| {
            // Return early if previous error
            if result.is_err() {
                return true;
            }

            let mkind = value.descriptor.instrument_kind();

            let stale = value.updated != finished_collection;
            let stateless = !value.stateful;

            // The following branch updates stateful aggregators. Skip these updates
            // if the aggregator is not stateful or if the aggregator is stale.
            if stale || stateless {
                // If this processor does not require memory, stale, stateless
                // entries can be removed. This implies that they were not updated
                // over the previous full collection interval.
                if stale && stateless && has_memory {
                    return false;
                }
            }

            // Update Aggregator state to support exporting either a
            // delta or a cumulative aggregation.
            if mkind.precomputed_sum() {
                if let Some(current_subtractor) =
                    value.current.as_any().downcast_ref::<SumAggregator>()
                {
                    // This line is equivalent to:
                    // value.delta = currentSubtractor - value.cumulative
                    if let (Some(cumulative), Some(delta)) =
                        (value.cumulative.as_ref(), value.delta.as_ref())
                    {
                        result = current_subtractor
                            .subtract(cumulative.as_ref(), delta.as_ref(), &value.descriptor)
                            .and_then(|_| {
                                value
                                    .current
                                    .synchronized_move(cumulative, &value.descriptor)
                            });
                    }
                } else {
                    result = Err(MetricsError::NoSubtraction);
                }
            } else {
                // This line is equivalent to:
                // value.cumulative = value.cumulative + value.delta
                if let Some(cumulative) = value.cumulative.as_ref() {
                    result = cumulative.merge(value.current.as_ref(), &value.descriptor)
                }
            }

            true
        });

        result
    }
}

#[derive(Debug, Default)]
struct BasicProcessorConfig {
    /// Memory controls whether the processor remembers metric instruments and label
    /// sets that were previously reported. When Memory is true,
    /// `CheckpointSet::try_for_each` will visit metrics that were not updated in
    /// the most recent interval.
    memory: bool,
}

#[derive(Debug)]
struct BasicProcessorState {
    config: BasicProcessorConfig,
    values: HashMap<StateKey, StateValue>,
    // Note: the timestamp logic currently assumes all exports are deltas.
    process_start: SystemTime,
    interval_start: SystemTime,
    interval_end: SystemTime,
    started_collection: u64,
    finished_collection: u64,
}

impl BasicProcessorState {
    fn with_memory(memory: bool) -> Self {
        let mut state = BasicProcessorState::default();
        state.config.memory = memory;
        state
    }
}

impl Default for BasicProcessorState {
    fn default() -> Self {
        BasicProcessorState {
            config: BasicProcessorConfig::default(),
            values: HashMap::default(),
            process_start: crate::time::now(),
            interval_start: crate::time::now(),
            interval_end: crate::time::now(),
            started_collection: 0,
            finished_collection: 0,
        }
    }
}

impl CheckpointSet for BasicProcessorState {
    fn try_for_each(
        &mut self,
        exporter: &dyn ExportKindFor,
        f: &mut dyn FnMut(&Record<'_>) -> Result<()>,
    ) -> Result<()> {
        if self.started_collection != self.finished_collection {
            return Err(MetricsError::InconsistentState);
        }

        self.values.iter().try_for_each(|(_key, value)| {
            let instrument_kind = value.descriptor.instrument_kind();

            let agg;
            let start;

            // If the processor does not have memory and it was not updated in the
            // prior round, do not visit this value.
            if !self.config.memory && value.updated != self.finished_collection.wrapping_sub(1) {
                return Ok(());
            }

            match exporter.export_kind_for(&value.descriptor) {
                ExportKind::Cumulative => {
                    // If stateful, the sum has been computed.  If stateless, the
                    // input was already cumulative. Either way, use the
                    // checkpointed value:
                    if value.stateful {
                        agg = value.cumulative.as_ref();
                    } else {
                        agg = Some(&value.current);
                    }

                    start = self.process_start;
                }

                ExportKind::Delta => {
                    // Precomputed sums are a special case.
                    if instrument_kind.precomputed_sum() {
                        agg = value.delta.as_ref();
                    } else {
                        agg = Some(&value.current);
                    }

                    start = self.interval_start;
                }
            }

            let res = f(&metrics::record(
                &value.descriptor,
                &value.labels,
                &value.resource,
                agg,
                start,
                self.interval_end,
            ));
            if let Err(MetricsError::NoDataCollected) = res {
                Ok(())
            } else {
                res
            }
        })
    }
}

#[derive(Debug, PartialEq, Eq, Hash)]
struct StateKey(u64);

#[derive(Debug)]
struct StateValue {
    /// Instrument descriptor
    descriptor: Descriptor,

    /// Instrument labels
    labels: LabelSet,

    /// Resource that created the instrument
    resource: Resource,

    /// Indicates the last sequence number when this value had process called by an
    /// accumulator.
    updated: u64,

    /// Indicates that a cumulative aggregation is being maintained, taken from the
    /// process start time.
    stateful: bool,

    /// Indicates that "current" was allocated
    /// by the processor in order to merge results from
    /// multiple `Accumulator`s during a single collection
    /// round, which may happen either because:
    ///
    /// (1) multiple `Accumulator`s output the same `Accumulation.
    /// (2) one `Accumulator` is configured with dimensionality reduction.
    current_owned: bool,

    /// The output from a single `Accumulator` (if !current_owned) or an
    /// `Aggregator` owned by the processor used to accumulate multiple values in a
    /// single collection round.
    current: Arc<dyn Aggregator + Send + Sync>,

    /// If `Some`, refers to an `Aggregator` owned by the processor used to compute
    /// deltas between precomputed sums.
    delta: Option<Arc<dyn Aggregator + Send + Sync>>,

    /// If `Some`, refers to an `Aggregator` owned by the processor used to store
    /// the last cumulative value.
    cumulative: Option<Arc<dyn Aggregator + Send + Sync>>,
}
<commit_msg>fix: Counter adds the delta from last collection. (#395)<commit_after>use crate::sdk::{
    export::metrics::{
        self, Accumulation, Aggregator, AggregatorSelector, CheckpointSet, Checkpointer,
        ExportKind, ExportKindFor, LockedProcessor, Processor, Record, Subtractor,
    },
    metrics::aggregators::SumAggregator,
    Resource,
};
use crate::{
    labels::{hash_labels, LabelSet},
    metrics::{Descriptor, MetricsError, Result},
};
use fnv::FnvHasher;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::sync::{Arc, Mutex, MutexGuard};
use std::time::SystemTime;

/// Create a new basic processor
pub fn basic(
    aggregator_selector: Box<dyn AggregatorSelector + Send + Sync>,
    export_selector: Box<dyn ExportKindFor + Send + Sync>,
    memory: bool,
) -> BasicProcessor {
    BasicProcessor {
        aggregator_selector,
        export_selector,
        state: Mutex::new(BasicProcessorState::with_memory(memory)),
    }
}

/// Basic metric integration strategy
#[derive(Debug)]
pub struct BasicProcessor {
    aggregator_selector: Box<dyn AggregatorSelector + Send + Sync>,
    export_selector: Box<dyn ExportKindFor + Send + Sync>,
    state: Mutex<BasicProcessorState>,
}

impl BasicProcessor {
    /// Lock this processor to return a mutable locked processor
    pub fn lock(&self) -> Result<BasicLockedProcessor<'_>> {
        self.state
            .lock()
            .map_err(From::from)
            .map(|locked| BasicLockedProcessor {
                parent: self,
                state: locked,
            })
    }
}

impl Processor for BasicProcessor {
    fn aggregation_selector(&self) -> &dyn AggregatorSelector {
        self.aggregator_selector.as_ref()
    }
}

/// A locked representation of the processor used where mutable references are necessary.
#[derive(Debug)]
pub struct BasicLockedProcessor<'a> {
    parent: &'a BasicProcessor,
    state: MutexGuard<'a, BasicProcessorState>,
}

impl<'a> LockedProcessor for BasicLockedProcessor<'a> {
    fn process(&mut self, accumulation: Accumulation<'_>) -> Result<()> {
        if self.state.started_collection != self.state.finished_collection.wrapping_add(1) {
            return Err(MetricsError::InconsistentState);
        }

        let desc = accumulation.descriptor();
        let mut hasher = FnvHasher::default();
        desc.attribute_hash().hash(&mut hasher);
        hash_labels(&mut hasher, accumulation.labels().into_iter());
        hash_labels(&mut hasher, accumulation.resource().into_iter());
        let key = StateKey(hasher.finish());
        let agg = accumulation.aggregator();
        let finished_collection = self.state.finished_collection;
        if let Some(value) = self.state.values.get_mut(&key) {
            // Advance the update sequence number.
            let same_collection = finished_collection == value.updated;
            value.updated = finished_collection;

            // At this point in the code, we have located an existing
            // value for some stateKey.  This can be because:
            //
            // (a) stateful aggregation is being used, the entry was
            // entered during a prior collection, and this is the first
            // time processing an accumulation for this stateKey in the
            // current collection.  Since this is the first time
            // processing an accumulation for this stateKey during this
            // collection, we don't know yet whether there are multiple
            // accumulators at work.  If there are multiple accumulators,
            // they'll hit case (b) the second time through.
            //
            // (b) multiple accumulators are being used, whether stateful
            // or not.
            //
            // Case (a) occurs when the instrument and the exporter
            // require memory to work correctly, either because the
            // instrument reports a PrecomputedSum to a DeltaExporter or
            // the reverse, a non-PrecomputedSum instrument with a
            // CumulativeExporter.  This logic is encapsulated in
            // ExportKind.MemoryRequired(MetricKind).
            //
            // Case (b) occurs when the variable `sameCollection` is true,
            // indicating that the stateKey for Accumulation has already
            // been seen in the same collection.  When this happens, it
            // implies that multiple Accumulators are being used, or that
            // a single Accumulator has been configured with a label key
            // filter.

            if !same_collection {
                if !value.current_owned {
                    // This is the first Accumulation we've seen for this
                    // stateKey during this collection.  Just keep a
                    // reference to the Accumulator's Aggregator. All the other cases
                    // copy Aggregator state.
                    value.current = agg.clone();
                    return Ok(());
                }
                return agg.synchronized_move(&value.current, desc);
            }

            // If the current is not owned, take ownership of a copy
            // before merging below.
            if !value.current_owned {
                let tmp = value.current.clone();
                if let Some(current) = self.parent.aggregation_selector().aggregator_for(desc) {
                    value.current = current;
                    value.current_owned = true;
                    tmp.synchronized_move(&value.current, &desc)?;
                }
            }

            // Combine this `Accumulation` with the prior `Accumulation`.
            return value.current.merge(agg.as_ref(), desc);
        }

        let stateful = self
            .parent
            .export_selector
            .export_kind_for(&desc)
            .memory_required(desc.instrument_kind());

        let mut delta = None;
        let cumulative = if stateful {
            if desc.instrument_kind().precomputed_sum() {
                // If we know we need to compute deltas, allocate one.
                delta = self.parent.aggregation_selector().aggregator_for(desc);
            }
            // Always allocate a cumulative aggregator if stateful
            self.parent.aggregation_selector().aggregator_for(desc)
        } else {
            None
        };

        self.state.values.insert(
            key,
            StateValue {
                descriptor: desc.clone(),
                labels: accumulation.labels().clone(),
                resource: accumulation.resource().clone(),
                current_owned: false,
                current: agg.clone(),
                delta,
                cumulative,
                stateful,
                updated: finished_collection,
            },
        );

        Ok(())
    }
}

impl Checkpointer for BasicLockedProcessor<'_> {
    fn checkpoint_set(&mut self) -> &mut dyn CheckpointSet {
        &mut *self.state
    }

    fn start_collection(&mut self) {
        if self.state.started_collection != 0 {
            self.state.interval_start = self.state.interval_end;
        }
        self.state.started_collection = self.state.started_collection.wrapping_add(1);
    }

    fn finish_collection(&mut self) -> Result<()> {
        self.state.interval_end = crate::time::now();
        if self.state.started_collection != self.state.finished_collection.wrapping_add(1) {
            return Err(MetricsError::InconsistentState);
        }
        let finished_collection = self.state.finished_collection;
        self.state.finished_collection = self.state.finished_collection.wrapping_add(1);
        let has_memory = self.state.config.memory;

        let mut result = Ok(());

        self.state.values.retain(|_key, value| {
            // Return early if previous error
            if result.is_err() {
                return true;
            }

            let mkind = value.descriptor.instrument_kind();

            let stale = value.updated != finished_collection;
            let stateless = !value.stateful;

            // The following branch updates stateful aggregators. Skip these updates
            // if the aggregator is not stateful or if the aggregator is stale.
            if stale || stateless {
                // If this processor does not require memory, stale, stateless
                // entries can be removed. This implies that they were not updated
                // over the previous full collection interval.
                if stale && stateless && !has_memory {
                    return false;
                }
                return true;
            }

            // Update Aggregator state to support exporting either a
            // delta or a cumulative aggregation.
            if mkind.precomputed_sum() {
                if let Some(current_subtractor) =
                    value.current.as_any().downcast_ref::<SumAggregator>()
                {
                    // This line is equivalent to:
                    // value.delta = currentSubtractor - value.cumulative
                    if let (Some(cumulative), Some(delta)) =
                        (value.cumulative.as_ref(), value.delta.as_ref())
                    {
                        result = current_subtractor
                            .subtract(cumulative.as_ref(), delta.as_ref(), &value.descriptor)
                            .and_then(|_| {
                                value
                                    .current
                                    .synchronized_move(cumulative, &value.descriptor)
                            });
                    }
                } else {
                    result = Err(MetricsError::NoSubtraction);
                }
            } else {
                // This line is equivalent to:
                // value.cumulative = value.cumulative + value.delta
                if let Some(cumulative) = value.cumulative.as_ref() {
                    result = cumulative.merge(value.current.as_ref(), &value.descriptor)
                }
            }

            true
        });

        result
    }
}

#[derive(Debug, Default)]
struct BasicProcessorConfig {
    /// Memory controls whether the processor remembers metric instruments and label
    /// sets that were previously reported. When Memory is true,
    /// `CheckpointSet::try_for_each` will visit metrics that were not updated in
    /// the most recent interval.
    memory: bool,
}

#[derive(Debug)]
struct BasicProcessorState {
    config: BasicProcessorConfig,
    values: HashMap<StateKey, StateValue>,
    // Note: the timestamp logic currently assumes all exports are deltas.
    process_start: SystemTime,
    interval_start: SystemTime,
    interval_end: SystemTime,
    started_collection: u64,
    finished_collection: u64,
}

impl BasicProcessorState {
    fn with_memory(memory: bool) -> Self {
        let mut state = BasicProcessorState::default();
        state.config.memory = memory;
        state
    }
}

impl Default for BasicProcessorState {
    fn default() -> Self {
        BasicProcessorState {
            config: BasicProcessorConfig::default(),
            values: HashMap::default(),
            process_start: crate::time::now(),
            interval_start: crate::time::now(),
            interval_end: crate::time::now(),
            started_collection: 0,
            finished_collection: 0,
        }
    }
}

impl CheckpointSet for BasicProcessorState {
    fn try_for_each(
        &mut self,
        exporter: &dyn ExportKindFor,
        f: &mut dyn FnMut(&Record<'_>) -> Result<()>,
    ) -> Result<()> {
        if self.started_collection != self.finished_collection {
            return Err(MetricsError::InconsistentState);
        }

        self.values.iter().try_for_each(|(_key, value)| {
            let instrument_kind = value.descriptor.instrument_kind();

            let agg;
            let start;

            // If the processor does not have memory and it was not updated in the
            // prior round, do not visit this value.
            if !self.config.memory && value.updated != self.finished_collection.wrapping_sub(1) {
                return Ok(());
            }

            match exporter.export_kind_for(&value.descriptor) {
                ExportKind::Cumulative => {
                    // If stateful, the sum has been computed.  If stateless, the
                    // input was already cumulative. Either way, use the
                    // checkpointed value:
                    if value.stateful {
                        agg = value.cumulative.as_ref();
                    } else {
                        agg = Some(&value.current);
                    }

                    start = self.process_start;
                }

                ExportKind::Delta => {
                    // Precomputed sums are a special case.
                    if instrument_kind.precomputed_sum() {
                        agg = value.delta.as_ref();
                    } else {
                        agg = Some(&value.current);
                    }

                    start = self.interval_start;
                }
            }

            let res = f(&metrics::record(
                &value.descriptor,
                &value.labels,
                &value.resource,
                agg,
                start,
                self.interval_end,
            ));
            if let Err(MetricsError::NoDataCollected) = res {
                Ok(())
            } else {
                res
            }
        })
    }
}

#[derive(Debug, PartialEq, Eq, Hash)]
struct StateKey(u64);

#[derive(Debug)]
struct StateValue {
    /// Instrument descriptor
    descriptor: Descriptor,

    /// Instrument labels
    labels: LabelSet,

    /// Resource that created the instrument
    resource: Resource,

    /// Indicates the last sequence number when this value had process called by an
    /// accumulator.
    updated: u64,

    /// Indicates that a cumulative aggregation is being maintained, taken from the
    /// process start time.
    stateful: bool,

    /// Indicates that "current" was allocated
    /// by the processor in order to merge results from
    /// multiple `Accumulator`s during a single collection
    /// round, which may happen either because:
    ///
    /// (1) multiple `Accumulator`s output the same `Accumulation.
    /// (2) one `Accumulator` is configured with dimensionality reduction.
    current_owned: bool,

    /// The output from a single `Accumulator` (if !current_owned) or an
    /// `Aggregator` owned by the processor used to accumulate multiple values in a
    /// single collection round.
    current: Arc<dyn Aggregator + Send + Sync>,

    /// If `Some`, refers to an `Aggregator` owned by the processor used to compute
    /// deltas between precomputed sums.
    delta: Option<Arc<dyn Aggregator + Send + Sync>>,

    /// If `Some`, refers to an `Aggregator` owned by the processor used to store
    /// the last cumulative value.
    cumulative: Option<Arc<dyn Aggregator + Send + Sync>>,
}
<|endoftext|>