oro_common/
build_manifest.rs1use std::{
2 collections::HashMap,
3 ffi::OsStr,
4 path::{Path, PathBuf},
5};
6
7use serde::{Deserialize, Serialize};
8use walkdir::WalkDir;
9
10use crate::{Bin, Directories, Manifest};
11
12#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(rename_all = "camelCase")]
14struct RawBuildManifest {
15 #[serde(default)]
16 pub name: Option<String>,
17
18 #[serde(default)]
19 pub bin: Option<Bin>,
20
21 #[serde(default)]
22 pub directories: Option<Directories>,
23
24 #[serde(default)]
25 pub scripts: HashMap<String, String>,
26}
27
28#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)]
32#[serde(rename_all = "camelCase")]
33pub struct BuildManifest {
34 #[serde(default)]
36 pub bin: HashMap<String, PathBuf>,
37
38 #[serde(default)]
40 pub scripts: HashMap<String, String>,
41}
42
43impl BuildManifest {
44 pub fn from_path(path: impl AsRef<Path>) -> std::io::Result<Self> {
47 let path = path.as_ref();
48 let pkg_str = std::fs::read_to_string(path)?;
49 let raw: RawBuildManifest = serde_json::from_str(&pkg_str)?;
50 Self::normalize(raw)
51 }
52
53 pub fn from_manifest(manifest: &Manifest) -> std::io::Result<Self> {
56 let raw = RawBuildManifest {
59 name: manifest.name.clone(),
60 bin: manifest.bin.clone(),
61 directories: manifest.directories.clone(),
62 scripts: manifest.scripts.clone(),
63 };
64 Self::normalize(raw)
65 }
66
67 fn normalize(raw: RawBuildManifest) -> std::io::Result<Self> {
68 let mut bin_map = HashMap::new();
69 if let Some(Bin::Hash(bins)) = raw.bin {
70 for (name, bin) in bins {
71 bin_map.insert(name, bin);
72 }
73 } else if let Some(Bin::Str(bin)) = raw.bin {
74 if let Some(name) = raw.name {
75 bin_map.insert(name, PathBuf::from(bin));
76 }
77 } else if let Some(Bin::Array(bins)) = raw.bin {
78 for bin in bins {
79 let name = bin
80 .as_path()
81 .file_name()
82 .ok_or_else(|| {
83 std::io::Error::new(
84 std::io::ErrorKind::InvalidData,
85 format!("invalid bin name: {}", bin.to_string_lossy()),
86 )
87 })?
88 .to_string_lossy()
89 .to_string();
90 bin_map.insert(name, bin);
91 }
92 } else if let Some(Directories {
93 bin: Some(bin_dir), ..
94 }) = raw.directories
95 {
96 for entry in WalkDir::new(bin_dir) {
97 let entry = entry?;
98 let path = entry.path();
99 if path.starts_with(".") {
100 continue;
101 }
102 if let Some(file_name) = path.file_name() {
103 bin_map.insert(file_name.to_string_lossy().to_string(), path.into());
104 }
105 }
106 };
107 let mut normalized = HashMap::new();
108 for (name, bin) in &bin_map {
109 let base = Path::new(name).file_name();
110 if base.is_none() || base == Some(OsStr::new("")) {
111 continue;
112 }
113 let base = Path::new("/")
114 .join(Path::new(
115 &base
116 .unwrap()
117 .to_string_lossy()
118 .to_string()
119 .replace(['\\', ':'], "/"),
120 ))
121 .strip_prefix(
122 #[cfg(windows)]
123 "\\",
124 #[cfg(not(windows))]
125 "/",
126 )
127 .expect("We added this ourselves")
128 .file_name()
129 .map(PathBuf::from);
130 if base.is_none() || base == Some(PathBuf::from("")) {
131 continue;
132 }
133
134 let base = base.unwrap();
135
136 let bin_target = Path::new("/")
137 .join(bin.to_string_lossy().to_string())
138 .strip_prefix(
139 #[cfg(windows)]
140 "\\",
141 #[cfg(not(windows))]
142 "/",
143 )
144 .expect("We added this ourselves")
145 .to_path_buf();
146 if bin_target == Path::new("") {
147 continue;
148 }
149
150 normalized.insert(base.to_string_lossy().to_string(), bin_target);
151 }
152 Ok(Self {
153 bin: normalized,
154 scripts: raw.scripts,
155 })
156 }
157}