1use anyhow::Result;
2use rand::distributions::Alphanumeric;
3use rand::{thread_rng, Rng};
4use serde::{Deserialize, Serialize};
5use std::path::Path;
6
7pub const DEFAULT_METADATA_FILE: &str = "meta.toml";
9
10#[derive(Debug, Default, Serialize, Deserialize)]
12pub struct Meta {
13 pub manifest: String,
14 pub project: String, pub name: String,
16 pub description: String,
17 pub authors: Vec<String>,
18 pub language: String,
19 #[serde(skip_serializing_if = "Option::is_none")]
20 pub build: Option<MetadataBuild>,
21}
22
23#[derive(Debug, Clone, Default, Serialize, Deserialize)]
25pub struct MetadataBuild {
26 pub rust_target_dir: Option<String>,
27}
28
29impl Meta {
30 pub fn from_file(path: &str) -> Result<Self> {
32 let content = std::fs::read_to_string(path)?;
33 let mut manifest: Meta = toml::from_str(&content)?;
34
35 if manifest.build.is_none() {
37 manifest.build = Some(MetadataBuild::default());
38 }
39
40 Ok(manifest)
41 }
42
43 pub fn from_binary(data: &[u8]) -> Result<Self> {
45 let manifest: Meta = toml::from_str(std::str::from_utf8(data)?)?;
46 Ok(manifest)
47 }
48
49 pub fn to_file(&self, path: &str) -> Result<()> {
51 let content = toml::to_string(&self)?;
52 std::fs::write(path, content)?;
53 Ok(())
54 }
55
56 pub fn get_arch(&self) -> String {
58 "wasm32-wasi".to_string()
59 }
60
61 pub fn is_wasi(&self) -> bool {
63 self.get_arch() == "wasm32-wasi"
64 }
65
66 pub fn get_target(&self) -> String {
68 let target = self
69 .build
70 .clone()
71 .unwrap_or_default()
72 .rust_target_dir
73 .unwrap_or_else(|| "target".to_string());
74 let arch = self.get_arch();
75 let target_dir = Path::new(&target).join(arch).join("release");
76 let name = self.name.replace('-', "_") + ".wasm";
77 target_dir.join(name).to_str().unwrap().to_string()
78 }
79
80 pub fn get_output(&self) -> String {
82 self.get_target().replace(".wasm", ".component.wasm")
83 }
84
85 pub fn get_src_dir(&self) -> String {
87 if self.language == "js" {
88 return "dist/".to_string();
89 }
90 "src/".to_string()
91 }
92
93 pub fn get_project_name(&self) -> String {
95 self.project.clone()
96 }
97
98 pub fn generate_project_name(&mut self) -> String {
100 let rand_string: String = thread_rng()
101 .sample_iter(&Alphanumeric)
102 .take(8)
103 .map(char::from)
104 .collect();
105 self.project = format!("{}-{}", self.name, rand_string.to_lowercase());
106 self.project.clone()
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113 #[test]
115 fn from_file() {
116 let manifest = Meta::from_file("../../tests/data/meta.toml").unwrap();
117 assert_eq!(manifest.manifest, "v1");
118 assert_eq!(manifest.name, "rust-basic");
119 assert_eq!(manifest.description, "example rust project");
120 assert_eq!(manifest.authors, vec!["your-name"]);
121 assert_eq!(manifest.language, "rust");
122 assert_eq!(
123 manifest.build.as_ref().unwrap().rust_target_dir,
124 Some("./target".to_string())
125 );
126 }
127
128 #[test]
130 fn to_file() {
131 let manifest = Meta::from_file("../../tests/data/meta.toml").unwrap();
132 manifest.to_file("../../tests/data/meta2.toml").unwrap();
133 let manifest2 = Meta::from_file("../../tests/data/meta2.toml").unwrap();
134 assert_eq!(manifest.manifest, manifest2.manifest);
135 assert_eq!(manifest.name, manifest2.name);
136 assert_eq!(manifest.description, manifest2.description);
137 assert_eq!(manifest.authors, manifest2.authors);
138 assert_eq!(manifest.language, manifest2.language);
139 assert_eq!(
140 manifest.build.as_ref().unwrap().rust_target_dir,
141 manifest2.build.as_ref().unwrap().rust_target_dir
142 );
143 std::fs::remove_file("../../tests/data/meta2.toml").unwrap();
144 }
145}