-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbuild.rs
136 lines (119 loc) · 3.83 KB
/
build.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
#![deny(clippy::pedantic, clippy::indexing_slicing)]
#![allow(clippy::unnecessary_wraps)]
use askama::Template;
use serde::Deserialize;
use std::{env, error::Error, fs::File, io::Write, path::Path};
#[derive(Deserialize, Debug)]
#[serde(rename_all(deserialize = "camelCase"))]
struct Record {
atomic_number: String,
symbol: String,
name: String,
atomic_mass: String,
cpk_hex_color: String,
electronic_configuration: String,
electronegativity: String,
atomic_radius: String,
ion_radius: String,
van_del_waals_radius: String,
ionization_energy: String,
electron_affinity: String,
oxidation_states: String,
standard_state: String,
bonding_type: String,
melting_point: String,
boiling_point: String,
density: String,
group_block: String,
year_discovered: String,
}
#[derive(Template)]
#[template(path = "elements.rs", escape = "none")]
struct Data {
elements: Vec<Record>,
}
mod filters {
/// Return the string with the first character in uppercase
fn uppercase(string: &str) -> String {
format!(
"{}{}",
string.chars().next().unwrap().to_uppercase(),
string.get(1..).unwrap()
)
}
/// Return the string surrounded with double quotes
pub fn str(string: &str) -> askama::Result<String> {
Ok(format!("\"{string}\""))
}
/// Return the literal wrapped in an option
pub fn option(string: &str) -> askama::Result<String> {
Ok(if string.is_empty() {
String::from("None")
} else {
format!("Some({string})")
})
}
/// Return the literal wrapped in an option as an f32
pub fn option_f32(string: &str) -> askama::Result<String> {
Ok(if string.is_empty() {
String::from("None")
} else {
format!("Some({string}_f32)")
})
}
/// Return the literal wrapped in an option as a `IonRadius`
pub fn option_ion_radius(string: &str) -> askama::Result<String> {
Ok(if string.is_empty() {
String::from("None")
} else {
let mut parts = string.split(' ');
let first = parts.next().unwrap().trim();
let second = parts.next().unwrap().trim();
format!("Some(IonRadius {{ radius: {first}_f32, variation: \"{second}\", }})",)
})
}
/// Return the literal wrapped in an option as a State
pub fn option_state(string: &str) -> askama::Result<String> {
Ok(if string.is_empty() {
String::from("None")
} else {
format!("Some(State::{})", uppercase(string))
})
}
/// Return the literal as a Year
pub fn year(string: &str) -> askama::Result<String> {
Ok(if string == "Ancient" {
String::from("Year::Ancient")
} else {
format!("Year::Known({string})")
})
}
/// Return the literal as a Vector
pub fn slice(string: &str) -> askama::Result<String> {
Ok(format!("&[{string}]"))
}
/// Print the variable in a suitable way for the documentation
pub fn doc(string: &str) -> askama::Result<String> {
if string.trim().is_empty() {
Ok("*unknown*".to_string())
} else {
Ok(format!("`{string}`"))
}
}
}
/// Generate the lib.txt file with an element list
fn main() -> Result<(), Box<dyn Error>> {
let out_dir = env::var("OUT_DIR")?;
let dest_path = Path::new(&out_dir).join("elements.rs");
// create CSV reader
let mut reader = csv::ReaderBuilder::new()
.trim(csv::Trim::All)
.from_path("data.csv")?;
// get elements
let elements = reader.deserialize().collect::<Result<Vec<Record>, _>>()?;
// render template
let mut f = File::create(dest_path)?;
let data = Data { elements };
writeln!(f, "{}", data.render()?)?;
Ok(())
}