cargo_mobile2/
dot_cargo.rs

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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use crate::{
    config::app::App,
    util::cli::{Report, Reportable},
};
use serde::{Deserialize, Serialize};
use std::{collections::BTreeMap, fs, io, path::PathBuf};
use thiserror::Error;
use toml::Value;

#[derive(Debug, Error)]
pub enum LoadError {
    #[error("Failed to create \".cargo\" directory at {path}: {cause}")]
    DirCreationFailed { path: PathBuf, cause: io::Error },
    #[error("Failed to rename cargo config from old style {from} to new style {to}: {cause}")]
    MigrateFailed {
        from: PathBuf,
        to: PathBuf,
        cause: io::Error,
    },
    #[error("Failed to read cargo config from {path}: {cause}")]
    ReadFailed { path: PathBuf, cause: io::Error },
    #[error("Failed to deserialize cargo config at {path}: {cause}")]
    DeserializeFailed {
        path: PathBuf,
        cause: toml::de::Error,
    },
}

impl Reportable for LoadError {
    fn report(&self) -> Report {
        Report::error("Failed to load .cargo file", self)
    }
}

#[derive(Debug, Error)]
pub enum WriteError {
    #[error("Failed to serialize cargo config: {0}")]
    SerializeFailed(toml::ser::Error),
    #[error("Failed to create \".cargo\" directory at {path}: {cause}")]
    DirCreationFailed { path: PathBuf, cause: io::Error },
    #[error("Failed to write cargo config to {path}: {cause}")]
    WriteFailed { path: PathBuf, cause: io::Error },
}

impl Reportable for WriteError {
    fn report(&self) -> Report {
        Report::error("Failed to write .cargo", self)
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct DotCargoBuild {
    target: String,
}

impl DotCargoBuild {
    pub fn new(target: impl Into<String>) -> Self {
        Self {
            target: target.into(),
        }
    }
}

#[derive(Debug, Default, Deserialize, Serialize)]
pub struct DotCargoTarget {
    pub linker: Option<String>,
    #[serde(default)]
    pub rustflags: Vec<String>,
}

impl DotCargoTarget {
    pub fn is_empty(&self) -> bool {
        self.linker.is_none() && self.rustflags.is_empty()
    }
}

#[derive(Debug, Default, Deserialize, Serialize)]
pub struct DotCargo {
    build: Option<DotCargoBuild>,
    target: BTreeMap<String, DotCargoTarget>,
    #[serde(flatten)]
    extra: BTreeMap<String, Value>,
}

impl DotCargo {
    fn create_dir_and_get_path(app: &App) -> Result<PathBuf, (PathBuf, io::Error)> {
        let dir = app.prefix_path(".cargo");
        fs::create_dir_all(&dir)
            .map(|()| dir.join("config.toml"))
            .map_err(|cause| (dir, cause))
    }

    pub fn load(app: &App) -> Result<Self, LoadError> {
        let path = Self::create_dir_and_get_path(app)
            .map_err(|(path, cause)| LoadError::DirCreationFailed { path, cause })?;
        let old_style = path
            .parent()
            .expect("developer error: cargo config path had no parent")
            .join("config");
        if old_style.is_file() {
            // Migrate from old-style cargo config
            std::fs::rename(&old_style, &path).map_err(|cause| LoadError::MigrateFailed {
                from: old_style,
                to: path.clone(),
                cause,
            })?;
        }
        if path.is_file() {
            let toml_str = fs::read_to_string(&path).map_err(|cause| LoadError::ReadFailed {
                path: path.clone(),
                cause,
            })?;
            toml::from_str(&toml_str).map_err(|cause| LoadError::DeserializeFailed { path, cause })
        } else {
            Ok(Self::default())
        }
    }

    pub fn set_default_target(&mut self, target: impl Into<String>) {
        self.build = Some(DotCargoBuild::new(target));
    }

    pub fn insert_target(&mut self, name: impl Into<String>, target: DotCargoTarget) {
        if !target.is_empty() {
            // merging could be nice, but is also very painful...
            self.target.insert(name.into(), target);
        }
    }

    pub fn write(self, app: &App) -> Result<(), WriteError> {
        let path = Self::create_dir_and_get_path(app)
            .map_err(|(path, cause)| WriteError::DirCreationFailed { path, cause })?;
        let ser = toml::to_string_pretty(&self).map_err(WriteError::SerializeFailed)?;
        fs::write(&path, ser).map_err(|cause| WriteError::WriteFailed { path, cause })
    }
}