1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! output rust version info file
//!
//! This crate is the presents, the file output of rustc --version and cargo tree command.
//!
//! # Examples
//!
//! Please write the following code in the build.rs:
//!
//! ```rust
//! use rust_version_info_file::rust_version_info_file;
//! 
//! fn main() {
//!     rust_version_info_file("target/rust-version-info.txt", "Cargo.toml");
//! }
//! ```
//!
//! And you get the file as result it.
//!
//! ```text
//! cat target/rust-version-info-file.txt
//! ```
//!
//! # On debian package
//! 
//! In Cargo.toml
//! 
//! ```text
//! [package.metadata.deb]
//! assets = [
//!     ["target/rust-version-info.txt", "usr/share/doc/your_package/", "644"],
//!     ["README.md", "usr/share/doc/your_package/", "644"],
//! ]
//! ```
//! 
use std::io::{Read, Write};
use std::path::Path;
use std::fs::OpenOptions;

pub fn rust_version_info_file<T: AsRef<Path>>(dst: T, cargo_toml_file: &str) {
    let dst_path: &Path = dst.as_ref();
    let old_s = read_file(dst_path);
    let curr_s = format!("{}\n{}\n", rustc_version_info(), tree_version_info(cargo_toml_file),);
    if old_s != curr_s {
        let mut fo = match OpenOptions::new()
            .create(true)
            .write(true)
            .truncate(true)
            .open(dst_path) {
            Ok(fo) => fo,
            Err(_) => return,
        };
        let _ = fo.write_fmt(format_args!("{}", curr_s));
        let _ = fo.flush();
    }
}

fn read_file(path: &Path) -> String {
    match OpenOptions::new()
        .create(false)
        .read(true)
        .open(path)
    {
        Ok(mut fi) => {
            let mut s = String::new();
            let _ = fi.read_to_string(&mut s);
            s
        }
        Err(_) => String::new(),
    }
}

fn rustc_version_info() -> String {
    let cmd = std::env::var_os("RUSTC").unwrap_or_else(|| std::ffi::OsString::from("rustc"));
    let out = std::process::Command::new(cmd)
        .arg("--version")
        .output()
        .unwrap();
    let v = out.stdout;
    let v_len = v.len();
    let v = if v_len > 0 && v[v_len - 1] == b'\n' {
        &v[0..v_len - 1]
    } else {
        &v[0..v_len]
    };
    String::from_utf8(v.to_vec()).unwrap()
}

fn tree_version_info(cargo_toml_file: &str) -> String {
    let cmd = "cargo";
    let out = std::process::Command::new(cmd)
        .arg("tree")
        .arg("--manifest-path")
        .arg(cargo_toml_file)
        .output()
        .unwrap();
    let v = out.stdout;
    let v_len = v.len();
    let v = if v_len > 0 && v[v_len - 1] == b'\n' {
        &v[0..v_len - 1]
    } else {
        &v[0..v_len]
    };
    //
    let string = String::from_utf8(v.to_vec()).unwrap();
    //
    let string = match string.find(" (") {
        Some(pos1) => {
            let (s1, s2) = string.split_at(pos1);
            match s2.find(')') {
                Some(pos2) => {
                    let (_s2, s3) = s2.split_at(pos2+1);
                    s1.to_owned() + s3
                }
                None => string,
            }
        },
        None => string,
    };
    //
    string
}