bricks/config/
lib.rs

1use std::{path::PathBuf, process::Command};
2
3use anyhow::Result;
4use home::home_dir;
5use serde::{Deserialize, Serialize};
6
7use super::overrides::{OverrideDatabase, Overrides};
8
9#[derive(Debug, Serialize, Deserialize)]
10pub enum LibKind {
11    #[serde(alias = "system")]
12    System,
13    #[serde(alias = "git")]
14    Git,
15}
16
17#[derive(Debug, Serialize, Deserialize)]
18pub struct Lib {
19    #[serde(default = "default_kind")]
20    pub kind: LibKind,
21    pub repo: Option<String>,
22    pub version: Option<String>,
23
24    pub overrides: Option<Overrides>,
25    //
26    // #[serde(default = "default_cflags")]
27    // pub cflags: String,
28    // #[serde(default = "default_ldflags")]
29    // pub ldflags: String,
30    //
31    // macos: Option<Platform>,
32    // windows: Option<Platform>,
33    // linux: Option<Platform>,
34}
35
36impl Lib {
37    // pub fn platform(&self) -> Option<&Platform> {
38    //     if cfg!(target_os = "linux") {
39    //         self.linux.as_ref()
40    //     } else if cfg!(target_os = "macos") {
41    //         self.macos.as_ref()
42    //     } else if cfg!(target_os = "windows") {
43    //         self.windows.as_ref()
44    //     } else {
45    //         None
46    //     }
47    // }
48    //
49    // pub fn cflags(&self) -> String {
50    //     let ext_cflags = &match self.platform() {
51    //         Some(platform) => match &platform.cflags {
52    //             Some(v) => v,
53    //             None => "",
54    //         },
55    //         None => "",
56    //     };
57    //     format!("{} {}", self.cflags, ext_cflags)
58    // }
59    //
60    // pub fn ldflags(&self) -> String {
61    //     let ext_ldflags = &match self.platform() {
62    //         Some(platform) => match &platform.ldflags {
63    //             Some(v) => v,
64    //             None => "",
65    //         },
66    //         None => "",
67    //     };
68    //     format!("{} {}", self.cflags, ext_ldflags)
69    // }
70
71    pub fn headers(&self, name: &str, override_db: &OverrideDatabase) -> Result<String> {
72        match self.kind {
73            LibKind::System => {
74                let output = Command::new("pkg-config")
75                    .arg(name)
76                    .arg("--cflags")
77                    .output()?;
78
79                Ok(String::from_utf8(output.stdout)?.trim_end().to_string())
80            }
81            LibKind::Git => {
82                let overrides = override_db.get(name);
83                let include_dir = if let Some(overrides) = overrides {
84                    overrides
85                        .include_dir
86                        .as_ref()
87                        .map(|include_dir| include_dir.to_string())
88                } else {
89                    None
90                };
91                let include_dir = match include_dir {
92                    Some(v) => v,
93                    None => "build/include".into(),
94                };
95                Ok(format!(
96                    "-I{}",
97                    self.pathify_repo()?.join(include_dir).display(),
98                ))
99            }
100        }
101    }
102
103    pub fn lib_links(&self, name: &str, override_db: &OverrideDatabase) -> Result<String> {
104        match self.kind {
105            LibKind::System => {
106                let output = Command::new("pkg-config")
107                    .arg(name)
108                    .args(["--libs", "--static"])
109                    .output()?;
110
111                Ok(String::from_utf8(output.stdout)?.trim_end().to_string())
112            }
113            LibKind::Git => {
114                let overrides = override_db.get(name);
115                let lib_dir = if let Some(overrides) = overrides {
116                    overrides
117                        .lib_dir
118                        .as_ref()
119                        .map(|include_dir| include_dir.to_string())
120                } else {
121                    None
122                };
123                let lib_dir = match lib_dir {
124                    Some(v) => v,
125                    None => "build/lib".into(),
126                };
127                Ok(format!(
128                    "-L{} -l{}",
129                    self.pathify_repo()?.join(lib_dir).display(),
130                    name,
131                ))
132            }
133        }
134    }
135
136    pub fn normalize_repo(&self) -> Option<String> {
137        match &self.repo {
138            Some(repo) => {
139                let mut new_repo = repo.clone();
140                if !repo.starts_with("https://") {
141                    new_repo = format!("https://{}", new_repo);
142                }
143                if !new_repo.ends_with(".git") {
144                    new_repo = format!("{}.git", new_repo)
145                }
146                Some(new_repo)
147            }
148            None => None,
149        }
150    }
151
152    pub fn directify_repo(&self) -> Option<String> {
153        match &self.repo {
154            Some(repo) => {
155                let new_repo = repo
156                    .trim_start_matches("https://")
157                    .trim_end_matches(".git")
158                    .replace("/", "-")
159                    .to_string();
160                Some(new_repo)
161            }
162            None => None,
163        }
164    }
165
166    pub fn pathify_repo_no_version(&self) -> Result<PathBuf, LibPathificationError> {
167        let home = home_dir().ok_or(LibPathificationError::HomeDirMissing)?;
168        let lib_dir = self
169            .directify_repo()
170            .ok_or(LibPathificationError::RepoUriMissing)?;
171
172        Ok(home.join(".bricks").join("libs").join(lib_dir))
173    }
174
175    pub fn pathify_repo(&self) -> Result<PathBuf, LibPathificationError> {
176        let home = home_dir().ok_or(LibPathificationError::HomeDirMissing)?;
177        let lib_dir = self
178            .directify_repo()
179            .ok_or(LibPathificationError::RepoUriMissing)?;
180        let version = self
181            .version
182            .as_ref()
183            .ok_or(LibPathificationError::VersionMissing)?;
184
185        Ok(home
186            .join(".bricks")
187            .join("libs")
188            .join(lib_dir)
189            .join(version))
190    }
191}
192
193#[derive(thiserror::Error, Debug)]
194pub enum LibPathificationError {
195    #[error("unable to get the home directory")]
196    HomeDirMissing,
197    #[error("lib is missing the `repo` property")]
198    RepoUriMissing,
199    #[error("lib is missing the `version` property")]
200    VersionMissing,
201}
202
203#[cfg(test)]
204mod tests {
205    use std::path::Path;
206
207    use super::*;
208
209    fn basic_lib() -> Lib {
210        Lib {
211            kind: LibKind::Git,
212            repo: Some("github.com/Tesohh/strings.git".to_string()),
213            version: Some("2020".to_string()),
214            overrides: None,
215
216            cflags: "".into(),
217            ldflags: "".into(),
218            macos: None,
219            windows: None,
220            linux: None,
221        }
222    }
223
224    #[test]
225    fn no_repo_should_return_none() {
226        let mut no_repo_lib = basic_lib();
227        no_repo_lib.repo = None;
228
229        assert_eq!(no_repo_lib.normalize_repo(), None);
230        assert_eq!(no_repo_lib.directify_repo(), None);
231        assert!(no_repo_lib.pathify_repo().is_err());
232    }
233
234    #[test]
235    fn repo_normalization() {
236        let mut lib = basic_lib();
237        lib.repo = Some("github.com/Tesohh/strings.git".to_string());
238
239        assert_eq!(
240            lib.normalize_repo(),
241            Some("https://github.com/Tesohh/strings.git".into())
242        );
243
244        lib.repo = Some("https://github.com/Tesohh/strings".into());
245        assert_eq!(
246            lib.normalize_repo(),
247            Some("https://github.com/Tesohh/strings.git".into())
248        );
249
250        lib.repo = Some("github.com/Tesohh/strings".into());
251        assert_eq!(
252            lib.normalize_repo(),
253            Some("https://github.com/Tesohh/strings.git".into())
254        );
255    }
256
257    #[test]
258    fn repo_directification() {
259        let lib = basic_lib();
260
261        assert_eq!(
262            lib.directify_repo(),
263            Some("github.com-Tesohh-strings".to_string())
264        );
265    }
266
267    #[test]
268    fn repo_pathification() {
269        let lib = basic_lib();
270
271        let path = lib.pathify_repo().unwrap();
272
273        assert_eq!(
274            path,
275            Path::new(&home_dir().unwrap())
276                .join(".bricks")
277                .join("libs")
278                .join("github.com-Tesohh-strings")
279                .join("2020")
280                .to_path_buf()
281        );
282    }
283
284    #[test]
285    fn git_headers_and_lib_links() {
286        let lib = basic_lib();
287        let override_db = OverrideDatabase::new();
288        assert_eq!(
289            lib.headers("strings", &override_db).unwrap(),
290            "-I".to_string()
291                + &Path::new(&home_dir().unwrap())
292                    .join(".bricks/libs")
293                    .join("github.com-Tesohh-strings/2020")
294                    .join("build/include")
295                    .display()
296                    .to_string()
297        );
298        assert_eq!(
299            lib.lib_links("strings", &override_db).unwrap(),
300            "-L".to_string()
301                + &Path::new(&home_dir().unwrap())
302                    .join(".bricks/libs")
303                    .join("github.com-Tesohh-strings/2020")
304                    .join("build/lib")
305                    .display()
306                    .to_string()
307                + " -lstrings"
308        );
309    }
310
311    #[test]
312    fn overrides() {
313        let lib = basic_lib();
314        let mut override_db = OverrideDatabase::new();
315        override_db.insert(
316            "strings".into(),
317            Overrides {
318                build: None,
319                run: None,
320                include_dir: Some("evil_build/evil_include".to_string()),
321                lib_dir: Some("evil_build/evil_lib".to_string()),
322            },
323        );
324
325        assert_eq!(
326            lib.headers("strings", &override_db).unwrap(),
327            "-I".to_string()
328                + &Path::new(&home_dir().unwrap())
329                    .join(".bricks")
330                    .join("libs")
331                    .join("github.com-Tesohh-strings")
332                    .join("2020")
333                    .join("evil_build/evil_include")
334                    .display()
335                    .to_string()
336        );
337        assert_eq!(
338            lib.lib_links("strings", &override_db).unwrap(),
339            "-L".to_string()
340                + &Path::new(&home_dir().unwrap())
341                    .join(".bricks")
342                    .join("libs")
343                    .join("github.com-Tesohh-strings")
344                    .join("2020")
345                    .join("evil_build/evil_lib")
346                    .display()
347                    .to_string()
348                + " -lstrings"
349        );
350    }
351}
352
353fn default_kind() -> LibKind {
354    LibKind::Git
355}