summaryrefslogtreecommitdiff
path: root/src/rust.rs
blob: 0ffe8c0f55b10e5ce91aa6c8dd0cb285ea546103 (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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use crate::errors::BumperError;
use cargo_edit::Manifest;
use log::{debug, info};
use std::path::{Path, PathBuf};
use std::process::Command;
use toml_edit::{Item, Value};

pub struct Rust {
    dirname: PathBuf,
    cargo_toml: CargoToml,
}

impl Rust {
    pub fn new(dirname: &Path) -> Result<Self, BumperError> {
        let cargo_toml = dirname.join("Cargo.toml");
        debug!(
            "does {} exist? {}",
            cargo_toml.display(),
            cargo_toml.exists()
        );
        if cargo_toml.exists() {
            return Ok(Self {
                dirname: dirname.to_path_buf(),
                cargo_toml: CargoToml::parse(&cargo_toml)?,
            });
        }
        Err(BumperError::UnknownProjectKind(dirname.to_path_buf()))
    }

    pub fn set_version(&mut self, version: &str) -> Result<String, BumperError> {
        debug!("parsing Cargo.toml from {}", self.dirname.display());
        // debug!("Cargo.toml:\n{:#?}", self.cargo_toml);
        self.cargo_toml.set_version(version)?;
        self.cargo_toml.write()?;
        self.update_cargo_lock()?;

        let dirname = self
            .dirname
            .canonicalize()
            .map_err(|err| BumperError::AbsPath(self.dirname.to_path_buf(), err))?;

        info!(
            "Rust project {}, in {}, version set to {}",
            self.cargo_toml.name()?,
            dirname.display(),
            version
        );
        Ok(version.to_string())
    }

    fn update_cargo_lock(&self) -> Result<(), BumperError> {
        info!("running cargo update in {}", self.dirname.display());
        let output = Command::new("cargo")
            .arg("update")
            .current_dir(&self.dirname)
            .output()
            .map_err(|err| BumperError::CargoInvoke(self.dirname.to_path_buf(), err))?;
        debug!("git exit code was {:?}", output.status.code());
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
            return Err(BumperError::Cargo(self.dirname.to_path_buf(), stderr));
        }
        Ok(())
    }
}

#[derive(Debug)]
struct CargoToml {
    dirname: PathBuf,
    manifest: Manifest,
}

impl CargoToml {
    fn parse(dirname: &Path) -> Result<Self, BumperError> {
        let manifest = Manifest::open(&Some(dirname.to_path_buf()))
            .map_err(|err| BumperError::FromToml(dirname.to_path_buf(), err))?;
        Ok(Self {
            dirname: dirname.to_path_buf(),
            manifest,
        })
    }

    fn get_package_table(&mut self) -> Result<&mut toml_edit::Table, BumperError> {
        let package = self
            .manifest
            .get_table(&["package".to_string()])
            .map_err(BumperError::NoPackage)?;
        if let Some(table) = package.as_table_mut() {
            Ok(table)
        } else {
            Err(BumperError::ProjectNotTable)
        }
    }

    fn name(&mut self) -> Result<String, BumperError> {
        let package = self.get_package_table()?;
        let v = package.entry("name");
        if let Some(name) = v.as_str() {
            Ok(name.to_string())
        } else {
            Err(BumperError::UnnamedProject)
        }
    }

    fn set_version(&mut self, version: &str) -> Result<(), BumperError> {
        let package = self.get_package_table()?;
        debug!("setting Cargo.toml set version to {} in memory", version);
        let v = package.entry("version");
        *v = Item::Value(Value::from(version));
        Ok(())
    }

    fn write(&self) -> Result<(), BumperError> {
        debug!("saving Cargo.toml to {}", self.dirname.display());
        let mut file = Manifest::find_file(&Some(self.dirname.to_path_buf()))
            .map_err(|err| BumperError::CargoTomlNotFound(self.dirname.to_path_buf(), err))?;
        self.manifest
            .write_to_file(&mut file)
            .map_err(|err| BumperError::WriteToml(self.dirname.to_path_buf(), err))?;
        Ok(())
    }
}