summaryrefslogtreecommitdiffstats
path: root/src/modules/package.rs
blob: ab201e4bcda323924da90230143dceb4f65e33fe (plain)
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
use super::Segment;
use crate::context::Context;
use ansi_term::Color;
use serde_json;
use std::fs::File;
use std::io;
use std::io::Read;
use std::path::PathBuf;
use toml;

/// Creates a segment with the current package version
///
/// Will display if a version is defined for your Node.js or Rust project (if one exists)
pub fn segment(context: &Context) -> Option<Segment> {
    match get_package_version(context) {
        Some(package_version) => {
            const PACKAGE_CHAR: &str = "📦";
            const SEGMENT_COLOR: Color = Color::Red;

            // TODO: Make the prefix for the module "is "
            let mut segment = Segment::new("package");
            segment.set_style(SEGMENT_COLOR.bold());

            segment.set_value(format!("{} {}", PACKAGE_CHAR, package_version));

            Some(segment)
        }
        None => None,
    }
}

// TODO: Combine into one function and just call for different file names!
fn is_cargo_toml(dir_entry: &PathBuf) -> bool {
    dir_entry.is_file() && dir_entry.file_name().unwrap_or_default() == "Cargo.toml"
}

fn is_package_json(dir_entry: &PathBuf) -> bool {
    dir_entry.is_file() && dir_entry.file_name().unwrap_or_default() == "package.json"
}

// TODO: Move to `utils.rs` file and import
fn read_file(file_name: &str) -> io::Result<String> {
    let mut file = File::open(file_name)?;
    let mut data = String::new();

    file.read_to_string(&mut data)?;

    Ok(data)
}

fn extract_cargo_version(file_contents: String) -> Option<String> {
    let cargo_toml = file_contents.parse::<toml::Value>().ok()?;

    match cargo_toml["package"]["version"].as_str() {
        Some(raw_version) => {
            let version = format_version(raw_version.to_string());
            Some(version)
        }
        None => None,
    }
}

fn extract_package_version(file_contents: String) -> Option<String> {
    let json: Option<serde_json::Value> = serde_json::from_str(&file_contents).ok()?;

    match json {
        Some(json) => {
            let raw_version = json["version"].to_string();
            if raw_version == "null" {
                None
            } else {
                Some(format_version(raw_version))
            }
        }
        None => None,
    }
}

fn get_package_version(context: &Context) -> Option<String> {
    let has_cargo_toml = context.dir_files.iter().any(is_cargo_toml);
    if has_cargo_toml {
        let file_contents = read_file("Cargo.toml").ok()?;
        return extract_cargo_version(file_contents);
    }

    let has_package_json = context.dir_files.iter().any(is_package_json);
    if has_package_json {
        let file_contents = read_file("package.json").ok()?;
        return extract_package_version(file_contents);
    }

    None
}

fn format_version(version: String) -> String {
    format!("v{}", version.replace('"', "").trim())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_format_version() {
        let input = String::from("0.1.0");
        assert_eq!(format_version(input), "v0.1.0");
    }
}