app.rs
1 pub mod browser; 2 mod database; 3 4 use crate::profile::Profile; 5 use adw::Application; 6 use anyhow::Result; 7 use browser::Browser; 8 use gtk::{ 9 glib::ExitCode, 10 prelude::{ActionExt, ApplicationExt, ApplicationExtManual, GtkApplicationExt}, 11 }; 12 use sqlite::Transaction; 13 use std::rc::Rc; 14 15 const APPLICATION_ID: &str = "io.github.yggverse.Yoda"; 16 17 pub struct App { 18 profile: Rc<Profile>, 19 application: Application, 20 } 21 22 impl App { 23 // Constructors 24 25 /// Build new `Self` 26 pub fn build(profile: Profile) -> Result<Self> { 27 // Init GTK 28 let application = Application::builder() 29 .application_id(APPLICATION_ID) 30 .build(); 31 32 // Init components 33 let profile = Rc::new(profile); 34 let browser = Rc::new(Browser::build(&profile)); 35 36 // Prevent startup warning @TODO 37 application.connect_activate(|_| {}); 38 39 // Init events 40 application.connect_startup({ 41 let browser = browser.clone(); 42 let profile = profile.clone(); 43 move |this| { 44 // Init readable connection 45 match profile.database.pool.get() { 46 Ok(connection) => { 47 // Create transaction 48 match connection.unchecked_transaction() { 49 Ok(transaction) => { 50 // Restore previous session from DB 51 match database::select(&transaction) { 52 Ok(records) => { 53 // Restore child components 54 for record in records { 55 if let Err(e) = browser.restore(&transaction, record.id) 56 { 57 todo!("{e}") 58 } 59 } 60 } 61 Err(e) => todo!("{e}"), 62 } 63 } 64 Err(e) => todo!("{e}"), 65 } 66 } 67 Err(e) => todo!("{e}"), 68 } 69 70 // Run initial features, show widget 71 browser.init(Some(this)).present(); 72 } 73 }); 74 75 application.connect_shutdown({ 76 let browser = browser.clone(); 77 let profile = profile.clone(); 78 move |_| { 79 match profile.save() { 80 Ok(_) => match profile.database.pool.get() { 81 Ok(mut connection) => { 82 // Create transaction 83 match connection.transaction() { 84 Ok(transaction) => { 85 match database::select(&transaction) { 86 Ok(records) => { 87 // Cleanup previous session records 88 for record in records { 89 match database::delete(&transaction, record.id) { 90 Ok(_) => { 91 // Delegate clean action to childs 92 if let Err(e) = 93 browser.clean(&transaction, record.id) 94 { 95 todo!("{e}") 96 } 97 } 98 Err(e) => todo!("{e}"), 99 } 100 } 101 102 // Save current session to DB 103 match database::insert(&transaction) { 104 Ok(_) => { 105 // Delegate save action to childs 106 if let Err(e) = browser.save( 107 &transaction, 108 database::last_insert_id(&transaction), 109 ) { 110 todo!("{e}") 111 } 112 } 113 Err(e) => todo!("{e}"), 114 } 115 } 116 Err(e) => todo!("{e}"), 117 } 118 119 // Confirm changes 120 if let Err(e) = transaction.commit() { 121 todo!("{e}") 122 } 123 } 124 Err(e) => todo!("{e}"), 125 } 126 } 127 Err(e) => todo!("{e}"), 128 }, 129 Err(e) => todo!("{e}"), 130 } 131 } 132 }); 133 134 // Init accels 135 for (detailed_action_name, accels) in &[ 136 // Browser actions 137 ( 138 format!( 139 "{}.{}", 140 browser.action.id, 141 browser.action.close.simple_action.name() 142 ), 143 ["<Primary>Escape"], 144 ), 145 ( 146 format!( 147 "{}.{}", 148 browser.action.id, 149 browser.action.debug.simple_action.name() 150 ), 151 ["<Primary>i"], 152 ), 153 // Tab actions 154 ( 155 format!( 156 "{}.{}", 157 browser.window.action.id, 158 browser.window.action.append.simple_action.name() 159 ), 160 ["<Primary>t"], 161 ), 162 ( 163 format!( 164 "{}.{}", 165 browser.window.action.id, 166 browser.window.action.bookmark.simple_action.name() 167 ), 168 ["<Primary>b"], 169 ), 170 ( 171 format!( 172 "{}.{}", 173 browser.window.action.id, 174 browser.window.action.find.simple_action.name() 175 ), 176 ["<Primary>f"], 177 ), 178 ( 179 format!( 180 "{}.{}", 181 browser.window.action.id, 182 browser.window.action.pin.simple_action.name() 183 ), 184 ["<Primary>p"], 185 ), 186 ( 187 format!( 188 "{}.{}", 189 browser.window.action.id, 190 browser.window.action.reload.simple_action.name() 191 ), 192 ["<Primary>r"], 193 ), 194 ( 195 format!( 196 "{}.{}", 197 browser.window.action.id, 198 browser.window.action.save_as.simple_action.name() 199 ), 200 ["<Primary>s"], 201 ), 202 ( 203 format!( 204 "{}.{}", 205 browser.window.action.id, 206 browser.window.action.open.simple_action.name() 207 ), 208 ["<Primary>o"], 209 ), 210 ( 211 format!( 212 "{}.{}", 213 browser.window.action.id, 214 browser.window.action.source.simple_action.name() 215 ), 216 ["<Primary>u"], 217 ), 218 ( 219 format!( 220 "{}.{}", 221 browser.window.action.id, 222 browser.window.action.home.simple_action.name() 223 ), 224 ["<Primary>h"], 225 ), 226 ( 227 format!( 228 "{}.{}", 229 browser.window.action.id, 230 browser.window.action.history_back.simple_action.name() 231 ), 232 ["<Primary>Left"], 233 ), 234 ( 235 format!( 236 "{}.{}", 237 browser.window.action.id, 238 browser.window.action.history_forward.simple_action.name() 239 ), 240 ["<Primary>Right"], 241 ), 242 ( 243 format!( 244 "{}.{}", 245 browser.window.action.id, 246 browser.window.action.close.simple_action.name() 247 ), 248 ["<Primary>q"], 249 ), 250 ] { 251 application.set_accels_for_action(detailed_action_name, accels); 252 } 253 254 // Return activated App struct 255 Ok(Self { 256 profile: profile.clone(), 257 application, 258 }) 259 } 260 261 // Actions 262 pub fn run(&self) -> Result<ExitCode> { 263 // Begin database migration @TODO 264 { 265 let mut connection = self.profile.database.pool.get()?; 266 let transaction = connection.transaction()?; 267 migrate(&transaction)?; 268 transaction.commit()?; 269 } // unlock database 270 271 // Start application 272 Ok(self.application.run()) 273 } 274 } 275 276 // Tools 277 fn migrate(tx: &Transaction) -> Result<()> { 278 // Migrate self components 279 database::init(tx)?; 280 281 // Delegate migration to childs 282 browser::migrate(tx)?; 283 284 // Success 285 Ok(()) 286 }