1use std::collections::BTreeMap;
2use std::collections::HashSet;
3use std::env;
4use std::fs;
5use std::mem;
6use std::path;
7use std::time::Instant;
8
9use crate::module::SoftwareModule;
10use flatpak_rs::module::FlatpakModule;
11
12pub const MODULES_DB_SUBDIR: &str = "/modules";
13
14pub struct Database {
15 pub modules: Vec<SoftwareModule>,
16}
17impl Database {
18 pub fn get_database() -> Database {
19 if let Err(e) = fs::create_dir_all(Database::get_modules_db_path()) {
20 panic!("Could not initialize database directory: {}.", e);
21 }
22
23 let before_loading = Instant::now();
24 let database = Database {
25 modules: Database::get_all_modules(),
26 };
27 let loading_duration = before_loading.elapsed();
28 if loading_duration.as_secs() == 0 {
29 log::info!("Loading the database took {}ms.", loading_duration.as_millis());
30 } else {
31 log::info!("Loading the database took {}s.", loading_duration.as_secs());
32 }
33
34 database
35 }
36
37 pub fn get_stats(&self) -> String {
38 let mut response = "".to_string();
39 response += &format!("Modules: {}.\n", self.modules.len());
40
41 let mut updateable_module_count = 0;
42 let mut module_build_systems_count: BTreeMap<String, i64> = BTreeMap::new();
43
44 for module in &self.modules {
45 if module.flatpak_module.uses_external_data_checker() {
46 updateable_module_count += 1;
47 }
48 if let Some(build_system) = &module.flatpak_module.buildsystem {
49 let build_system_name = build_system.to_string();
50 let new_build_system_count =
51 module_build_systems_count.get(&build_system_name).unwrap_or(&0) + 1;
52 module_build_systems_count.insert(build_system_name.to_string(), new_build_system_count);
53 }
54 }
55 response += &format!("Modules supporting updates: {}.\n", updateable_module_count);
56
57 response += &format!(
58 "Database in-memory size: {}.\n",
59 crate::utils::format_bytes(self.get_database_memory_size())
60 );
61
62 for (build_system, build_system_count) in module_build_systems_count {
63 response += &format!(
64 "{:05.2}% Modules use {}\n",
65 (build_system_count as f64 / self.modules.len() as f64) * 100.0,
66 build_system,
67 );
68 }
69
70 response
77 }
78
79 pub fn get_database_memory_size(&self) -> usize {
80 let mut db_size = 0;
81 for module in &self.modules {
82 db_size += mem::size_of_val(module);
83 }
84 return db_size;
85 }
86
87 pub fn get_db_path() -> String {
88 let default_db_path: String = match env::var("HOME") {
89 Ok(h) => format!("{}/.fpm-db", h),
90 Err(_e) => ".fpm-db".to_string(),
91 };
92
93 let db_path = match env::var("FPM_DB_DIR") {
94 Ok(p) => p,
95 Err(_e) => {
96 log::debug!("FPM_DB_DIR is not defined. Defaulting to {}.", default_db_path);
97 return default_db_path;
98 }
99 };
100 if let Err(e) = fs::create_dir_all(&db_path) {
101 panic!("Could not initialize DB directory: {}.", e);
102 }
103 db_path
104 }
105
106 pub fn get_modules_db_path() -> String {
107 Database::get_db_path() + MODULES_DB_SUBDIR
108 }
109
110 pub fn get_all_modules() -> Vec<SoftwareModule> {
111 let modules_path = Database::get_modules_db_path();
112 let modules_path = path::Path::new(&modules_path);
113 let all_modules_paths = match crate::utils::get_all_paths(modules_path) {
114 Ok(paths) => paths,
115 Err(e) => {
116 log::error!("Could not get modules from database: {}.", e);
117 return vec![];
118 }
119 };
120 let mut modules: Vec<SoftwareModule> = vec![];
121 for module_path in all_modules_paths.iter() {
122 let module_path_str = module_path.to_str().unwrap();
123 if !module_path.is_file() {
124 log::debug!("{} is not a file.", &module_path_str);
125 continue;
126 }
127 if !module_path_str.ends_with("yml") && !module_path_str.ends_with("yaml") {
129 continue;
130 }
131 let module_content = match fs::read_to_string(module_path) {
132 Ok(content) => content,
133 Err(e) => {
134 log::debug!("Could not read module file {}: {}.", &module_path_str, e);
135 continue;
136 }
137 };
138 let module = match serde_yaml::from_str(&module_content) {
139 Ok(m) => m,
140 Err(e) => {
141 log::debug!("Could not parse module file at {}: {}.", &module_path_str, e);
142 continue;
143 }
144 };
145 modules.push(module);
146 }
147 modules
148 }
149
150 pub fn search_modules(&self, search_term: &str) -> Vec<&FlatpakModule> {
151 let mut modules: Vec<&FlatpakModule> = vec![];
152 for module in &self.modules {
153 if module
154 .flatpak_module
155 .name
156 .to_lowercase()
157 .contains(&search_term.to_lowercase())
158 {
159 modules.push(&module.flatpak_module);
160 }
161 }
162 modules
163 }
164
165 pub fn remove_module() {}
166
167 pub fn add_module(&mut self, new_module: FlatpakModule) {
168 let module_hash = crate::utils::get_module_hash(&new_module);
169 let mut new_software_module = SoftwareModule::default();
170 new_software_module.flatpak_module = new_module;
171
172 let modules_path = Database::get_modules_db_path();
173 let new_module_path = format!("{}/{}.yaml", modules_path, module_hash);
174 log::info!("Adding module at {}", new_module_path);
175 let new_module_fs_path = path::Path::new(&new_module_path);
176 if new_module_fs_path.exists() {
177 return;
180 }
181 match fs::write(
182 new_module_fs_path,
183 serde_yaml::to_string(&new_software_module).unwrap(),
184 ) {
185 Ok(content) => content,
186 Err(e) => {
187 eprintln!(
188 "Could not write new module at {}: {}",
189 new_module_path.to_string(),
190 e
191 );
192 }
193 };
194 self.modules.push(new_software_module);
195 }
196}