Skip to main content

amlich_core/
insight_data.rs

1use serde::Deserialize;
2use std::collections::HashMap;
3use std::sync::OnceLock;
4
5const CANCHI_JSON: &str = include_str!("../data/canchi.json");
6const TIET_KHI_JSON: &str = include_str!("../data/tiet-khi.json");
7
8#[derive(Debug, Deserialize, Clone)]
9pub struct BilingualText {
10    pub vi: String,
11    pub en: String,
12}
13
14#[derive(Debug, Deserialize, Clone)]
15pub struct BilingualList {
16    pub vi: Vec<String>,
17    pub en: Vec<String>,
18}
19
20#[derive(Debug, Deserialize, Clone)]
21pub struct CanInfo {
22    pub name: String,
23    pub element: String,
24    pub meaning: BilingualText,
25    pub nature: BilingualText,
26}
27
28#[derive(Debug, Deserialize, Clone)]
29pub struct ChiInfo {
30    pub name: String,
31    pub animal: BilingualText,
32    pub element: String,
33    pub meaning: BilingualText,
34    pub hours: String,
35}
36
37#[derive(Debug, Deserialize, Clone)]
38pub struct ElementInfo {
39    pub name: BilingualText,
40    pub nature: BilingualText,
41}
42
43#[derive(Debug, Deserialize, Clone)]
44#[serde(rename_all = "camelCase")]
45pub struct DayGuidance {
46    pub good_for: BilingualList,
47    pub avoid_for: BilingualList,
48}
49
50#[derive(Debug, Deserialize)]
51#[serde(rename_all = "camelCase")]
52struct CanChiFile {
53    can: Vec<CanInfo>,
54    chi: Vec<ChiInfo>,
55    elements: HashMap<String, ElementInfo>,
56    day_guidance: HashMap<String, DayGuidance>,
57}
58
59#[derive(Debug, Deserialize, Clone)]
60pub struct TietKhiInsight {
61    pub id: String,
62    pub name: BilingualText,
63    pub longitude: i32,
64    pub meaning: BilingualText,
65    pub astronomy: BilingualText,
66    pub agriculture: BilingualList,
67    pub health: BilingualList,
68    pub weather: BilingualText,
69}
70
71#[derive(Debug, Deserialize)]
72#[serde(rename_all = "camelCase")]
73struct TietKhiFile {
74    tiet_khi: Vec<TietKhiInsight>,
75}
76
77static CANCHI_DATA: OnceLock<CanChiFile> = OnceLock::new();
78static TIET_KHI_DATA: OnceLock<TietKhiFile> = OnceLock::new();
79
80fn canchi_data() -> &'static CanChiFile {
81    CANCHI_DATA.get_or_init(|| {
82        serde_json::from_str(CANCHI_JSON).expect("Failed to parse data/canchi.json")
83    })
84}
85
86fn tiet_khi_data() -> &'static TietKhiFile {
87    TIET_KHI_DATA.get_or_init(|| {
88        serde_json::from_str(TIET_KHI_JSON).expect("Failed to parse data/tiet-khi.json")
89    })
90}
91
92pub fn all_can() -> &'static [CanInfo] {
93    &canchi_data().can
94}
95
96pub fn all_chi() -> &'static [ChiInfo] {
97    &canchi_data().chi
98}
99
100pub fn all_elements() -> &'static HashMap<String, ElementInfo> {
101    &canchi_data().elements
102}
103
104pub fn all_day_guidance() -> &'static HashMap<String, DayGuidance> {
105    &canchi_data().day_guidance
106}
107
108pub fn all_tiet_khi_insights() -> &'static [TietKhiInsight] {
109    &tiet_khi_data().tiet_khi
110}
111
112pub fn find_can(name: &str) -> Option<&'static CanInfo> {
113    all_can().iter().find(|item| item.name == name)
114}
115
116pub fn find_chi(name: &str) -> Option<&'static ChiInfo> {
117    all_chi().iter().find(|item| item.name == name)
118}
119
120pub fn get_day_guidance(chi_name: &str) -> Option<&'static DayGuidance> {
121    all_day_guidance().get(chi_name)
122}
123
124pub fn find_tiet_khi_insight(term_name: &str) -> Option<&'static TietKhiInsight> {
125    all_tiet_khi_insights()
126        .iter()
127        .find(|item| item.name.vi == term_name || item.name.en == term_name)
128}
129
130#[cfg(test)]
131mod tests {
132    use super::{
133        all_can, all_chi, all_day_guidance, all_elements, all_tiet_khi_insights,
134        find_tiet_khi_insight,
135    };
136
137    #[test]
138    fn parses_canchi_collections() {
139        assert_eq!(all_can().len(), 10);
140        assert_eq!(all_chi().len(), 12);
141        assert_eq!(all_elements().len(), 5);
142        assert_eq!(all_day_guidance().len(), 12);
143    }
144
145    #[test]
146    fn parses_all_tiet_khi() {
147        assert_eq!(all_tiet_khi_insights().len(), 24);
148    }
149
150    #[test]
151    fn lookup_tiet_khi_by_vi_name() {
152        let term = find_tiet_khi_insight("Xuân Phân").expect("Xuân Phân should exist");
153        assert_eq!(term.longitude, 0);
154        assert!(!term.health.vi.is_empty());
155    }
156}