mirror of
https://github.com/YGGverse/Yoda.git
synced 2026-03-31 16:45:27 +00:00
use anyhow crate, return id on insert
This commit is contained in:
parent
e859b97d79
commit
5effd63575
42 changed files with 496 additions and 1164 deletions
|
|
@ -30,6 +30,7 @@ version = "0.9.1"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ansi-parser = "0.9.1"
|
ansi-parser = "0.9.1"
|
||||||
|
anyhow = "1.0.97"
|
||||||
ggemini = "0.17.0"
|
ggemini = "0.17.0"
|
||||||
ggemtext = "0.3.1"
|
ggemtext = "0.3.1"
|
||||||
indexmap = "2.7.0"
|
indexmap = "2.7.0"
|
||||||
|
|
|
||||||
10
src/app.rs
10
src/app.rs
|
|
@ -1,10 +1,10 @@
|
||||||
pub mod browser;
|
pub mod browser;
|
||||||
mod database;
|
mod database;
|
||||||
|
|
||||||
use browser::Browser;
|
|
||||||
|
|
||||||
use crate::profile::Profile;
|
use crate::profile::Profile;
|
||||||
use adw::Application;
|
use adw::Application;
|
||||||
|
use anyhow::Result;
|
||||||
|
use browser::Browser;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
glib::ExitCode,
|
glib::ExitCode,
|
||||||
prelude::{ActionExt, ApplicationExt, ApplicationExtManual, GtkApplicationExt},
|
prelude::{ActionExt, ApplicationExt, ApplicationExtManual, GtkApplicationExt},
|
||||||
|
|
@ -294,11 +294,9 @@ impl App {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
fn migrate(tx: &Transaction) -> Result<(), String> {
|
fn migrate(tx: &Transaction) -> Result<()> {
|
||||||
// Migrate self components
|
// Migrate self components
|
||||||
if let Err(e) = database::init(tx) {
|
database::init(tx)?;
|
||||||
return Err(e.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate migration to childs
|
// Delegate migration to childs
|
||||||
browser::migrate(tx)?;
|
browser::migrate(tx)?;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ use window::Window;
|
||||||
|
|
||||||
use crate::Profile;
|
use crate::Profile;
|
||||||
use adw::{prelude::AdwDialogExt, AboutDialog, Application};
|
use adw::{prelude::AdwDialogExt, AboutDialog, Application};
|
||||||
|
use anyhow::Result;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
gio::{Cancellable, File},
|
gio::{Cancellable, File},
|
||||||
prelude::GtkWindowExt,
|
prelude::GtkWindowExt,
|
||||||
|
|
@ -109,69 +110,42 @@ impl Browser {
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
|
|
||||||
pub fn clean(&self, transaction: &Transaction, app_id: i64) -> Result<(), String> {
|
pub fn clean(&self, transaction: &Transaction, app_id: i64) -> Result<()> {
|
||||||
match database::select(transaction, app_id) {
|
for record in database::select(transaction, app_id)? {
|
||||||
Ok(records) => {
|
database::delete(transaction, record.id)?;
|
||||||
for record in records {
|
|
||||||
match database::delete(transaction, record.id) {
|
|
||||||
Ok(_) => {
|
|
||||||
// Delegate clean action to childs
|
// Delegate clean action to childs
|
||||||
self.window.clean(transaction, record.id)?;
|
self.window.clean(transaction, record.id)?;
|
||||||
self.widget.clean(transaction, record.id)?;
|
self.widget.clean(transaction, record.id)?;
|
||||||
|
|
||||||
/* @TODO
|
/* @TODO
|
||||||
self.header.clean(transaction, &record.id)?; */
|
self.header.clean(transaction, &record.id)?; */
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn restore(&self, transaction: &Transaction, app_id: i64) -> Result<(), String> {
|
pub fn restore(&self, transaction: &Transaction, app_id: i64) -> Result<()> {
|
||||||
match database::select(transaction, app_id) {
|
for record in database::select(transaction, app_id)? {
|
||||||
Ok(records) => {
|
|
||||||
for record in records {
|
|
||||||
// Delegate restore action to childs
|
// Delegate restore action to childs
|
||||||
self.widget.restore(transaction, record.id)?;
|
self.widget.restore(transaction, record.id)?;
|
||||||
self.window.restore(transaction, record.id)?;
|
self.window.restore(transaction, record.id)?;
|
||||||
|
|
||||||
/* @TODO
|
/* @TODO
|
||||||
self.header.restore(transaction, &record.id)?; */
|
self.header.restore(transaction, &record.id)?; */
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save(&self, transaction: &Transaction, app_id: i64) -> Result<(), String> {
|
pub fn save(&self, transaction: &Transaction, app_id: i64) -> Result<()> {
|
||||||
match database::insert(transaction, app_id) {
|
let id = database::insert(transaction, app_id)?;
|
||||||
Ok(_) => {
|
|
||||||
let id = database::last_insert_id(transaction);
|
|
||||||
|
|
||||||
// Delegate save action to childs
|
// Delegate save action to childs
|
||||||
self.widget.save(transaction, id)?;
|
self.widget.save(transaction, id)?;
|
||||||
self.window.save(transaction, id)?;
|
self.window.save(transaction, id)?;
|
||||||
|
|
||||||
/* @TODO
|
/* @TODO
|
||||||
self.header.save(transaction, &id)?; */
|
self.header.save(transaction, &id)?; */
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(&self, application: Option<&Application>) -> &Self {
|
pub fn init(&self, application: Option<&Application>) -> &Self {
|
||||||
// Assign browser window to this application
|
// Assign browser window to this application
|
||||||
self.widget.application_window.set_application(application); // @TODO
|
self.widget.application_window.set_application(application); // @TODO
|
||||||
|
|
||||||
// Init main window
|
// Init main window
|
||||||
self.window.init();
|
self.window.init();
|
||||||
self
|
self
|
||||||
|
|
@ -184,11 +158,9 @@ impl Browser {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
pub fn migrate(tx: &Transaction) -> Result<()> {
|
||||||
// Migrate self components
|
// Migrate self components
|
||||||
if let Err(e) = database::init(tx) {
|
database::init(tx)?;
|
||||||
return Err(e.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate migration to childs
|
// Delegate migration to childs
|
||||||
/* @TODO
|
/* @TODO
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
use sqlite::{Error, Transaction};
|
use anyhow::Result;
|
||||||
|
use sqlite::Transaction;
|
||||||
|
|
||||||
pub struct Table {
|
pub struct Table {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
// pub app_id: i64, not in use
|
// pub app_id: i64, not in use
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
pub fn init(tx: &Transaction) -> Result<usize> {
|
||||||
tx.execute(
|
Ok(tx.execute(
|
||||||
"CREATE TABLE IF NOT EXISTS `app_browser`
|
"CREATE TABLE IF NOT EXISTS `app_browser`
|
||||||
(
|
(
|
||||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
|
@ -15,14 +16,15 @@ pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
||||||
FOREIGN KEY (`app_id`) REFERENCES `app`(`id`)
|
FOREIGN KEY (`app_id`) REFERENCES `app`(`id`)
|
||||||
)",
|
)",
|
||||||
[],
|
[],
|
||||||
)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(tx: &Transaction, app_id: i64) -> Result<usize, Error> {
|
pub fn insert(tx: &Transaction, app_id: i64) -> Result<i64> {
|
||||||
tx.execute("INSERT INTO `app_browser` (`app_id`) VALUES (?)", [app_id])
|
tx.execute("INSERT INTO `app_browser` (`app_id`) VALUES (?)", [app_id])?;
|
||||||
|
Ok(tx.last_insert_rowid())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select(tx: &Transaction, app_id: i64) -> Result<Vec<Table>, Error> {
|
pub fn select(tx: &Transaction, app_id: i64) -> Result<Vec<Table>> {
|
||||||
let mut stmt = tx.prepare("SELECT `id`, `app_id` FROM `app_browser` WHERE `app_id` = ?")?;
|
let mut stmt = tx.prepare("SELECT `id`, `app_id` FROM `app_browser` WHERE `app_id` = ?")?;
|
||||||
|
|
||||||
let result = stmt.query_map([app_id], |row| {
|
let result = stmt.query_map([app_id], |row| {
|
||||||
|
|
@ -42,10 +44,6 @@ pub fn select(tx: &Transaction, app_id: i64) -> Result<Vec<Table>, Error> {
|
||||||
Ok(records)
|
Ok(records)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(tx: &Transaction, id: i64) -> Result<usize, Error> {
|
pub fn delete(tx: &Transaction, id: i64) -> Result<usize> {
|
||||||
tx.execute("DELETE FROM `app_browser` WHERE `id` = ?", [id])
|
Ok(tx.execute("DELETE FROM `app_browser` WHERE `id` = ?", [id])?)
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_insert_id(tx: &Transaction) -> i64 {
|
|
||||||
tx.last_insert_rowid()
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ mod database;
|
||||||
|
|
||||||
use super::Window;
|
use super::Window;
|
||||||
use adw::ApplicationWindow;
|
use adw::ApplicationWindow;
|
||||||
|
use anyhow::Result;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
gio::SimpleActionGroup,
|
gio::SimpleActionGroup,
|
||||||
glib::GString,
|
glib::GString,
|
||||||
|
|
@ -59,29 +60,15 @@ impl Widget {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
pub fn clean(&self, transaction: &Transaction, app_browser_id: i64) -> Result<(), String> {
|
pub fn clean(&self, transaction: &Transaction, app_browser_id: i64) -> Result<()> {
|
||||||
match database::select(transaction, app_browser_id) {
|
for record in database::select(transaction, app_browser_id)? {
|
||||||
Ok(records) => {
|
database::delete(transaction, record.id)?;
|
||||||
for record in records {
|
|
||||||
match database::delete(transaction, record.id) {
|
|
||||||
Ok(_) => {
|
|
||||||
// Delegate clean action to childs
|
|
||||||
// nothing yet..
|
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn restore(&self, transaction: &Transaction, app_browser_id: i64) -> Result<(), String> {
|
pub fn restore(&self, transaction: &Transaction, app_browser_id: i64) -> Result<()> {
|
||||||
match database::select(transaction, app_browser_id) {
|
for record in database::select(transaction, app_browser_id)? {
|
||||||
Ok(records) => {
|
|
||||||
for record in records {
|
|
||||||
// Restore widget
|
// Restore widget
|
||||||
self.application_window.set_maximized(record.is_maximized);
|
self.application_window.set_maximized(record.is_maximized);
|
||||||
self.application_window
|
self.application_window
|
||||||
|
|
@ -90,43 +77,27 @@ impl Widget {
|
||||||
// Delegate restore action to childs
|
// Delegate restore action to childs
|
||||||
// nothing yet..
|
// nothing yet..
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save(&self, transaction: &Transaction, app_browser_id: i64) -> Result<(), String> {
|
pub fn save(&self, transaction: &Transaction, app_browser_id: i64) -> Result<()> {
|
||||||
match database::insert(
|
database::insert(
|
||||||
transaction,
|
transaction,
|
||||||
app_browser_id,
|
app_browser_id,
|
||||||
self.application_window.default_width(),
|
self.application_window.default_width(),
|
||||||
self.application_window.default_height(),
|
self.application_window.default_height(),
|
||||||
self.application_window.is_maximized(),
|
self.application_window.is_maximized(),
|
||||||
) {
|
)?;
|
||||||
Ok(_) => {
|
|
||||||
// Delegate save action to childs
|
|
||||||
// let id = self.database.last_insert_id(transaction);
|
|
||||||
// nothing yet..
|
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
pub fn migrate(tx: &Transaction) -> Result<()> {
|
||||||
// Migrate self components
|
// Migrate self components
|
||||||
if let Err(e) = database::init(tx) {
|
database::init(tx)?;
|
||||||
return Err(e.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate migration to childs
|
// Delegate migration to childs
|
||||||
// nothing yet..
|
// nothing yet..
|
||||||
|
|
||||||
// Success
|
// Success
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use sqlite::{Error, Transaction};
|
use anyhow::Result;
|
||||||
|
use sqlite::Transaction;
|
||||||
|
|
||||||
pub struct Table {
|
pub struct Table {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
|
|
@ -8,8 +9,8 @@ pub struct Table {
|
||||||
pub is_maximized: bool,
|
pub is_maximized: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
pub fn init(tx: &Transaction) -> Result<usize> {
|
||||||
tx.execute(
|
Ok(tx.execute(
|
||||||
"CREATE TABLE IF NOT EXISTS `app_browser_widget`
|
"CREATE TABLE IF NOT EXISTS `app_browser_widget`
|
||||||
(
|
(
|
||||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
|
@ -21,7 +22,7 @@ pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
||||||
FOREIGN KEY (`app_browser_id`) REFERENCES `app_browser`(`id`)
|
FOREIGN KEY (`app_browser_id`) REFERENCES `app_browser`(`id`)
|
||||||
)",
|
)",
|
||||||
[],
|
[],
|
||||||
)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(
|
pub fn insert(
|
||||||
|
|
@ -30,7 +31,7 @@ pub fn insert(
|
||||||
default_width: i32,
|
default_width: i32,
|
||||||
default_height: i32,
|
default_height: i32,
|
||||||
is_maximized: bool,
|
is_maximized: bool,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<i64> {
|
||||||
tx.execute(
|
tx.execute(
|
||||||
"INSERT INTO `app_browser_widget` (
|
"INSERT INTO `app_browser_widget` (
|
||||||
`app_browser_id`,
|
`app_browser_id`,
|
||||||
|
|
@ -44,10 +45,11 @@ pub fn insert(
|
||||||
default_height as i64,
|
default_height as i64,
|
||||||
is_maximized as i64,
|
is_maximized as i64,
|
||||||
],
|
],
|
||||||
)
|
)?;
|
||||||
|
Ok(tx.last_insert_rowid())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select(tx: &Transaction, app_browser_id: i64) -> Result<Vec<Table>, Error> {
|
pub fn select(tx: &Transaction, app_browser_id: i64) -> Result<Vec<Table>> {
|
||||||
let mut stmt = tx.prepare(
|
let mut stmt = tx.prepare(
|
||||||
"SELECT `id`,
|
"SELECT `id`,
|
||||||
`app_browser_id`,
|
`app_browser_id`,
|
||||||
|
|
@ -76,11 +78,6 @@ pub fn select(tx: &Transaction, app_browser_id: i64) -> Result<Vec<Table>, Error
|
||||||
Ok(records)
|
Ok(records)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(tx: &Transaction, id: i64) -> Result<usize, Error> {
|
pub fn delete(tx: &Transaction, id: i64) -> Result<usize> {
|
||||||
tx.execute("DELETE FROM `app_browser_widget` WHERE `id` = ?", [id])
|
Ok(tx.execute("DELETE FROM `app_browser_widget` WHERE `id` = ?", [id])?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* not in use
|
|
||||||
pub fn last_insert_id(tx: &Transaction) -> i64 {
|
|
||||||
tx.last_insert_rowid()
|
|
||||||
} */
|
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,16 @@ mod database;
|
||||||
mod header;
|
mod header;
|
||||||
pub mod tab;
|
pub mod tab;
|
||||||
|
|
||||||
use action::{Action, Position};
|
|
||||||
use adw::ToolbarView;
|
|
||||||
use header::Header;
|
|
||||||
use sqlite::Transaction;
|
|
||||||
use tab::Tab;
|
|
||||||
|
|
||||||
use super::Action as BrowserAction;
|
use super::Action as BrowserAction;
|
||||||
use crate::Profile;
|
use crate::Profile;
|
||||||
|
use action::{Action, Position};
|
||||||
|
use adw::ToolbarView;
|
||||||
|
use anyhow::Result;
|
||||||
use gtk::{prelude::BoxExt, Box, Orientation};
|
use gtk::{prelude::BoxExt, Box, Orientation};
|
||||||
|
use header::Header;
|
||||||
|
use sqlite::Transaction;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use tab::Tab;
|
||||||
|
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
pub action: Rc<Action>,
|
pub action: Rc<Action>,
|
||||||
|
|
@ -145,52 +145,26 @@ impl Window {
|
||||||
self.tab.escape();
|
self.tab.escape();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clean(&self, transaction: &Transaction, app_browser_id: i64) -> Result<(), String> {
|
pub fn clean(&self, transaction: &Transaction, app_browser_id: i64) -> Result<()> {
|
||||||
match database::select(transaction, app_browser_id) {
|
for record in database::select(transaction, app_browser_id)? {
|
||||||
Ok(records) => {
|
database::delete(transaction, record.id)?;
|
||||||
for record in records {
|
|
||||||
match database::delete(transaction, record.id) {
|
|
||||||
Ok(_) => {
|
|
||||||
// Delegate clean action to childs
|
// Delegate clean action to childs
|
||||||
self.tab.clean(transaction, record.id)?;
|
self.tab.clean(transaction, record.id)?;
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn restore(&self, transaction: &Transaction, app_browser_id: i64) -> Result<(), String> {
|
pub fn restore(&self, transaction: &Transaction, app_browser_id: i64) -> Result<()> {
|
||||||
match database::select(transaction, app_browser_id) {
|
for record in database::select(transaction, app_browser_id)? {
|
||||||
Ok(records) => {
|
|
||||||
for record in records {
|
|
||||||
// Delegate restore action to childs
|
// Delegate restore action to childs
|
||||||
self.tab.restore(transaction, record.id)?;
|
self.tab.restore(transaction, record.id)?;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save(&self, transaction: &Transaction, app_browser_id: i64) -> Result<(), String> {
|
pub fn save(&self, transaction: &Transaction, app_browser_id: i64) -> Result<()> {
|
||||||
match database::insert(transaction, app_browser_id) {
|
self.tab
|
||||||
Ok(_) => {
|
.save(transaction, database::insert(transaction, app_browser_id)?)?;
|
||||||
// Delegate save action to childs
|
|
||||||
if let Err(e) = self
|
|
||||||
.tab
|
|
||||||
.save(transaction, database::last_insert_id(transaction))
|
|
||||||
{
|
|
||||||
return Err(e.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -200,11 +174,9 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
pub fn migrate(tx: &Transaction) -> Result<()> {
|
||||||
// Migrate self components
|
// Migrate self components
|
||||||
if let Err(e) = database::init(tx) {
|
database::init(tx)?;
|
||||||
return Err(e.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate migration to childs
|
// Delegate migration to childs
|
||||||
tab::migrate(tx)?;
|
tab::migrate(tx)?;
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
use sqlite::{Error, Transaction};
|
use anyhow::Result;
|
||||||
|
use sqlite::Transaction;
|
||||||
|
|
||||||
pub struct Table {
|
pub struct Table {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
// pub app_browser_id: i64, not in use
|
// pub app_browser_id: i64, not in use
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
pub fn init(tx: &Transaction) -> Result<usize> {
|
||||||
tx.execute(
|
Ok(tx.execute(
|
||||||
"CREATE TABLE IF NOT EXISTS `app_browser_window`
|
"CREATE TABLE IF NOT EXISTS `app_browser_window`
|
||||||
(
|
(
|
||||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
|
@ -15,17 +16,18 @@ pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
||||||
FOREIGN KEY (`app_browser_id`) REFERENCES `app_browser`(`id`)
|
FOREIGN KEY (`app_browser_id`) REFERENCES `app_browser`(`id`)
|
||||||
)",
|
)",
|
||||||
[],
|
[],
|
||||||
)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(tx: &Transaction, app_browser_id: i64) -> Result<usize, Error> {
|
pub fn insert(tx: &Transaction, app_browser_id: i64) -> Result<i64> {
|
||||||
tx.execute(
|
tx.execute(
|
||||||
"INSERT INTO `app_browser_window` (`app_browser_id`) VALUES (?)",
|
"INSERT INTO `app_browser_window` (`app_browser_id`) VALUES (?)",
|
||||||
[app_browser_id],
|
[app_browser_id],
|
||||||
)
|
)?;
|
||||||
|
Ok(tx.last_insert_rowid())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select(tx: &Transaction, app_browser_id: i64) -> Result<Vec<Table>, Error> {
|
pub fn select(tx: &Transaction, app_browser_id: i64) -> Result<Vec<Table>> {
|
||||||
let mut stmt = tx.prepare(
|
let mut stmt = tx.prepare(
|
||||||
"SELECT `id`,
|
"SELECT `id`,
|
||||||
`app_browser_id` FROM `app_browser_window`
|
`app_browser_id` FROM `app_browser_window`
|
||||||
|
|
@ -49,10 +51,6 @@ pub fn select(tx: &Transaction, app_browser_id: i64) -> Result<Vec<Table>, Error
|
||||||
Ok(records)
|
Ok(records)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(tx: &Transaction, id: i64) -> Result<usize, Error> {
|
pub fn delete(tx: &Transaction, id: i64) -> Result<usize> {
|
||||||
tx.execute("DELETE FROM `app_browser_window` WHERE `id` = ?", [id])
|
Ok(tx.execute("DELETE FROM `app_browser_window` WHERE `id` = ?", [id])?)
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_insert_id(tx: &Transaction) -> i64 {
|
|
||||||
tx.last_insert_rowid()
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use super::{Action as WindowAction, BrowserAction, Position};
|
||||||
use crate::Profile;
|
use crate::Profile;
|
||||||
use action::Action;
|
use action::Action;
|
||||||
use adw::{TabPage, TabView};
|
use adw::{TabPage, TabView};
|
||||||
|
use anyhow::Result;
|
||||||
use error::Error;
|
use error::Error;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
gio::Icon,
|
gio::Icon,
|
||||||
|
|
@ -309,44 +310,23 @@ impl Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clean(
|
pub fn clean(&self, transaction: &Transaction, app_browser_window_id: i64) -> Result<()> {
|
||||||
&self,
|
for record in database::select(transaction, app_browser_window_id)? {
|
||||||
transaction: &Transaction,
|
database::delete(transaction, record.id)?;
|
||||||
app_browser_window_id: i64,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
match database::select(transaction, app_browser_window_id) {
|
|
||||||
Ok(records) => {
|
|
||||||
for record in records {
|
|
||||||
match database::delete(transaction, record.id) {
|
|
||||||
Ok(_) => {
|
|
||||||
// Delegate clean action to childs
|
// Delegate clean action to childs
|
||||||
for (_, item) in self.index.borrow().iter() {
|
for (_, item) in self.index.borrow().iter() {
|
||||||
item.clean(transaction, record.id)?
|
item.clean(transaction, record.id)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn restore(
|
pub fn restore(&self, transaction: &Transaction, app_browser_window_id: i64) -> Result<()> {
|
||||||
&self,
|
for tab_record in database::select(transaction, app_browser_window_id)? {
|
||||||
transaction: &Transaction,
|
|
||||||
app_browser_window_id: i64,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
match database::select(transaction, app_browser_window_id) {
|
|
||||||
Ok(tab_records) => {
|
|
||||||
for tab_record in tab_records {
|
|
||||||
for item_record in item::restore(transaction, tab_record.id)? {
|
for item_record in item::restore(transaction, tab_record.id)? {
|
||||||
// Generate new `TabPage` with blank `Widget`
|
// Generate new `TabPage` with blank `Widget`
|
||||||
let (tab_page, target_child) = new_tab_page(
|
let (tab_page, target_child) =
|
||||||
&self.tab_view,
|
new_tab_page(&self.tab_view, Position::Number(item_record.page_position));
|
||||||
Position::Number(item_record.page_position),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Init new tab item
|
// Init new tab item
|
||||||
let item = Rc::new(Item::build(
|
let item = Rc::new(Item::build(
|
||||||
|
|
@ -385,27 +365,14 @@ impl Tab {
|
||||||
item.page.restore(transaction, item_record.id)?;
|
item.page.restore(transaction, item_record.id)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save(
|
pub fn save(&self, transaction: &Transaction, app_browser_window_id: i64) -> Result<()> {
|
||||||
&self,
|
let id = database::insert(transaction, app_browser_window_id)?;
|
||||||
transaction: &Transaction,
|
|
||||||
app_browser_window_id: i64,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
match database::insert(transaction, app_browser_window_id) {
|
|
||||||
Ok(_) => {
|
|
||||||
// Delegate save action to childs
|
|
||||||
let id = database::last_insert_id(transaction);
|
|
||||||
for (_, item) in self.index.borrow().iter() {
|
for (_, item) in self.index.borrow().iter() {
|
||||||
item.save(transaction, id, self.tab_view.page_position(&item.tab_page))?;
|
item.save(transaction, id, self.tab_view.page_position(&item.tab_page))?;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -430,11 +397,9 @@ impl Tab {
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
|
|
||||||
pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
pub fn migrate(tx: &Transaction) -> Result<()> {
|
||||||
// Migrate self components
|
// Migrate self components
|
||||||
if let Err(e) = database::init(tx) {
|
database::init(tx)?;
|
||||||
return Err(e.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate migration to childs
|
// Delegate migration to childs
|
||||||
item::migrate(tx)?;
|
item::migrate(tx)?;
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
use sqlite::{Error, Transaction};
|
use anyhow::Result;
|
||||||
|
use sqlite::Transaction;
|
||||||
|
|
||||||
pub struct Table {
|
pub struct Table {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
// pub app_browser_window_id: i64, not in use
|
// pub app_browser_window_id: i64, not in use
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
pub fn init(tx: &Transaction) -> Result<usize> {
|
||||||
tx.execute(
|
Ok(tx.execute(
|
||||||
"CREATE TABLE IF NOT EXISTS `app_browser_window_tab`
|
"CREATE TABLE IF NOT EXISTS `app_browser_window_tab`
|
||||||
(
|
(
|
||||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
|
@ -15,19 +16,20 @@ pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
||||||
FOREIGN KEY (`app_browser_window_id`) REFERENCES `app_browser_window`(`id`)
|
FOREIGN KEY (`app_browser_window_id`) REFERENCES `app_browser_window`(`id`)
|
||||||
)",
|
)",
|
||||||
[],
|
[],
|
||||||
)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(tx: &Transaction, app_browser_window_id: i64) -> Result<usize, Error> {
|
pub fn insert(tx: &Transaction, app_browser_window_id: i64) -> Result<i64> {
|
||||||
tx.execute(
|
tx.execute(
|
||||||
"INSERT INTO `app_browser_window_tab` (
|
"INSERT INTO `app_browser_window_tab` (
|
||||||
`app_browser_window_id`
|
`app_browser_window_id`
|
||||||
) VALUES (?)",
|
) VALUES (?)",
|
||||||
[app_browser_window_id],
|
[app_browser_window_id],
|
||||||
)
|
)?;
|
||||||
|
Ok(tx.last_insert_rowid())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select(tx: &Transaction, app_browser_window_id: i64) -> Result<Vec<Table>, Error> {
|
pub fn select(tx: &Transaction, app_browser_window_id: i64) -> Result<Vec<Table>> {
|
||||||
let mut stmt = tx.prepare(
|
let mut stmt = tx.prepare(
|
||||||
"SELECT `id`,
|
"SELECT `id`,
|
||||||
`app_browser_window_id` FROM `app_browser_window_tab`
|
`app_browser_window_id` FROM `app_browser_window_tab`
|
||||||
|
|
@ -51,10 +53,6 @@ pub fn select(tx: &Transaction, app_browser_window_id: i64) -> Result<Vec<Table>
|
||||||
Ok(records)
|
Ok(records)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(tx: &Transaction, id: i64) -> Result<usize, Error> {
|
pub fn delete(tx: &Transaction, id: i64) -> Result<usize> {
|
||||||
tx.execute("DELETE FROM `app_browser_window_tab` WHERE `id` = ?", [id])
|
Ok(tx.execute("DELETE FROM `app_browser_window_tab` WHERE `id` = ?", [id])?)
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_insert_id(tx: &Transaction) -> i64 {
|
|
||||||
tx.last_insert_rowid()
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use super::{Action as TabAction, BrowserAction, WindowAction};
|
||||||
use crate::Profile;
|
use crate::Profile;
|
||||||
use action::Action;
|
use action::Action;
|
||||||
use adw::TabPage;
|
use adw::TabPage;
|
||||||
|
use anyhow::Result;
|
||||||
use client::Client;
|
use client::Client;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
prelude::{ActionExt, ActionMapExt, BoxExt},
|
prelude::{ActionExt, ActionMapExt, BoxExt},
|
||||||
|
|
@ -164,26 +165,12 @@ impl Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clean(
|
pub fn clean(&self, transaction: &Transaction, app_browser_window_tab_id: i64) -> Result<()> {
|
||||||
&self,
|
for record in database::select(transaction, app_browser_window_tab_id)? {
|
||||||
transaction: &Transaction,
|
database::delete(transaction, record.id)?;
|
||||||
app_browser_window_tab_id: i64,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
match database::select(transaction, app_browser_window_tab_id) {
|
|
||||||
Ok(records) => {
|
|
||||||
for record in records {
|
|
||||||
match database::delete(transaction, record.id) {
|
|
||||||
Ok(_) => {
|
|
||||||
// Delegate clean action to the item childs
|
// Delegate clean action to the item childs
|
||||||
self.page.clean(transaction, record.id)?;
|
self.page.clean(transaction, record.id)?;
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,32 +179,23 @@ impl Item {
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
app_browser_window_tab_id: i64,
|
app_browser_window_tab_id: i64,
|
||||||
page_position: i32,
|
page_position: i32,
|
||||||
) -> Result<(), String> {
|
) -> Result<()> {
|
||||||
match database::insert(
|
let id = database::insert(
|
||||||
transaction,
|
transaction,
|
||||||
app_browser_window_tab_id,
|
app_browser_window_tab_id,
|
||||||
page_position,
|
page_position,
|
||||||
self.tab_page.is_pinned(),
|
self.tab_page.is_pinned(),
|
||||||
self.tab_page.is_selected(),
|
self.tab_page.is_selected(),
|
||||||
) {
|
)?;
|
||||||
Ok(_) => {
|
|
||||||
// Delegate save action to childs
|
|
||||||
let id = database::last_insert_id(transaction);
|
|
||||||
self.page.save(transaction, id)?;
|
self.page.save(transaction, id)?;
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
pub fn migrate(tx: &Transaction) -> Result<()> {
|
||||||
// Migrate self components
|
// Migrate self components
|
||||||
if let Err(e) = database::init(tx) {
|
database::init(tx)?;
|
||||||
return Err(e.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate migration to childs
|
// Delegate migration to childs
|
||||||
page::migrate(tx)?;
|
page::migrate(tx)?;
|
||||||
|
|
@ -231,9 +209,6 @@ pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
||||||
pub fn restore(
|
pub fn restore(
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
app_browser_window_tab_id: i64,
|
app_browser_window_tab_id: i64,
|
||||||
) -> Result<Vec<database::Table>, String> {
|
) -> Result<Vec<database::Table>> {
|
||||||
match database::select(transaction, app_browser_window_tab_id) {
|
database::select(transaction, app_browser_window_tab_id)
|
||||||
Ok(records) => Ok(records),
|
|
||||||
Err(e) => Err(e.to_string()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use sqlite::{Error, Transaction};
|
use anyhow::Result;
|
||||||
|
use sqlite::Transaction;
|
||||||
|
|
||||||
pub struct Table {
|
pub struct Table {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
|
|
@ -8,8 +9,8 @@ pub struct Table {
|
||||||
pub is_selected: bool,
|
pub is_selected: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
pub fn init(tx: &Transaction) -> Result<usize> {
|
||||||
tx.execute(
|
Ok(tx.execute(
|
||||||
"CREATE TABLE IF NOT EXISTS `app_browser_window_tab_item`
|
"CREATE TABLE IF NOT EXISTS `app_browser_window_tab_item`
|
||||||
(
|
(
|
||||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
|
@ -21,7 +22,7 @@ pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
||||||
FOREIGN KEY (`app_browser_window_tab_id`) REFERENCES `app_browser_window_tab` (`id`)
|
FOREIGN KEY (`app_browser_window_tab_id`) REFERENCES `app_browser_window_tab` (`id`)
|
||||||
)",
|
)",
|
||||||
[],
|
[],
|
||||||
)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(
|
pub fn insert(
|
||||||
|
|
@ -30,7 +31,7 @@ pub fn insert(
|
||||||
page_position: i32,
|
page_position: i32,
|
||||||
is_pinned: bool,
|
is_pinned: bool,
|
||||||
is_selected: bool,
|
is_selected: bool,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<i64> {
|
||||||
tx.execute(
|
tx.execute(
|
||||||
"INSERT INTO `app_browser_window_tab_item` (
|
"INSERT INTO `app_browser_window_tab_item` (
|
||||||
`app_browser_window_tab_id`,
|
`app_browser_window_tab_id`,
|
||||||
|
|
@ -44,10 +45,11 @@ pub fn insert(
|
||||||
is_pinned as i64,
|
is_pinned as i64,
|
||||||
is_selected as i64,
|
is_selected as i64,
|
||||||
],
|
],
|
||||||
)
|
)?;
|
||||||
|
Ok(tx.last_insert_rowid())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select(tx: &Transaction, app_browser_window_tab_id: i64) -> Result<Vec<Table>, Error> {
|
pub fn select(tx: &Transaction, app_browser_window_tab_id: i64) -> Result<Vec<Table>> {
|
||||||
let mut stmt = tx.prepare(
|
let mut stmt = tx.prepare(
|
||||||
"SELECT `id`,
|
"SELECT `id`,
|
||||||
`app_browser_window_tab_id`,
|
`app_browser_window_tab_id`,
|
||||||
|
|
@ -79,13 +81,9 @@ pub fn select(tx: &Transaction, app_browser_window_tab_id: i64) -> Result<Vec<Ta
|
||||||
Ok(records)
|
Ok(records)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(tx: &Transaction, id: i64) -> Result<usize, Error> {
|
pub fn delete(tx: &Transaction, id: i64) -> Result<usize> {
|
||||||
tx.execute(
|
Ok(tx.execute(
|
||||||
"DELETE FROM `app_browser_window_tab_item` WHERE `id` = ?",
|
"DELETE FROM `app_browser_window_tab_item` WHERE `id` = ?",
|
||||||
[id],
|
[id],
|
||||||
)
|
)?)
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_insert_id(tx: &Transaction) -> i64 {
|
|
||||||
tx.last_insert_rowid()
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ mod search;
|
||||||
|
|
||||||
use super::{Action as ItemAction, BrowserAction, Profile, TabAction, WindowAction};
|
use super::{Action as ItemAction, BrowserAction, Profile, TabAction, WindowAction};
|
||||||
use adw::TabPage;
|
use adw::TabPage;
|
||||||
|
use anyhow::Result;
|
||||||
use content::Content;
|
use content::Content;
|
||||||
use error::Error;
|
use error::Error;
|
||||||
use input::Input;
|
use input::Input;
|
||||||
|
|
@ -104,21 +105,12 @@ impl Page {
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
app_browser_window_tab_item_id: i64,
|
app_browser_window_tab_item_id: i64,
|
||||||
) -> Result<(), String> {
|
) -> Result<()> {
|
||||||
match database::select(transaction, app_browser_window_tab_item_id) {
|
for record in database::select(transaction, app_browser_window_tab_item_id)? {
|
||||||
Ok(records) => {
|
database::delete(transaction, record.id)?;
|
||||||
for record in records {
|
|
||||||
match database::delete(transaction, record.id) {
|
|
||||||
Ok(_) => {
|
|
||||||
// Delegate clean action to the item childs
|
// Delegate clean action to the item childs
|
||||||
self.navigation.clean(transaction, &record.id)?;
|
self.navigation.clean(transaction, &record.id)?;
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,11 +119,9 @@ impl Page {
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
app_browser_window_tab_item_id: i64,
|
app_browser_window_tab_item_id: i64,
|
||||||
) -> Result<(), String> {
|
) -> Result<()> {
|
||||||
// Begin page restore
|
// Begin page restore
|
||||||
match database::select(transaction, app_browser_window_tab_item_id) {
|
for record in database::select(transaction, app_browser_window_tab_item_id)? {
|
||||||
Ok(records) => {
|
|
||||||
for record in records {
|
|
||||||
// Restore `Self`
|
// Restore `Self`
|
||||||
if let Some(title) = record.title {
|
if let Some(title) = record.title {
|
||||||
self.set_title(title.as_str());
|
self.set_title(title.as_str());
|
||||||
|
|
@ -144,9 +134,6 @@ impl Page {
|
||||||
self.profile.history.memory.request.set(uri);
|
self.profile.history.memory.request.set(uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,10 +142,10 @@ impl Page {
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
app_browser_window_tab_item_id: i64,
|
app_browser_window_tab_item_id: i64,
|
||||||
) -> Result<(), String> {
|
) -> Result<()> {
|
||||||
// Keep value in memory until operation complete
|
// Keep value in memory until operation complete
|
||||||
let title = self.tab_page.title();
|
let title = self.tab_page.title();
|
||||||
match database::insert(
|
let id = database::insert(
|
||||||
transaction,
|
transaction,
|
||||||
app_browser_window_tab_item_id,
|
app_browser_window_tab_item_id,
|
||||||
self.tab_page.needs_attention(),
|
self.tab_page.needs_attention(),
|
||||||
|
|
@ -166,15 +153,9 @@ impl Page {
|
||||||
true => None,
|
true => None,
|
||||||
false => Some(title.as_str()),
|
false => Some(title.as_str()),
|
||||||
},
|
},
|
||||||
) {
|
)?;
|
||||||
Ok(_) => {
|
|
||||||
let id = database::last_insert_id(transaction);
|
|
||||||
|
|
||||||
// Delegate save action to childs
|
// Delegate save action to childs
|
||||||
self.navigation.save(transaction, &id)?;
|
self.navigation.save(transaction, &id)?;
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,11 +179,9 @@ impl Page {
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
|
|
||||||
pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
pub fn migrate(tx: &Transaction) -> Result<()> {
|
||||||
// Migrate self components
|
// Migrate self components
|
||||||
if let Err(e) = database::init(tx) {
|
database::init(tx)?;
|
||||||
return Err(e.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate migration to childs
|
// Delegate migration to childs
|
||||||
navigation::migrate(tx)?;
|
navigation::migrate(tx)?;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use sqlite::{Error, Transaction};
|
use anyhow::Result;
|
||||||
|
use sqlite::Transaction;
|
||||||
|
|
||||||
pub struct Table {
|
pub struct Table {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
|
|
@ -7,8 +8,8 @@ pub struct Table {
|
||||||
pub title: Option<String>,
|
pub title: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
pub fn init(tx: &Transaction) -> Result<usize> {
|
||||||
tx.execute(
|
Ok(tx.execute(
|
||||||
"CREATE TABLE IF NOT EXISTS `app_browser_window_tab_item_page`
|
"CREATE TABLE IF NOT EXISTS `app_browser_window_tab_item_page`
|
||||||
(
|
(
|
||||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
|
@ -19,7 +20,7 @@ pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
||||||
FOREIGN KEY (`app_browser_window_tab_item_id`) REFERENCES `app_browser_window_tab_item` (`id`)
|
FOREIGN KEY (`app_browser_window_tab_item_id`) REFERENCES `app_browser_window_tab_item` (`id`)
|
||||||
)",
|
)",
|
||||||
[],
|
[],
|
||||||
)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(
|
pub fn insert(
|
||||||
|
|
@ -27,7 +28,7 @@ pub fn insert(
|
||||||
app_browser_window_tab_item_id: i64,
|
app_browser_window_tab_item_id: i64,
|
||||||
is_needs_attention: bool,
|
is_needs_attention: bool,
|
||||||
title: Option<&str>,
|
title: Option<&str>,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<i64> {
|
||||||
tx.execute(
|
tx.execute(
|
||||||
"INSERT INTO `app_browser_window_tab_item_page` (
|
"INSERT INTO `app_browser_window_tab_item_page` (
|
||||||
`app_browser_window_tab_item_id`,
|
`app_browser_window_tab_item_id`,
|
||||||
|
|
@ -35,10 +36,11 @@ pub fn insert(
|
||||||
`title`
|
`title`
|
||||||
) VALUES (?, ?, ?)",
|
) VALUES (?, ?, ?)",
|
||||||
(app_browser_window_tab_item_id, is_needs_attention, title),
|
(app_browser_window_tab_item_id, is_needs_attention, title),
|
||||||
)
|
)?;
|
||||||
|
Ok(tx.last_insert_rowid())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select(tx: &Transaction, app_browser_window_tab_item_id: i64) -> Result<Vec<Table>, Error> {
|
pub fn select(tx: &Transaction, app_browser_window_tab_item_id: i64) -> Result<Vec<Table>> {
|
||||||
let mut stmt = tx.prepare(
|
let mut stmt = tx.prepare(
|
||||||
"SELECT `id`,
|
"SELECT `id`,
|
||||||
`app_browser_window_tab_item_id`,
|
`app_browser_window_tab_item_id`,
|
||||||
|
|
@ -67,13 +69,9 @@ pub fn select(tx: &Transaction, app_browser_window_tab_item_id: i64) -> Result<V
|
||||||
Ok(records)
|
Ok(records)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(tx: &Transaction, id: i64) -> Result<usize, Error> {
|
pub fn delete(tx: &Transaction, id: i64) -> Result<usize> {
|
||||||
tx.execute(
|
Ok(tx.execute(
|
||||||
"DELETE FROM `app_browser_window_tab_item_page` WHERE `id` = ?",
|
"DELETE FROM `app_browser_window_tab_item_page` WHERE `id` = ?",
|
||||||
[id],
|
[id],
|
||||||
)
|
)?)
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_insert_id(tx: &Transaction) -> i64 {
|
|
||||||
tx.last_insert_rowid()
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ mod reload;
|
||||||
mod request;
|
mod request;
|
||||||
|
|
||||||
use super::{ItemAction, Profile, TabAction, WindowAction};
|
use super::{ItemAction, Profile, TabAction, WindowAction};
|
||||||
|
use anyhow::Result;
|
||||||
use bookmark::Bookmark;
|
use bookmark::Bookmark;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
glib::{GString, Uri},
|
glib::{GString, Uri},
|
||||||
|
|
@ -72,22 +73,12 @@ impl Navigation {
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
app_browser_window_tab_item_page_id: &i64,
|
app_browser_window_tab_item_page_id: &i64,
|
||||||
) -> Result<(), String> {
|
) -> Result<()> {
|
||||||
match database::select(transaction, app_browser_window_tab_item_page_id) {
|
for record in database::select(transaction, app_browser_window_tab_item_page_id)? {
|
||||||
Ok(records) => {
|
database::delete(transaction, &record.id)?;
|
||||||
for record in records {
|
|
||||||
match database::delete(transaction, &record.id) {
|
|
||||||
Ok(_) => {
|
|
||||||
// Delegate clean action to the item childs
|
// Delegate clean action to the item childs
|
||||||
self.request.clean(transaction, &record.id)?;
|
self.request.clean(transaction, &record.id)?;
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,17 +86,11 @@ impl Navigation {
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
app_browser_window_tab_item_page_id: &i64,
|
app_browser_window_tab_item_page_id: &i64,
|
||||||
) -> Result<(), String> {
|
) -> Result<()> {
|
||||||
match database::select(transaction, app_browser_window_tab_item_page_id) {
|
for record in database::select(transaction, app_browser_window_tab_item_page_id)? {
|
||||||
Ok(records) => {
|
|
||||||
for record in records {
|
|
||||||
// Delegate restore action to the item childs
|
// Delegate restore action to the item childs
|
||||||
self.request.restore(transaction, &record.id)?;
|
self.request.restore(transaction, &record.id)?;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,17 +98,10 @@ impl Navigation {
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
app_browser_window_tab_item_page_id: &i64,
|
app_browser_window_tab_item_page_id: &i64,
|
||||||
) -> Result<(), String> {
|
) -> Result<()> {
|
||||||
match database::insert(transaction, app_browser_window_tab_item_page_id) {
|
let id = database::insert(transaction, app_browser_window_tab_item_page_id)?;
|
||||||
Ok(_) => {
|
|
||||||
let id = database::last_insert_id(transaction);
|
|
||||||
|
|
||||||
// Delegate save action to childs
|
// Delegate save action to childs
|
||||||
self.request.save(transaction, &id)?;
|
self.request.save(transaction, &id)?;
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,11 +151,9 @@ impl Navigation {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
pub fn migrate(tx: &Transaction) -> Result<()> {
|
||||||
// Migrate self components
|
// Migrate self components
|
||||||
if let Err(e) = database::init(tx) {
|
database::init(tx)?;
|
||||||
return Err(e.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate migration to childs
|
// Delegate migration to childs
|
||||||
request::migrate(tx)?;
|
request::migrate(tx)?;
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ impl Bookmark for Button {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&self, profile: &Profile, request: &Entry) {
|
fn update(&self, profile: &Profile, request: &Entry) {
|
||||||
let has_bookmark = profile.bookmark.get(&request.text()).is_ok();
|
let has_bookmark = profile.bookmark.get(&request.text()).is_some();
|
||||||
self.set_icon_name(icon_name(has_bookmark));
|
self.set_icon_name(icon_name(has_bookmark));
|
||||||
self.set_tooltip_text(Some(tooltip_text(has_bookmark)));
|
self.set_tooltip_text(Some(tooltip_text(has_bookmark)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
use sqlite::{Error, Transaction};
|
use anyhow::Result;
|
||||||
|
use sqlite::Transaction;
|
||||||
|
|
||||||
pub struct Table {
|
pub struct Table {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
// pub app_browser_window_tab_item_page_id: i64, not in use
|
// pub app_browser_window_tab_item_page_id: i64, not in use
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
pub fn init(tx: &Transaction) -> Result<usize> {
|
||||||
tx.execute(
|
Ok(tx.execute(
|
||||||
"CREATE TABLE IF NOT EXISTS `app_browser_window_tab_item_page_navigation`
|
"CREATE TABLE IF NOT EXISTS `app_browser_window_tab_item_page_navigation`
|
||||||
(
|
(
|
||||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
|
@ -15,22 +16,20 @@ pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
||||||
FOREIGN KEY (`app_browser_window_tab_item_page_id`) REFERENCES `app_browser_window_tab_item_page`(`id`)
|
FOREIGN KEY (`app_browser_window_tab_item_page_id`) REFERENCES `app_browser_window_tab_item_page`(`id`)
|
||||||
)",
|
)",
|
||||||
[],
|
[],
|
||||||
)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(tx: &Transaction, app_browser_window_tab_item_page_id: &i64) -> Result<usize, Error> {
|
pub fn insert(tx: &Transaction, app_browser_window_tab_item_page_id: &i64) -> Result<i64> {
|
||||||
tx.execute(
|
tx.execute(
|
||||||
"INSERT INTO `app_browser_window_tab_item_page_navigation` (
|
"INSERT INTO `app_browser_window_tab_item_page_navigation` (
|
||||||
`app_browser_window_tab_item_page_id`
|
`app_browser_window_tab_item_page_id`
|
||||||
) VALUES (?)",
|
) VALUES (?)",
|
||||||
[app_browser_window_tab_item_page_id],
|
[app_browser_window_tab_item_page_id],
|
||||||
)
|
)?;
|
||||||
|
Ok(tx.last_insert_rowid())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select(
|
pub fn select(tx: &Transaction, app_browser_window_tab_item_page_id: &i64) -> Result<Vec<Table>> {
|
||||||
tx: &Transaction,
|
|
||||||
app_browser_window_tab_item_page_id: &i64,
|
|
||||||
) -> Result<Vec<Table>, Error> {
|
|
||||||
let mut stmt = tx.prepare(
|
let mut stmt = tx.prepare(
|
||||||
"SELECT `id`,
|
"SELECT `id`,
|
||||||
`app_browser_window_tab_item_page_id`
|
`app_browser_window_tab_item_page_id`
|
||||||
|
|
@ -55,13 +54,9 @@ pub fn select(
|
||||||
Ok(records)
|
Ok(records)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(tx: &Transaction, id: &i64) -> Result<usize, Error> {
|
pub fn delete(tx: &Transaction, id: &i64) -> Result<usize> {
|
||||||
tx.execute(
|
Ok(tx.execute(
|
||||||
"DELETE FROM `app_browser_window_tab_item_page_navigation` WHERE `id` = ?",
|
"DELETE FROM `app_browser_window_tab_item_page_navigation` WHERE `id` = ?",
|
||||||
[id],
|
[id],
|
||||||
)
|
)?)
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_insert_id(tx: &Transaction) -> i64 {
|
|
||||||
tx.last_insert_rowid()
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,15 @@ mod identity;
|
||||||
mod primary_icon;
|
mod primary_icon;
|
||||||
mod search;
|
mod search;
|
||||||
|
|
||||||
use adw::{prelude::AdwDialogExt, AlertDialog};
|
|
||||||
use primary_icon::PrimaryIcon;
|
|
||||||
|
|
||||||
use super::{ItemAction, Profile};
|
use super::{ItemAction, Profile};
|
||||||
|
use adw::{prelude::AdwDialogExt, AlertDialog};
|
||||||
|
use anyhow::Result;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
glib::{gformat, GString, Uri, UriFlags},
|
glib::{gformat, GString, Uri, UriFlags},
|
||||||
prelude::{EditableExt, EntryExt, WidgetExt},
|
prelude::{EditableExt, EntryExt, WidgetExt},
|
||||||
Entry, EntryIconPosition, StateFlags,
|
Entry, EntryIconPosition, StateFlags,
|
||||||
};
|
};
|
||||||
|
use primary_icon::PrimaryIcon;
|
||||||
use sqlite::Transaction;
|
use sqlite::Transaction;
|
||||||
use std::{cell::Cell, rc::Rc};
|
use std::{cell::Cell, rc::Rc};
|
||||||
|
|
||||||
|
|
@ -29,19 +29,19 @@ pub trait Request {
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
app_browser_window_tab_item_page_navigation_id: &i64,
|
app_browser_window_tab_item_page_navigation_id: &i64,
|
||||||
) -> Result<(), String>;
|
) -> Result<()>;
|
||||||
|
|
||||||
fn restore(
|
fn restore(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
app_browser_window_tab_item_page_navigation_id: &i64,
|
app_browser_window_tab_item_page_navigation_id: &i64,
|
||||||
) -> Result<(), String>;
|
) -> Result<()>;
|
||||||
|
|
||||||
fn save(
|
fn save(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
app_browser_window_tab_item_page_navigation_id: &i64,
|
app_browser_window_tab_item_page_navigation_id: &i64,
|
||||||
) -> Result<(), String>;
|
) -> Result<()>;
|
||||||
|
|
||||||
fn update_primary_icon(&self, profile: &Profile);
|
fn update_primary_icon(&self, profile: &Profile);
|
||||||
fn update_secondary_icon(&self);
|
fn update_secondary_icon(&self);
|
||||||
|
|
@ -147,22 +147,13 @@ impl Request for Entry {
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
app_browser_window_tab_item_page_navigation_id: &i64,
|
app_browser_window_tab_item_page_navigation_id: &i64,
|
||||||
) -> Result<(), String> {
|
) -> Result<()> {
|
||||||
match database::select(transaction, app_browser_window_tab_item_page_navigation_id) {
|
for record in database::select(transaction, app_browser_window_tab_item_page_navigation_id)?
|
||||||
Ok(records) => {
|
{
|
||||||
for record in records {
|
database::delete(transaction, &record.id)?;
|
||||||
match database::delete(transaction, &record.id) {
|
|
||||||
Ok(_) => {
|
|
||||||
// Delegate clean action to the item childs
|
// Delegate clean action to the item childs
|
||||||
// nothing yet..
|
// nothing yet..
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,21 +161,15 @@ impl Request for Entry {
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
app_browser_window_tab_item_page_navigation_id: &i64,
|
app_browser_window_tab_item_page_navigation_id: &i64,
|
||||||
) -> Result<(), String> {
|
) -> Result<()> {
|
||||||
match database::select(transaction, app_browser_window_tab_item_page_navigation_id) {
|
for record in database::select(transaction, app_browser_window_tab_item_page_navigation_id)?
|
||||||
Ok(records) => {
|
{
|
||||||
for record in records {
|
|
||||||
if let Some(text) = record.text {
|
if let Some(text) = record.text {
|
||||||
self.set_text(&text);
|
self.set_text(&text);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delegate restore action to the item childs
|
// Delegate restore action to the item childs
|
||||||
// nothing yet..
|
// nothing yet..
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,27 +177,19 @@ impl Request for Entry {
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
app_browser_window_tab_item_page_navigation_id: &i64,
|
app_browser_window_tab_item_page_navigation_id: &i64,
|
||||||
) -> Result<(), String> {
|
) -> Result<()> {
|
||||||
// Keep value in memory until operation complete
|
// Keep value in memory until operation complete
|
||||||
let text = self.text();
|
let text = self.text();
|
||||||
|
let _id = database::insert(
|
||||||
match database::insert(
|
|
||||||
transaction,
|
transaction,
|
||||||
app_browser_window_tab_item_page_navigation_id,
|
app_browser_window_tab_item_page_navigation_id,
|
||||||
match text.is_empty() {
|
match text.is_empty() {
|
||||||
true => None,
|
true => None,
|
||||||
false => Some(text.as_str()),
|
false => Some(text.as_str()),
|
||||||
},
|
},
|
||||||
) {
|
)?;
|
||||||
Ok(_) => {
|
|
||||||
// let id = database::last_insert_id(transaction);
|
|
||||||
|
|
||||||
// Delegate save action to childs
|
// Delegate save action to childs
|
||||||
// nothing yet..
|
// nothing yet..
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -369,11 +346,9 @@ impl Request for Entry {
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
|
|
||||||
pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
pub fn migrate(tx: &Transaction) -> Result<()> {
|
||||||
// Migrate self components
|
// Migrate self components
|
||||||
if let Err(e) = database::init(tx) {
|
database::init(tx)?;
|
||||||
return Err(e.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate migration to childs
|
// Delegate migration to childs
|
||||||
// nothing yet..
|
// nothing yet..
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use sqlite::{Error, Transaction};
|
use anyhow::Result;
|
||||||
|
use sqlite::Transaction;
|
||||||
|
|
||||||
pub struct Table {
|
pub struct Table {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
|
|
@ -6,8 +7,8 @@ pub struct Table {
|
||||||
pub text: Option<String>, // can be stored as NULL
|
pub text: Option<String>, // can be stored as NULL
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
pub fn init(tx: &Transaction) -> Result<usize> {
|
||||||
tx.execute(
|
Ok( tx.execute(
|
||||||
"CREATE TABLE IF NOT EXISTS `app_browser_window_tab_item_page_navigation_request`
|
"CREATE TABLE IF NOT EXISTS `app_browser_window_tab_item_page_navigation_request`
|
||||||
(
|
(
|
||||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
|
@ -17,27 +18,28 @@ pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
||||||
FOREIGN KEY (`app_browser_window_tab_item_page_navigation_id`) REFERENCES `app_browser_window_tab_item_page_navigation`(`id`)
|
FOREIGN KEY (`app_browser_window_tab_item_page_navigation_id`) REFERENCES `app_browser_window_tab_item_page_navigation`(`id`)
|
||||||
)",
|
)",
|
||||||
[],
|
[],
|
||||||
)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(
|
pub fn insert(
|
||||||
tx: &Transaction,
|
tx: &Transaction,
|
||||||
app_browser_window_tab_item_page_navigation_id: &i64,
|
app_browser_window_tab_item_page_navigation_id: &i64,
|
||||||
text: Option<&str>,
|
text: Option<&str>,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<i64> {
|
||||||
tx.execute(
|
tx.execute(
|
||||||
"INSERT INTO `app_browser_window_tab_item_page_navigation_request` (
|
"INSERT INTO `app_browser_window_tab_item_page_navigation_request` (
|
||||||
`app_browser_window_tab_item_page_navigation_id`,
|
`app_browser_window_tab_item_page_navigation_id`,
|
||||||
`text`
|
`text`
|
||||||
) VALUES (?, ?)",
|
) VALUES (?, ?)",
|
||||||
(app_browser_window_tab_item_page_navigation_id, text),
|
(app_browser_window_tab_item_page_navigation_id, text),
|
||||||
)
|
)?;
|
||||||
|
Ok(tx.last_insert_rowid())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select(
|
pub fn select(
|
||||||
tx: &Transaction,
|
tx: &Transaction,
|
||||||
app_browser_window_tab_item_page_navigation_id: &i64,
|
app_browser_window_tab_item_page_navigation_id: &i64,
|
||||||
) -> Result<Vec<Table>, Error> {
|
) -> Result<Vec<Table>> {
|
||||||
let mut stmt = tx.prepare(
|
let mut stmt = tx.prepare(
|
||||||
"SELECT `id`,
|
"SELECT `id`,
|
||||||
`app_browser_window_tab_item_page_navigation_id`,
|
`app_browser_window_tab_item_page_navigation_id`,
|
||||||
|
|
@ -64,14 +66,9 @@ pub fn select(
|
||||||
Ok(records)
|
Ok(records)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(tx: &Transaction, id: &i64) -> Result<usize, Error> {
|
pub fn delete(tx: &Transaction, id: &i64) -> Result<usize> {
|
||||||
tx.execute(
|
Ok(tx.execute(
|
||||||
"DELETE FROM `app_browser_window_tab_item_page_navigation_request` WHERE `id` = ?",
|
"DELETE FROM `app_browser_window_tab_item_page_navigation_request` WHERE `id` = ?",
|
||||||
[id],
|
[id],
|
||||||
)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* not in use
|
|
||||||
pub fn last_insert_id(tx: &Transaction) -> i64 {
|
|
||||||
tx.last_insert_rowid()
|
|
||||||
} */
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use std::rc::Rc;
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
match gtk::init() {
|
match gtk::init() {
|
||||||
Ok(_) => App::build(&Rc::new(Profile::new())).run(),
|
Ok(_) => App::build(&Rc::new(Profile::new().unwrap())).run(),
|
||||||
Err(_) => ExitCode::FAILURE,
|
Err(_) => ExitCode::FAILURE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ use history::History;
|
||||||
use identity::Identity;
|
use identity::Identity;
|
||||||
use search::Search;
|
use search::Search;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
use gtk::glib::{user_config_dir, DateTime};
|
use gtk::glib::{user_config_dir, DateTime};
|
||||||
use sqlite::{Connection, Transaction};
|
use sqlite::{Connection, Transaction};
|
||||||
use std::{fs::create_dir_all, path::PathBuf, rc::Rc, sync::RwLock};
|
use std::{fs::create_dir_all, path::PathBuf, rc::Rc, sync::RwLock};
|
||||||
|
|
@ -29,16 +30,10 @@ pub struct Profile {
|
||||||
pub config_path: PathBuf,
|
pub config_path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Profile {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Profile {
|
impl Profile {
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Result<Self> {
|
||||||
// Init profile path
|
// Init profile path
|
||||||
let mut config_path = user_config_dir();
|
let mut config_path = user_config_dir();
|
||||||
|
|
||||||
|
|
@ -51,44 +46,26 @@ impl Profile {
|
||||||
env!("CARGO_PKG_VERSION_MINOR")
|
env!("CARGO_PKG_VERSION_MINOR")
|
||||||
)); // @TODO remove after auto-migrate feature implementation
|
)); // @TODO remove after auto-migrate feature implementation
|
||||||
|
|
||||||
if let Err(e) = create_dir_all(&config_path) {
|
create_dir_all(&config_path)?;
|
||||||
panic!("{e}")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init database path
|
// Init database path
|
||||||
let mut database_path = config_path.clone();
|
let mut database_path = config_path.clone();
|
||||||
database_path.push(DB_NAME);
|
database_path.push(DB_NAME);
|
||||||
|
|
||||||
// Init database connection
|
// Init database connection
|
||||||
let connection = match Connection::open(database_path.as_path()) {
|
let connection = Rc::new(RwLock::new(Connection::open(database_path.as_path())?));
|
||||||
Ok(connection) => Rc::new(RwLock::new(connection)),
|
|
||||||
Err(e) => panic!("{e}"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Init profile components
|
// Init profile components
|
||||||
{
|
{
|
||||||
// Init writable connection
|
// Init writable connection
|
||||||
let mut connection = match connection.write() {
|
let mut connection = connection.write().unwrap(); // @TODO handle
|
||||||
Ok(connection) => connection,
|
|
||||||
Err(e) => todo!("{e}"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Init new transaction
|
// Init new transaction
|
||||||
let transaction = match connection.transaction() {
|
let transaction = connection.transaction()?;
|
||||||
Ok(transaction) => transaction,
|
|
||||||
Err(e) => todo!("{e}"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Begin migration
|
// Begin migration
|
||||||
match migrate(&transaction) {
|
migrate(&transaction)?;
|
||||||
Ok(_) => {
|
transaction.commit()?;
|
||||||
// Confirm changes
|
|
||||||
if let Err(e) = transaction.commit() {
|
|
||||||
todo!("{e}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => todo!("{e}"),
|
|
||||||
}
|
|
||||||
} // unlock database
|
} // unlock database
|
||||||
|
|
||||||
// Init model
|
// Init model
|
||||||
|
|
@ -104,7 +81,7 @@ impl Profile {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Init components
|
// Init components
|
||||||
let bookmark = Rc::new(Bookmark::build(&connection, &profile_id));
|
let bookmark = Rc::new(Bookmark::build(&connection, &profile_id)?);
|
||||||
let history = Rc::new(History::build(&connection, &profile_id));
|
let history = Rc::new(History::build(&connection, &profile_id));
|
||||||
let search = Rc::new(Search::build(&connection, &profile_id).unwrap()); // @TODO handle
|
let search = Rc::new(Search::build(&connection, &profile_id).unwrap()); // @TODO handle
|
||||||
let identity = Rc::new(match Identity::build(&connection, &profile_id) {
|
let identity = Rc::new(match Identity::build(&connection, &profile_id) {
|
||||||
|
|
@ -113,22 +90,20 @@ impl Profile {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Result
|
// Result
|
||||||
Self {
|
Ok(Self {
|
||||||
bookmark,
|
bookmark,
|
||||||
database,
|
database,
|
||||||
history,
|
history,
|
||||||
identity,
|
identity,
|
||||||
search,
|
search,
|
||||||
config_path,
|
config_path,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
pub fn migrate(tx: &Transaction) -> Result<()> {
|
||||||
// Migrate self components
|
// Migrate self components
|
||||||
if let Err(e) = database::init(tx) {
|
database::init(tx)?;
|
||||||
return Err(e.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate migration to children components
|
// Delegate migration to children components
|
||||||
bookmark::migrate(tx)?;
|
bookmark::migrate(tx)?;
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
mod database;
|
mod database;
|
||||||
mod error;
|
|
||||||
mod memory;
|
mod memory;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
use database::Database;
|
use database::Database;
|
||||||
use error::Error;
|
|
||||||
use memory::Memory;
|
|
||||||
|
|
||||||
use gtk::glib::DateTime;
|
use gtk::glib::DateTime;
|
||||||
|
use memory::Memory;
|
||||||
use sqlite::{Connection, Transaction};
|
use sqlite::{Connection, Transaction};
|
||||||
use std::{rc::Rc, sync::RwLock};
|
use std::{rc::Rc, sync::RwLock};
|
||||||
|
|
||||||
|
|
@ -19,48 +17,38 @@ impl Bookmark {
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
/// Create new `Self`
|
/// Create new `Self`
|
||||||
pub fn build(connection: &Rc<RwLock<Connection>>, profile_id: &Rc<i64>) -> Self {
|
pub fn build(connection: &Rc<RwLock<Connection>>, profile_id: &Rc<i64>) -> Result<Self> {
|
||||||
// Init children components
|
// Init children components
|
||||||
let database = Rc::new(Database::new(connection, profile_id));
|
let database = Rc::new(Database::new(connection, profile_id));
|
||||||
let memory = Rc::new(Memory::new());
|
let memory = Rc::new(Memory::new());
|
||||||
|
|
||||||
// Build initial index
|
// Build initial index
|
||||||
match database.records(None) {
|
for record in database.records(None)? {
|
||||||
Ok(records) => {
|
memory.add(record.request, record.id)?;
|
||||||
for record in records {
|
|
||||||
if let Err(e) = memory.add(record.request, record.id) {
|
|
||||||
todo!("{}", e.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => todo!("{}", e.to_string()),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return new `Self`
|
// Return new `Self`
|
||||||
Self { database, memory }
|
Ok(Self { database, memory })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
|
|
||||||
/// Get record `id` by `request` from memory index
|
/// Get record `id` by `request` from memory index
|
||||||
pub fn get(&self, request: &str) -> Result<i64, Error> {
|
pub fn get(&self, request: &str) -> Option<i64> {
|
||||||
match self.memory.get(request) {
|
self.memory.get(request)
|
||||||
Ok(id) => Ok(id),
|
|
||||||
Err(_) => Err(Error::MemoryNotFound),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Toggle record in `database` and `memory` index
|
/// Toggle record in `database` and `memory` index
|
||||||
/// * return `true` on bookmark created, `false` on deleted
|
/// * return `true` on bookmark created, `false` on deleted
|
||||||
pub fn toggle(&self, request: &str) -> Result<bool, Error> {
|
pub fn toggle(&self, request: &str) -> Result<bool> {
|
||||||
// Delete record if exists
|
// Delete record if exists
|
||||||
if let Ok(id) = self.get(request) {
|
if let Some(id) = self.get(request) {
|
||||||
match self.database.delete(id) {
|
match self.database.delete(id) {
|
||||||
Ok(_) => match self.memory.delete(request) {
|
Ok(_) => match self.memory.delete(request) {
|
||||||
Ok(_) => Ok(false),
|
Ok(_) => Ok(false),
|
||||||
Err(_) => Err(Error::MemoryDelete),
|
Err(_) => panic!(), // unexpected
|
||||||
},
|
},
|
||||||
Err(_) => Err(Error::DatabaseDelete),
|
Err(_) => panic!(), // unexpected
|
||||||
}
|
}
|
||||||
// Otherwise, create new record
|
// Otherwise, create new record
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -70,9 +58,9 @@ impl Bookmark {
|
||||||
{
|
{
|
||||||
Ok(id) => match self.memory.add(request.into(), id) {
|
Ok(id) => match self.memory.add(request.into(), id) {
|
||||||
Ok(_) => Ok(true),
|
Ok(_) => Ok(true),
|
||||||
Err(_) => Err(Error::MemoryAdd),
|
Err(_) => panic!(), // unexpected
|
||||||
},
|
},
|
||||||
Err(_) => Err(Error::DatabaseAdd),
|
Err(_) => panic!(), // unexpected
|
||||||
}
|
}
|
||||||
} // @TODO return affected rows on success?
|
} // @TODO return affected rows on success?
|
||||||
}
|
}
|
||||||
|
|
@ -80,11 +68,9 @@ impl Bookmark {
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
|
|
||||||
pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
pub fn migrate(tx: &Transaction) -> Result<()> {
|
||||||
// Migrate self components
|
// Migrate self components
|
||||||
if let Err(e) = database::init(tx) {
|
database::init(tx)?;
|
||||||
return Err(e.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate migration to childs
|
// Delegate migration to childs
|
||||||
// nothing yet..
|
// nothing yet..
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
|
use anyhow::Result;
|
||||||
use gtk::glib::DateTime;
|
use gtk::glib::DateTime;
|
||||||
use sqlite::{Connection, Error, Transaction};
|
use sqlite::{Connection, Transaction};
|
||||||
use std::{rc::Rc, sync::RwLock};
|
use std::{rc::Rc, sync::RwLock};
|
||||||
|
|
||||||
pub struct Table {
|
pub struct Table {
|
||||||
|
|
@ -28,7 +29,7 @@ impl Database {
|
||||||
// Getters
|
// Getters
|
||||||
|
|
||||||
/// Get bookmark records from database with optional filter by `request`
|
/// Get bookmark records from database with optional filter by `request`
|
||||||
pub fn records(&self, request: Option<&str>) -> Result<Vec<Table>, Error> {
|
pub fn records(&self, request: Option<&str>) -> Result<Vec<Table>> {
|
||||||
let readable = self.connection.read().unwrap(); // @TODO
|
let readable = self.connection.read().unwrap(); // @TODO
|
||||||
let tx = readable.unchecked_transaction()?;
|
let tx = readable.unchecked_transaction()?;
|
||||||
select(&tx, *self.profile_id, request)
|
select(&tx, *self.profile_id, request)
|
||||||
|
|
@ -38,45 +39,28 @@ impl Database {
|
||||||
|
|
||||||
/// Create new bookmark record in database
|
/// Create new bookmark record in database
|
||||||
/// * return last insert ID on success
|
/// * return last insert ID on success
|
||||||
pub fn add(&self, time: DateTime, request: String) -> Result<i64, Error> {
|
pub fn add(&self, time: DateTime, request: String) -> Result<i64> {
|
||||||
// Begin new transaction
|
|
||||||
let mut writable = self.connection.write().unwrap(); // @TODO
|
let mut writable = self.connection.write().unwrap(); // @TODO
|
||||||
let tx = writable.transaction()?;
|
let tx = writable.transaction()?;
|
||||||
|
let id = insert(&tx, *self.profile_id, time, request)?;
|
||||||
// Create new record
|
tx.commit()?;
|
||||||
insert(&tx, *self.profile_id, time, request)?;
|
Ok(id)
|
||||||
|
|
||||||
// Hold insert ID for result
|
|
||||||
let id = last_insert_id(&tx);
|
|
||||||
|
|
||||||
// Done
|
|
||||||
match tx.commit() {
|
|
||||||
Ok(_) => Ok(id),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete bookmark record from database
|
/// Delete bookmark record from database
|
||||||
pub fn delete(&self, id: i64) -> Result<(), Error> {
|
pub fn delete(&self, id: i64) -> Result<usize> {
|
||||||
// Begin new transaction
|
|
||||||
let mut writable = self.connection.write().unwrap(); // @TODO
|
let mut writable = self.connection.write().unwrap(); // @TODO
|
||||||
let tx = writable.transaction()?;
|
let tx = writable.transaction()?;
|
||||||
|
let usize = delete(&tx, id)?;
|
||||||
// Delete record by ID
|
tx.commit()?;
|
||||||
match delete(&tx, id) {
|
Ok(usize)
|
||||||
Ok(_) => match tx.commit() {
|
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
},
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Low-level DB API
|
// Low-level DB API
|
||||||
|
|
||||||
pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
pub fn init(tx: &Transaction) -> Result<usize> {
|
||||||
tx.execute(
|
Ok(tx.execute(
|
||||||
"CREATE TABLE IF NOT EXISTS `profile_bookmark`
|
"CREATE TABLE IF NOT EXISTS `profile_bookmark`
|
||||||
(
|
(
|
||||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
|
@ -87,15 +71,10 @@ pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
||||||
FOREIGN KEY (`profile_id`) REFERENCES `profile`(`id`)
|
FOREIGN KEY (`profile_id`) REFERENCES `profile`(`id`)
|
||||||
)",
|
)",
|
||||||
[],
|
[],
|
||||||
)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(
|
pub fn insert(tx: &Transaction, profile_id: i64, time: DateTime, request: String) -> Result<i64> {
|
||||||
tx: &Transaction,
|
|
||||||
profile_id: i64,
|
|
||||||
time: DateTime,
|
|
||||||
request: String,
|
|
||||||
) -> Result<usize, Error> {
|
|
||||||
tx.execute(
|
tx.execute(
|
||||||
"INSERT INTO `profile_bookmark` (
|
"INSERT INTO `profile_bookmark` (
|
||||||
`profile_id`,
|
`profile_id`,
|
||||||
|
|
@ -103,14 +82,11 @@ pub fn insert(
|
||||||
`request`
|
`request`
|
||||||
) VALUES (?, ?, ?)",
|
) VALUES (?, ?, ?)",
|
||||||
(profile_id, time.to_unix(), request),
|
(profile_id, time.to_unix(), request),
|
||||||
)
|
)?;
|
||||||
|
Ok(tx.last_insert_rowid())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select(
|
pub fn select(tx: &Transaction, profile_id: i64, request: Option<&str>) -> Result<Vec<Table>> {
|
||||||
tx: &Transaction,
|
|
||||||
profile_id: i64,
|
|
||||||
request: Option<&str>,
|
|
||||||
) -> Result<Vec<Table>, Error> {
|
|
||||||
let mut stmt = tx.prepare(
|
let mut stmt = tx.prepare(
|
||||||
"SELECT `id`, `profile_id`, `time`, `request`
|
"SELECT `id`, `profile_id`, `time`, `request`
|
||||||
FROM `profile_bookmark`
|
FROM `profile_bookmark`
|
||||||
|
|
@ -136,10 +112,6 @@ pub fn select(
|
||||||
Ok(records)
|
Ok(records)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(tx: &Transaction, id: i64) -> Result<usize, Error> {
|
pub fn delete(tx: &Transaction, id: i64) -> Result<usize> {
|
||||||
tx.execute("DELETE FROM `profile_bookmark` WHERE `id` = ?", [id])
|
Ok(tx.execute("DELETE FROM `profile_bookmark` WHERE `id` = ?", [id])?)
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_insert_id(tx: &Transaction) -> i64 {
|
|
||||||
tx.last_insert_rowid()
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
DatabaseAdd,
|
|
||||||
DatabaseDelete,
|
|
||||||
MemoryAdd,
|
|
||||||
MemoryDelete,
|
|
||||||
MemoryNotFound,
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
mod error;
|
use anyhow::Result;
|
||||||
use error::Error;
|
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::{cell::RefCell, collections::HashMap};
|
use std::{cell::RefCell, collections::HashMap};
|
||||||
|
|
||||||
|
|
@ -29,37 +27,34 @@ impl Memory {
|
||||||
|
|
||||||
/// Add new record with `request` as key and `id` as value
|
/// Add new record with `request` as key and `id` as value
|
||||||
/// * validate record with same key does not exist yet
|
/// * validate record with same key does not exist yet
|
||||||
pub fn add(&self, request: String, id: i64) -> Result<(), Error> {
|
pub fn add(&self, request: String, id: i64) -> Result<()> {
|
||||||
// Borrow shared index access
|
// Borrow shared index access
|
||||||
let mut index = self.index.borrow_mut();
|
let mut index = self.index.borrow_mut();
|
||||||
|
|
||||||
// Prevent existing key overwrite
|
// Prevent existing key overwrite
|
||||||
if index.contains_key(&request) {
|
if index.contains_key(&request) {
|
||||||
return Err(Error::Overwrite(request));
|
panic!() // unexpected
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slot should be free, let check it twice
|
// Slot should be free, let check it twice
|
||||||
match index.insert(request, id) {
|
match index.insert(request, id) {
|
||||||
Some(_) => Err(Error::Unexpected),
|
Some(_) => panic!(), // unexpected
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete record from index by `request`
|
/// Delete record from index by `request`
|
||||||
/// * validate record key is exist
|
/// * validate record key is exist
|
||||||
pub fn delete(&self, request: &str) -> Result<(), Error> {
|
pub fn delete(&self, request: &str) -> Result<()> {
|
||||||
match self.index.borrow_mut().remove(request) {
|
match self.index.borrow_mut().remove(request) {
|
||||||
Some(_) => Ok(()),
|
Some(_) => Ok(()),
|
||||||
None => Err(Error::Unexpected), // @TODO
|
None => panic!(), // unexpected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get `id` by `request` from memory index
|
/// Get `id` by `request` from memory index
|
||||||
pub fn get(&self, request: &str) -> Result<i64, Error> {
|
pub fn get(&self, request: &str) -> Option<i64> {
|
||||||
match self.index.borrow().get(request) {
|
self.index.borrow().get(request).copied()
|
||||||
Some(&value) => Ok(value),
|
|
||||||
None => Err(Error::Unexpected), // @TODO
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get recent requests vector sorted by `ID` DESC
|
/// Get recent requests vector sorted by `ID` DESC
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
use std::fmt::{Display, Formatter, Result};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
Overwrite(String),
|
|
||||||
Unexpected,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Error {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
|
||||||
match self {
|
|
||||||
Self::Overwrite(key) => {
|
|
||||||
write!(f, "Overwrite attempt for existing record `{key}`")
|
|
||||||
}
|
|
||||||
Self::Unexpected => write!(f, "Unexpected error"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
|
use anyhow::Result;
|
||||||
use gtk::glib::DateTime;
|
use gtk::glib::DateTime;
|
||||||
use sqlite::{Connection, Error, Transaction};
|
use sqlite::{Connection, Transaction};
|
||||||
use std::{rc::Rc, sync::RwLock};
|
use std::{rc::Rc, sync::RwLock};
|
||||||
|
|
||||||
pub struct Table {
|
pub struct Table {
|
||||||
|
|
@ -26,14 +27,14 @@ impl Database {
|
||||||
// Getters
|
// Getters
|
||||||
|
|
||||||
/// Get all records
|
/// Get all records
|
||||||
pub fn records(&self) -> Result<Vec<Table>, Error> {
|
pub fn records(&self) -> Result<Vec<Table>> {
|
||||||
let readable = self.connection.read().unwrap();
|
let readable = self.connection.read().unwrap();
|
||||||
let tx = readable.unchecked_transaction()?;
|
let tx = readable.unchecked_transaction()?;
|
||||||
select(&tx)
|
select(&tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get active profile record if exist
|
/// Get active profile record if exist
|
||||||
pub fn active(&self) -> Result<Option<Table>, Error> {
|
pub fn active(&self) -> Result<Option<Table>> {
|
||||||
let records = self.records()?;
|
let records = self.records()?;
|
||||||
Ok(records.into_iter().find(|record| record.is_active))
|
Ok(records.into_iter().find(|record| record.is_active))
|
||||||
}
|
}
|
||||||
|
|
@ -41,36 +42,24 @@ impl Database {
|
||||||
// Setters
|
// Setters
|
||||||
|
|
||||||
/// Create new record in `Self` database connected
|
/// Create new record in `Self` database connected
|
||||||
pub fn add(&self, is_active: bool, time: DateTime, name: Option<String>) -> Result<i64, Error> {
|
pub fn add(&self, is_active: bool, time: DateTime, name: Option<String>) -> Result<i64> {
|
||||||
// Begin new transaction
|
|
||||||
let mut writable = self.connection.write().unwrap();
|
let mut writable = self.connection.write().unwrap();
|
||||||
let tx = writable.transaction()?;
|
let tx = writable.transaction()?;
|
||||||
|
|
||||||
// New record has active status
|
|
||||||
if is_active {
|
if is_active {
|
||||||
// Deactivate other records as only one profile should be active
|
|
||||||
for record in select(&tx)? {
|
for record in select(&tx)? {
|
||||||
update(&tx, record.id, false, record.time, record.name)?;
|
update(&tx, record.id, false, record.time, record.name)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let id = insert(&tx, is_active, time, name)?;
|
||||||
// Create new record
|
|
||||||
insert(&tx, is_active, time, name)?;
|
|
||||||
|
|
||||||
// Hold insert ID for result
|
|
||||||
let id = last_insert_id(&tx);
|
|
||||||
|
|
||||||
// Done
|
|
||||||
tx.commit()?;
|
tx.commit()?;
|
||||||
|
|
||||||
Ok(id)
|
Ok(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Low-level DB API
|
// Low-level DB API
|
||||||
|
|
||||||
pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
pub fn init(tx: &Transaction) -> Result<usize> {
|
||||||
tx.execute(
|
Ok(tx.execute(
|
||||||
"CREATE TABLE IF NOT EXISTS `profile`
|
"CREATE TABLE IF NOT EXISTS `profile`
|
||||||
(
|
(
|
||||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
|
@ -79,7 +68,7 @@ pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
||||||
`name` VARCHAR(255)
|
`name` VARCHAR(255)
|
||||||
)",
|
)",
|
||||||
[],
|
[],
|
||||||
)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(
|
pub fn insert(
|
||||||
|
|
@ -87,7 +76,7 @@ pub fn insert(
|
||||||
is_active: bool,
|
is_active: bool,
|
||||||
time: DateTime,
|
time: DateTime,
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<i64> {
|
||||||
tx.execute(
|
tx.execute(
|
||||||
"INSERT INTO `profile` (
|
"INSERT INTO `profile` (
|
||||||
`is_active`,
|
`is_active`,
|
||||||
|
|
@ -95,7 +84,8 @@ pub fn insert(
|
||||||
`name`
|
`name`
|
||||||
) VALUES (?, ?, ?)",
|
) VALUES (?, ?, ?)",
|
||||||
(is_active, time.to_unix(), name),
|
(is_active, time.to_unix(), name),
|
||||||
)
|
)?;
|
||||||
|
Ok(tx.last_insert_rowid())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(
|
pub fn update(
|
||||||
|
|
@ -104,14 +94,14 @@ pub fn update(
|
||||||
is_active: bool,
|
is_active: bool,
|
||||||
time: DateTime,
|
time: DateTime,
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<usize> {
|
||||||
tx.execute(
|
Ok(tx.execute(
|
||||||
"UPDATE `profile` SET `is_active` = ?, `time` = ?, `name` = ? WHERE `id` = ?",
|
"UPDATE `profile` SET `is_active` = ?, `time` = ?, `name` = ? WHERE `id` = ?",
|
||||||
(is_active, time.to_unix(), name, id),
|
(is_active, time.to_unix(), name, id),
|
||||||
)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select(tx: &Transaction) -> Result<Vec<Table>, Error> {
|
pub fn select(tx: &Transaction) -> Result<Vec<Table>> {
|
||||||
let mut stmt = tx.prepare("SELECT `id`, `is_active`, `time`, `name` FROM `profile`")?;
|
let mut stmt = tx.prepare("SELECT `id`, `is_active`, `time`, `name` FROM `profile`")?;
|
||||||
let result = stmt.query_map([], |row| {
|
let result = stmt.query_map([], |row| {
|
||||||
Ok(Table {
|
Ok(Table {
|
||||||
|
|
@ -131,7 +121,3 @@ pub fn select(tx: &Transaction) -> Result<Vec<Table>, Error> {
|
||||||
|
|
||||||
Ok(records)
|
Ok(records)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn last_insert_id(tx: &Transaction) -> i64 {
|
|
||||||
tx.last_insert_rowid()
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
use gtk::glib::DateTime;
|
|
||||||
use sqlite::{Error, Transaction};
|
|
||||||
|
|
||||||
pub struct Table {
|
|
||||||
pub id: i64,
|
|
||||||
pub profile_id: i64,
|
|
||||||
pub time: DateTime,
|
|
||||||
pub request: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Database {
|
|
||||||
// nothing yet..
|
|
||||||
}
|
|
||||||
|
|
||||||
// Low-level DB API
|
|
||||||
|
|
||||||
pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
|
||||||
tx.execute(
|
|
||||||
"CREATE TABLE IF NOT EXISTS `profile_history`
|
|
||||||
(
|
|
||||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
||||||
`profile_id` INTEGER NOT NULL,
|
|
||||||
`time` INTEGER NOT NULL,
|
|
||||||
`request` TEXT NOT NULL,
|
|
||||||
|
|
||||||
FOREIGN KEY (`profile_id`) REFERENCES `profile`(`id`)
|
|
||||||
)",
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert(
|
|
||||||
tx: &Transaction,
|
|
||||||
profile_id: i64,
|
|
||||||
time: DateTime,
|
|
||||||
request: String,
|
|
||||||
) -> Result<usize, Error> {
|
|
||||||
tx.execute(
|
|
||||||
"INSERT INTO `history` (
|
|
||||||
`profile_id`,
|
|
||||||
`time`,
|
|
||||||
`request`
|
|
||||||
) VALUES (?, ?, ?)",
|
|
||||||
(profile_id, time.to_unix(), request),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn select(
|
|
||||||
tx: &Transaction,
|
|
||||||
profile_id: i64,
|
|
||||||
request: Option<String>,
|
|
||||||
) -> Result<Vec<Table>, Error> {
|
|
||||||
let mut stmt = tx.prepare(
|
|
||||||
"SELECT `id`, `profile_id`, `time`, `request`
|
|
||||||
FROM `profile_history`
|
|
||||||
WHERE `profile_id` = ? AND `request` LIKE ?",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let result = stmt.query_map((profile_id, request.unwrap_or("%".to_string())), |row| {
|
|
||||||
Ok(Table {
|
|
||||||
id: row.get(0)?,
|
|
||||||
profile_id: row.get(1)?,
|
|
||||||
time: DateTime::from_unix_local(row.get(2)?).unwrap(),
|
|
||||||
request: row.get(3)?,
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let mut records = Vec::new();
|
|
||||||
|
|
||||||
for record in result {
|
|
||||||
let table = record?;
|
|
||||||
records.push(table);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(records)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn delete(tx: &Transaction, id: i64) -> Result<usize, Error> {
|
|
||||||
tx.execute("DELETE FROM `profile_history` WHERE `id` = ?", [id])
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +1,15 @@
|
||||||
mod auth;
|
mod auth;
|
||||||
mod certificate;
|
mod certificate;
|
||||||
mod database;
|
mod database;
|
||||||
mod error;
|
|
||||||
mod item;
|
mod item;
|
||||||
mod memory;
|
mod memory;
|
||||||
|
|
||||||
|
use anyhow::{bail, Result};
|
||||||
use auth::Auth;
|
use auth::Auth;
|
||||||
use database::Database;
|
use database::Database;
|
||||||
pub use error::Error;
|
|
||||||
use item::Item;
|
|
||||||
|
|
||||||
use memory::Memory;
|
|
||||||
|
|
||||||
use gtk::glib::DateTime;
|
use gtk::glib::DateTime;
|
||||||
|
use item::Item;
|
||||||
|
use memory::Memory;
|
||||||
use sqlite::{Connection, Transaction};
|
use sqlite::{Connection, Transaction};
|
||||||
use std::{rc::Rc, sync::RwLock};
|
use std::{rc::Rc, sync::RwLock};
|
||||||
|
|
||||||
|
|
@ -32,11 +29,11 @@ impl Identity {
|
||||||
pub fn build(
|
pub fn build(
|
||||||
connection: &Rc<RwLock<Connection>>,
|
connection: &Rc<RwLock<Connection>>,
|
||||||
profile_identity_id: &Rc<i64>,
|
profile_identity_id: &Rc<i64>,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self> {
|
||||||
// Init components
|
// Init components
|
||||||
let auth = match Auth::build(connection) {
|
let auth = match Auth::build(connection) {
|
||||||
Ok(auth) => Rc::new(auth),
|
Ok(auth) => Rc::new(auth),
|
||||||
Err(e) => return Err(Error::Auth(e)),
|
Err(e) => bail!("Could not create auth: {e}"),
|
||||||
};
|
};
|
||||||
let database = Rc::new(Database::build(connection, profile_identity_id));
|
let database = Rc::new(Database::build(connection, profile_identity_id));
|
||||||
let memory = Rc::new(Memory::new());
|
let memory = Rc::new(Memory::new());
|
||||||
|
|
@ -58,33 +55,23 @@ impl Identity {
|
||||||
|
|
||||||
/// Add new record to database, update memory index
|
/// Add new record to database, update memory index
|
||||||
/// * return new `profile_identity_id` on success
|
/// * return new `profile_identity_id` on success
|
||||||
pub fn add(&self, pem: &str) -> Result<i64, Error> {
|
pub fn add(&self, pem: &str) -> Result<i64> {
|
||||||
match self.database.add(pem) {
|
let profile_identity_id = self.database.add(pem)?;
|
||||||
Ok(profile_identity_id) => {
|
|
||||||
self.index()?;
|
self.index()?;
|
||||||
Ok(profile_identity_id)
|
Ok(profile_identity_id)
|
||||||
}
|
}
|
||||||
Err(e) => Err(Error::Database(e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete record from database including children dependencies, update memory index
|
/// Delete record from database including children dependencies, update memory index
|
||||||
pub fn delete(&self, profile_identity_id: i64) -> Result<(), Error> {
|
pub fn delete(&self, profile_identity_id: i64) -> Result<()> {
|
||||||
match self.auth.remove_ref(profile_identity_id) {
|
self.auth.remove_ref(profile_identity_id)?;
|
||||||
Ok(_) => match self.database.delete(profile_identity_id) {
|
self.database.delete(profile_identity_id)?;
|
||||||
Ok(_) => {
|
|
||||||
self.index()?;
|
self.index()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(e) => Err(Error::Database(e)),
|
|
||||||
},
|
|
||||||
Err(e) => Err(Error::Auth(e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate new certificate and insert record to DB, update memory index
|
/// Generate new certificate and insert record to DB, update memory index
|
||||||
/// * return new `profile_identity_id` on success
|
/// * return new `profile_identity_id` on success
|
||||||
pub fn make(&self, time: Option<(DateTime, DateTime)>, name: &str) -> Result<i64, Error> {
|
pub fn make(&self, time: Option<(DateTime, DateTime)>, name: &str) -> Result<i64> {
|
||||||
// Generate new certificate
|
// Generate new certificate
|
||||||
match certificate::generate(
|
match certificate::generate(
|
||||||
match time {
|
match time {
|
||||||
|
|
@ -97,29 +84,17 @@ impl Identity {
|
||||||
name,
|
name,
|
||||||
) {
|
) {
|
||||||
Ok(pem) => self.add(&pem),
|
Ok(pem) => self.add(&pem),
|
||||||
Err(e) => Err(Error::Certificate(e)),
|
Err(e) => bail!("Could not create certificate: {e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create new `Memory` index from `Database` for `Self`
|
/// Create new `Memory` index from `Database` for `Self`
|
||||||
pub fn index(&self) -> Result<(), Error> {
|
pub fn index(&self) -> Result<()> {
|
||||||
// Clear previous records
|
// Clear previous records
|
||||||
if let Err(e) = self.memory.clear() {
|
self.memory.clear()?;
|
||||||
return Err(Error::Memory(e));
|
for record in self.database.records()? {
|
||||||
|
self.memory.add(record.id, record.pem)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build new index
|
|
||||||
match self.database.records() {
|
|
||||||
Ok(records) => {
|
|
||||||
for record in records {
|
|
||||||
if let Err(e) = self.memory.add(record.id, record.pem) {
|
|
||||||
return Err(Error::Memory(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => return Err(Error::Database(e)),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,11 +119,9 @@ impl Identity {
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
|
|
||||||
pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
pub fn migrate(tx: &Transaction) -> Result<()> {
|
||||||
// Migrate self components
|
// Migrate self components
|
||||||
if let Err(e) = database::init(tx) {
|
database::init(tx)?;
|
||||||
return Err(e.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate migration to childs
|
// Delegate migration to childs
|
||||||
auth::migrate(tx)?;
|
auth::migrate(tx)?;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
//! Controller for children `database` and `memory` components
|
//! Controller for children `database` and `memory` components
|
||||||
|
|
||||||
mod database;
|
mod database;
|
||||||
mod error;
|
|
||||||
mod memory;
|
mod memory;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
use database::Database;
|
use database::Database;
|
||||||
pub use error::Error;
|
|
||||||
use memory::Memory;
|
use memory::Memory;
|
||||||
|
|
||||||
use sqlite::{Connection, Transaction};
|
use sqlite::{Connection, Transaction};
|
||||||
use std::{rc::Rc, sync::RwLock};
|
use std::{rc::Rc, sync::RwLock};
|
||||||
|
|
||||||
|
|
@ -21,7 +19,7 @@ impl Auth {
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
/// Create new `Self`
|
/// Create new `Self`
|
||||||
pub fn build(connection: &Rc<RwLock<Connection>>) -> Result<Self, Error> {
|
pub fn build(connection: &Rc<RwLock<Connection>>) -> Result<Self> {
|
||||||
// Init `Self`
|
// Init `Self`
|
||||||
let this = Self {
|
let this = Self {
|
||||||
database: Rc::new(Database::build(connection)),
|
database: Rc::new(Database::build(connection)),
|
||||||
|
|
@ -41,18 +39,14 @@ impl Auth {
|
||||||
/// * deactivate active auth by remove previous records from `Self` database
|
/// * deactivate active auth by remove previous records from `Self` database
|
||||||
/// * reindex `Self` memory index on success
|
/// * reindex `Self` memory index on success
|
||||||
/// * return last insert `profile_identity_auth_id` on success
|
/// * return last insert `profile_identity_auth_id` on success
|
||||||
pub fn apply(&self, profile_identity_id: i64, request: &str) -> Result<i64, Error> {
|
pub fn apply(&self, profile_identity_id: i64, request: &str) -> Result<i64> {
|
||||||
// Cleanup records match `scope` (unauthorize)
|
// Cleanup records match `scope` (unauthorize)
|
||||||
self.remove(request)?;
|
self.remove(request)?;
|
||||||
|
|
||||||
// Create new record (auth)
|
// Create new record (auth)
|
||||||
let profile_identity_auth_id = match self
|
let profile_identity_auth_id = self
|
||||||
.database
|
.database
|
||||||
.add(profile_identity_id, &filter_scope(request))
|
.add(profile_identity_id, &filter_scope(request))?;
|
||||||
{
|
|
||||||
Ok(id) => id,
|
|
||||||
Err(e) => return Err(Error::Database(e)),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reindex
|
// Reindex
|
||||||
self.index()?;
|
self.index()?;
|
||||||
|
|
@ -62,56 +56,31 @@ impl Auth {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove all records match request (unauthorize)
|
/// Remove all records match request (unauthorize)
|
||||||
pub fn remove(&self, request: &str) -> Result<(), Error> {
|
pub fn remove(&self, request: &str) -> Result<()> {
|
||||||
match self.database.records_scope(Some(&filter_scope(request))) {
|
for record in self.database.records_scope(Some(&filter_scope(request)))? {
|
||||||
Ok(records) => {
|
self.database.delete(record.id)?;
|
||||||
for record in records {
|
|
||||||
if let Err(e) = self.database.delete(record.id) {
|
|
||||||
return Err(Error::Database(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => return Err(Error::Database(e)),
|
|
||||||
}
|
}
|
||||||
self.index()?;
|
self.index()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove all records match `profile_identity_id` foreign reference key
|
/// Remove all records match `profile_identity_id` foreign reference key
|
||||||
pub fn remove_ref(&self, profile_identity_id: i64) -> Result<(), Error> {
|
pub fn remove_ref(&self, profile_identity_id: i64) -> Result<()> {
|
||||||
match self.database.records_ref(profile_identity_id) {
|
for record in self.database.records_ref(profile_identity_id)? {
|
||||||
Ok(records) => {
|
self.database.delete(record.id)?;
|
||||||
for record in records {
|
|
||||||
if let Err(e) = self.database.delete(record.id) {
|
|
||||||
return Err(Error::Database(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => return Err(Error::Database(e)),
|
|
||||||
}
|
}
|
||||||
self.index()?;
|
self.index()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create new `Memory` index from `Database` for `Self`
|
/// Create new `Memory` index from `Database` for `Self`
|
||||||
pub fn index(&self) -> Result<(), Error> {
|
pub fn index(&self) -> Result<()> {
|
||||||
// Clear previous records
|
// Clear previous records
|
||||||
if let Err(e) = self.memory.clear() {
|
self.memory.clear()?;
|
||||||
return Err(Error::Memory(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build new index
|
// Build new index
|
||||||
match self.database.records_scope(None) {
|
for record in self.database.records_scope(None)? {
|
||||||
Ok(records) => {
|
self.memory.add(record.scope, record.profile_identity_id)?;
|
||||||
for record in records {
|
|
||||||
if let Err(e) = self.memory.add(record.scope, record.profile_identity_id) {
|
|
||||||
return Err(Error::Memory(e));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => return Err(Error::Database(e)),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,11 +123,9 @@ impl Auth {
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
|
|
||||||
pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
pub fn migrate(tx: &Transaction) -> Result<()> {
|
||||||
// Migrate self components
|
// Migrate self components
|
||||||
if let Err(e) = database::init(tx) {
|
database::init(tx)?;
|
||||||
return Err(e.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate migration to childs
|
// Delegate migration to childs
|
||||||
// nothing yet..
|
// nothing yet..
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use sqlite::{Connection, Error, Transaction};
|
use anyhow::Result;
|
||||||
|
use sqlite::{Connection, Transaction};
|
||||||
use std::{rc::Rc, sync::RwLock};
|
use std::{rc::Rc, sync::RwLock};
|
||||||
|
|
||||||
pub struct Table {
|
pub struct Table {
|
||||||
|
|
@ -25,51 +26,34 @@ impl Database {
|
||||||
// Actions
|
// Actions
|
||||||
|
|
||||||
/// Create new record in database
|
/// Create new record in database
|
||||||
pub fn add(&self, profile_identity_id: i64, scope: &str) -> Result<i64, Error> {
|
pub fn add(&self, profile_identity_id: i64, scope: &str) -> Result<i64> {
|
||||||
// Begin new transaction
|
|
||||||
let mut writable = self.connection.write().unwrap(); // @TODO
|
let mut writable = self.connection.write().unwrap(); // @TODO
|
||||||
let tx = writable.transaction()?;
|
let tx = writable.transaction()?;
|
||||||
|
let id = insert(&tx, profile_identity_id, scope)?;
|
||||||
// Create new record
|
tx.commit()?;
|
||||||
insert(&tx, profile_identity_id, scope)?;
|
Ok(id)
|
||||||
|
|
||||||
// Hold insert ID for result
|
|
||||||
let id = last_insert_id(&tx);
|
|
||||||
|
|
||||||
// Done
|
|
||||||
match tx.commit() {
|
|
||||||
Ok(_) => Ok(id),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete record with given `id` from database
|
/// Delete record with given `id` from database
|
||||||
pub fn delete(&self, id: i64) -> Result<(), Error> {
|
pub fn delete(&self, id: i64) -> Result<()> {
|
||||||
// Begin new transaction
|
|
||||||
let mut writable = self.connection.write().unwrap(); // @TODO
|
let mut writable = self.connection.write().unwrap(); // @TODO
|
||||||
let tx = writable.transaction()?;
|
let tx = writable.transaction()?;
|
||||||
|
|
||||||
// Create new record
|
|
||||||
delete(&tx, id)?;
|
delete(&tx, id)?;
|
||||||
|
tx.commit()?;
|
||||||
// Done
|
Ok(())
|
||||||
match tx.commit() {
|
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
|
|
||||||
/// Get records from database match current `profile_id` optionally filtered by `scope`
|
/// Get records from database match current `profile_id` optionally filtered by `scope`
|
||||||
pub fn records_scope(&self, scope: Option<&str>) -> Result<Vec<Table>, Error> {
|
pub fn records_scope(&self, scope: Option<&str>) -> Result<Vec<Table>> {
|
||||||
let readable = self.connection.read().unwrap(); // @TODO
|
let readable = self.connection.read().unwrap(); // @TODO
|
||||||
let tx = readable.unchecked_transaction()?;
|
let tx = readable.unchecked_transaction()?;
|
||||||
select_scope(&tx, scope)
|
select_scope(&tx, scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get records from database match current `profile_id` optionally filtered by `scope`
|
/// Get records from database match current `profile_id` optionally filtered by `scope`
|
||||||
pub fn records_ref(&self, profile_identity_id: i64) -> Result<Vec<Table>, Error> {
|
pub fn records_ref(&self, profile_identity_id: i64) -> Result<Vec<Table>> {
|
||||||
let readable = self.connection.read().unwrap(); // @TODO
|
let readable = self.connection.read().unwrap(); // @TODO
|
||||||
let tx = readable.unchecked_transaction()?;
|
let tx = readable.unchecked_transaction()?;
|
||||||
select_ref(&tx, profile_identity_id)
|
select_ref(&tx, profile_identity_id)
|
||||||
|
|
@ -78,8 +62,8 @@ impl Database {
|
||||||
|
|
||||||
// Low-level DB API
|
// Low-level DB API
|
||||||
|
|
||||||
pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
pub fn init(tx: &Transaction) -> Result<usize> {
|
||||||
tx.execute(
|
Ok(tx.execute(
|
||||||
"CREATE TABLE IF NOT EXISTS `profile_identity_auth`
|
"CREATE TABLE IF NOT EXISTS `profile_identity_auth`
|
||||||
(
|
(
|
||||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
|
@ -90,24 +74,25 @@ pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
||||||
UNIQUE (`scope`)
|
UNIQUE (`scope`)
|
||||||
)",
|
)",
|
||||||
[],
|
[],
|
||||||
)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(tx: &Transaction, profile_identity_id: i64, scope: &str) -> Result<usize, Error> {
|
pub fn insert(tx: &Transaction, profile_identity_id: i64, scope: &str) -> Result<i64> {
|
||||||
tx.execute(
|
tx.execute(
|
||||||
"INSERT INTO `profile_identity_auth` (
|
"INSERT INTO `profile_identity_auth` (
|
||||||
`profile_identity_id`,
|
`profile_identity_id`,
|
||||||
`scope`
|
`scope`
|
||||||
) VALUES (?, ?)",
|
) VALUES (?, ?)",
|
||||||
(profile_identity_id, scope),
|
(profile_identity_id, scope),
|
||||||
)
|
)?;
|
||||||
|
Ok(tx.last_insert_rowid())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(tx: &Transaction, id: i64) -> Result<usize, Error> {
|
pub fn delete(tx: &Transaction, id: i64) -> Result<usize> {
|
||||||
tx.execute("DELETE FROM `profile_identity_auth` WHERE `id` = ?", [id])
|
Ok(tx.execute("DELETE FROM `profile_identity_auth` WHERE `id` = ?", [id])?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_scope(tx: &Transaction, scope: Option<&str>) -> Result<Vec<Table>, Error> {
|
pub fn select_scope(tx: &Transaction, scope: Option<&str>) -> Result<Vec<Table>> {
|
||||||
let mut stmt = tx.prepare(
|
let mut stmt = tx.prepare(
|
||||||
"SELECT `id`,
|
"SELECT `id`,
|
||||||
`profile_identity_id`,
|
`profile_identity_id`,
|
||||||
|
|
@ -135,7 +120,7 @@ pub fn select_scope(tx: &Transaction, scope: Option<&str>) -> Result<Vec<Table>,
|
||||||
Ok(records)
|
Ok(records)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_ref(tx: &Transaction, profile_identity_id: i64) -> Result<Vec<Table>, Error> {
|
pub fn select_ref(tx: &Transaction, profile_identity_id: i64) -> Result<Vec<Table>> {
|
||||||
let mut stmt = tx.prepare(
|
let mut stmt = tx.prepare(
|
||||||
"SELECT `id`,
|
"SELECT `id`,
|
||||||
`profile_identity_id`,
|
`profile_identity_id`,
|
||||||
|
|
@ -162,7 +147,3 @@ pub fn select_ref(tx: &Transaction, profile_identity_id: i64) -> Result<Vec<Tabl
|
||||||
|
|
||||||
Ok(records)
|
Ok(records)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn last_insert_id(tx: &Transaction) -> i64 {
|
|
||||||
tx.last_insert_rowid()
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
use std::fmt::{Display, Formatter, Result};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
Database(sqlite::Error),
|
|
||||||
Memory(super::memory::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Error {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
|
||||||
match self {
|
|
||||||
Self::Database(e) => write!(f, "Database error: {e}"),
|
|
||||||
Self::Memory(e) => write!(f, "Memory error: {e}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod error;
|
|
||||||
|
|
||||||
|
use anyhow::{bail, Result};
|
||||||
pub use auth::Auth;
|
pub use auth::Auth;
|
||||||
pub use error::Error;
|
|
||||||
|
|
||||||
use std::{cell::RefCell, collections::HashMap};
|
use std::{cell::RefCell, collections::HashMap};
|
||||||
|
|
||||||
/// Reduce disk usage by cache Auth index in memory
|
/// Reduce disk usage by cache Auth index in memory
|
||||||
|
|
@ -31,30 +29,30 @@ impl Memory {
|
||||||
|
|
||||||
/// Add new record with `scope` as key and `profile_identity_id` as value
|
/// Add new record with `scope` as key and `profile_identity_id` as value
|
||||||
/// * validate record with same key does not exist yet
|
/// * validate record with same key does not exist yet
|
||||||
pub fn add(&self, scope: String, profile_identity_id: i64) -> Result<(), Error> {
|
pub fn add(&self, scope: String, profile_identity_id: i64) -> Result<()> {
|
||||||
// Borrow shared index access
|
// Borrow shared index access
|
||||||
let mut index = self.index.borrow_mut();
|
let mut index = self.index.borrow_mut();
|
||||||
|
|
||||||
// Prevent existing key overwrite
|
// Prevent existing key overwrite
|
||||||
if index.contains_key(&scope) {
|
if index.contains_key(&scope) {
|
||||||
return Err(Error::Overwrite(scope));
|
bail!("Overwrite attempt for existing record `{scope}`")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slot should be free, let check it twice
|
// Slot should be free, let check it twice
|
||||||
match index.insert(scope, profile_identity_id) {
|
match index.insert(scope, profile_identity_id) {
|
||||||
Some(_) => Err(Error::Unexpected),
|
Some(_) => bail!("Unexpected error"),
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cleanup index
|
/// Cleanup index
|
||||||
pub fn clear(&self) -> Result<(), Error> {
|
pub fn clear(&self) -> Result<()> {
|
||||||
let mut index = self.index.borrow_mut();
|
let mut index = self.index.borrow_mut();
|
||||||
index.clear();
|
index.clear();
|
||||||
if index.is_empty() {
|
if index.is_empty() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::Clear)
|
bail!("Could not cleanup memory index")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
use std::fmt::{Display, Formatter, Result};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
Clear,
|
|
||||||
Overwrite(String),
|
|
||||||
Unexpected,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Error {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
|
||||||
match self {
|
|
||||||
Self::Clear => write!(f, "Could not cleanup memory index"),
|
|
||||||
Self::Overwrite(key) => {
|
|
||||||
write!(f, "Overwrite attempt for existing record `{key}`")
|
|
||||||
}
|
|
||||||
Self::Unexpected => write!(f, "Unexpected error"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
use std::fmt::{Display, Formatter, Result};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
Auth(super::auth::Error),
|
|
||||||
Certificate(Box<dyn std::error::Error>),
|
|
||||||
Database(sqlite::Error),
|
|
||||||
Memory(super::memory::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Error {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
|
||||||
match self {
|
|
||||||
Self::Auth(e) => write!(f, "Could not create auth: {e}"),
|
|
||||||
Self::Certificate(e) => {
|
|
||||||
write!(f, "Could not create certificate: {e}")
|
|
||||||
}
|
|
||||||
Self::Database(e) => {
|
|
||||||
write!(f, "Database error: {e}")
|
|
||||||
}
|
|
||||||
Self::Memory(e) => write!(f, "Memory error: {e}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
mod error;
|
use anyhow::{bail, Result};
|
||||||
use error::Error;
|
|
||||||
|
|
||||||
use gtk::gio::TlsCertificate;
|
use gtk::gio::TlsCertificate;
|
||||||
|
|
||||||
/// Gemini identity holder for cached record in application-wide struct format.
|
/// Gemini identity holder for cached record in application-wide struct format.
|
||||||
|
|
@ -12,10 +10,10 @@ pub struct Item {
|
||||||
|
|
||||||
impl Item {
|
impl Item {
|
||||||
/// Convert `Self` to [TlsCertificate](https://docs.gtk.org/gio/class.TlsCertificate.html)
|
/// Convert `Self` to [TlsCertificate](https://docs.gtk.org/gio/class.TlsCertificate.html)
|
||||||
pub fn to_tls_certificate(&self) -> Result<TlsCertificate, Error> {
|
pub fn to_tls_certificate(&self) -> Result<TlsCertificate> {
|
||||||
match TlsCertificate::from_pem(&self.pem) {
|
match TlsCertificate::from_pem(&self.pem) {
|
||||||
Ok(certificate) => Ok(certificate),
|
Ok(certificate) => Ok(certificate),
|
||||||
Err(e) => Err(Error::TlsCertificate(e)),
|
Err(e) => bail!("TLS certificate error: {e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
use gtk::glib;
|
|
||||||
use std::fmt::{Display, Formatter, Result};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
TlsCertificate(glib::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Error {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
|
||||||
match self {
|
|
||||||
Self::TlsCertificate(e) => write!(f, "TLS certificate error: {e}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
pub mod error;
|
use anyhow::{bail, Result};
|
||||||
pub use error::Error;
|
|
||||||
|
|
||||||
use std::{cell::RefCell, collections::HashMap};
|
use std::{cell::RefCell, collections::HashMap};
|
||||||
|
|
||||||
/// Reduce disk usage by cache index in memory
|
/// Reduce disk usage by cache index in memory
|
||||||
|
|
@ -28,38 +26,38 @@ impl Memory {
|
||||||
|
|
||||||
/// Add new record with `id` as key and `pem` as value
|
/// Add new record with `id` as key and `pem` as value
|
||||||
/// * validate record with same key does not exist yet
|
/// * validate record with same key does not exist yet
|
||||||
pub fn add(&self, profile_identity_id: i64, pem: String) -> Result<(), Error> {
|
pub fn add(&self, profile_identity_id: i64, pem: String) -> Result<()> {
|
||||||
// Borrow shared index access
|
// Borrow shared index access
|
||||||
let mut index = self.index.borrow_mut();
|
let mut index = self.index.borrow_mut();
|
||||||
|
|
||||||
// Prevent existing key overwrite
|
// Prevent existing key overwrite
|
||||||
if index.contains_key(&profile_identity_id) {
|
if index.contains_key(&profile_identity_id) {
|
||||||
return Err(Error::Overwrite(profile_identity_id));
|
bail!("Overwrite attempt for existing record `{profile_identity_id}`")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slot should be free, let check it twice
|
// Slot should be free, let check it twice
|
||||||
match index.insert(profile_identity_id, pem) {
|
match index.insert(profile_identity_id, pem) {
|
||||||
Some(_) => Err(Error::Unexpected),
|
Some(_) => bail!("Unexpected error"),
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get `pem` clone by `id` from memory index
|
/// Get `pem` clone by `id` from memory index
|
||||||
pub fn get(&self, id: i64) -> Result<String, Error> {
|
pub fn get(&self, id: i64) -> Result<String> {
|
||||||
match self.index.borrow().get(&id) {
|
match self.index.borrow().get(&id) {
|
||||||
Some(pem) => Ok(pem.clone()),
|
Some(pem) => Ok(pem.clone()),
|
||||||
None => Err(Error::NotFound(id)),
|
None => bail!("Record `{id}` not found in memory index"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cleanup index
|
/// Cleanup index
|
||||||
pub fn clear(&self) -> Result<(), Error> {
|
pub fn clear(&self) -> Result<()> {
|
||||||
let mut index = self.index.borrow_mut();
|
let mut index = self.index.borrow_mut();
|
||||||
index.clear();
|
index.clear();
|
||||||
if index.is_empty() {
|
if index.is_empty() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::Clear)
|
bail!("Could not cleanup memory index")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
use std::fmt::{Display, Formatter, Result};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
Clear,
|
|
||||||
NotFound(i64),
|
|
||||||
Overwrite(i64),
|
|
||||||
Unexpected,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Error {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
|
||||||
match self {
|
|
||||||
Self::Clear => write!(f, "Could not cleanup memory index"),
|
|
||||||
Self::NotFound(key) => {
|
|
||||||
write!(f, "Record `{key}` not found in memory index")
|
|
||||||
}
|
|
||||||
Self::Overwrite(key) => write!(f, "Overwrite attempt for existing record `{key}`"),
|
|
||||||
Self::Unexpected => write!(f, "Unexpected error"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
mod database;
|
mod database;
|
||||||
mod error;
|
|
||||||
mod memory;
|
mod memory;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
use database::Database;
|
use database::Database;
|
||||||
use error::Error;
|
|
||||||
use gtk::glib::Uri;
|
use gtk::glib::Uri;
|
||||||
use memory::Memory;
|
use memory::Memory;
|
||||||
use sqlite::{Connection, Transaction};
|
use sqlite::{Connection, Transaction};
|
||||||
|
|
@ -18,40 +17,30 @@ impl Search {
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
/// Create new `Self`
|
/// Create new `Self`
|
||||||
pub fn build(connection: &Rc<RwLock<Connection>>, profile_id: &Rc<i64>) -> Result<Self, Error> {
|
pub fn build(connection: &Rc<RwLock<Connection>>, profile_id: &Rc<i64>) -> Result<Self> {
|
||||||
match Database::init(connection, profile_id) {
|
let database = Database::init(connection, profile_id)?;
|
||||||
Ok(database) => {
|
|
||||||
// Init fast search index
|
// Init fast search index
|
||||||
let memory = Memory::init();
|
let memory = Memory::init();
|
||||||
|
|
||||||
// Build initial index
|
// Build initial index
|
||||||
index(&database, &memory)?;
|
index(&database, &memory)?;
|
||||||
|
|
||||||
// Return new `Self`
|
// Return new `Self`
|
||||||
Ok(Self { database, memory })
|
Ok(Self { database, memory })
|
||||||
}
|
}
|
||||||
Err(e) => Err(Error::Database(e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
|
|
||||||
/// Add new search provider record
|
/// Add new search provider record
|
||||||
/// * requires valid [Uri](https://docs.gtk.org/glib/struct.Uri.html)
|
/// * requires valid [Uri](https://docs.gtk.org/glib/struct.Uri.html)
|
||||||
pub fn add(&self, query: &Uri, is_default: bool) -> Result<(), Error> {
|
pub fn add(&self, query: &Uri, is_default: bool) -> Result<()> {
|
||||||
match self.database.add(query.to_string(), is_default) {
|
self.database.add(query.to_string(), is_default)?;
|
||||||
Ok(_) => index(&self.database, &self.memory),
|
Ok(())
|
||||||
Err(e) => Err(Error::Database(e)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add new search provider record
|
/// Add new search provider record
|
||||||
/// * requires valid [Uri](https://docs.gtk.org/glib/struct.Uri.html)
|
/// * requires valid [Uri](https://docs.gtk.org/glib/struct.Uri.html)
|
||||||
pub fn set_default(&self, profile_search_id: i64) -> Result<(), Error> {
|
pub fn set_default(&self, profile_search_id: i64) -> Result<()> {
|
||||||
match self.database.set_default(profile_search_id) {
|
self.database.set_default(profile_search_id)?;
|
||||||
Ok(_) => index(&self.database, &self.memory),
|
index(&self.database, &self.memory)
|
||||||
Err(e) => Err(Error::Database(e)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get records from the memory index
|
/// Get records from the memory index
|
||||||
|
|
@ -60,11 +49,9 @@ impl Search {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete record from `database` and `memory` index
|
/// Delete record from `database` and `memory` index
|
||||||
pub fn delete(&self, id: i64) -> Result<(), Error> {
|
pub fn delete(&self, id: i64) -> Result<()> {
|
||||||
match self.database.delete(id) {
|
self.database.delete(id)?;
|
||||||
Ok(_) => index(&self.database, &self.memory),
|
index(&self.database, &self.memory)
|
||||||
Err(e) => Err(Error::Database(e)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
|
|
@ -77,11 +64,9 @@ impl Search {
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
|
|
||||||
pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
pub fn migrate(tx: &Transaction) -> Result<()> {
|
||||||
// Migrate self components
|
// Migrate self components
|
||||||
if let Err(e) = database::init(tx) {
|
database::init(tx)?;
|
||||||
return Err(e.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate migration to childs
|
// Delegate migration to childs
|
||||||
// nothing yet..
|
// nothing yet..
|
||||||
|
|
@ -91,15 +76,10 @@ pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sync memory index with database
|
/// Sync memory index with database
|
||||||
fn index(database: &Database, memory: &Memory) -> Result<(), Error> {
|
fn index(database: &Database, memory: &Memory) -> Result<()> {
|
||||||
memory.clear();
|
memory.clear();
|
||||||
match database.records() {
|
for record in database.records()? {
|
||||||
Ok(records) => {
|
|
||||||
for record in records {
|
|
||||||
memory.push(record.id, record.query, record.is_default)
|
memory.push(record.id, record.query, record.is_default)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Err(e) => return Err(Error::Database(e)),
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use sqlite::{Connection, Error, Transaction};
|
use anyhow::Result;
|
||||||
|
use sqlite::{Connection, Transaction};
|
||||||
use std::{rc::Rc, sync::RwLock};
|
use std::{rc::Rc, sync::RwLock};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
@ -18,7 +19,7 @@ impl Database {
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
/// Create new `Self`
|
/// Create new `Self`
|
||||||
pub fn init(connection: &Rc<RwLock<Connection>>, profile_id: &Rc<i64>) -> Result<Self, Error> {
|
pub fn init(connection: &Rc<RwLock<Connection>>, profile_id: &Rc<i64>) -> Result<Self> {
|
||||||
let mut writable = connection.write().unwrap(); // @TODO handle
|
let mut writable = connection.write().unwrap(); // @TODO handle
|
||||||
let tx = writable.transaction()?;
|
let tx = writable.transaction()?;
|
||||||
|
|
||||||
|
|
@ -38,7 +39,7 @@ impl Database {
|
||||||
// Getters
|
// Getters
|
||||||
|
|
||||||
/// Get records from database
|
/// Get records from database
|
||||||
pub fn records(&self) -> Result<Vec<Row>, Error> {
|
pub fn records(&self) -> Result<Vec<Row>> {
|
||||||
let readable = self.connection.read().unwrap(); // @TODO handle
|
let readable = self.connection.read().unwrap(); // @TODO handle
|
||||||
let tx = readable.unchecked_transaction()?;
|
let tx = readable.unchecked_transaction()?;
|
||||||
select(&tx, *self.profile_id)
|
select(&tx, *self.profile_id)
|
||||||
|
|
@ -48,30 +49,19 @@ impl Database {
|
||||||
|
|
||||||
/// Create new record in database
|
/// Create new record in database
|
||||||
/// * return last insert ID on success
|
/// * return last insert ID on success
|
||||||
pub fn add(&self, query: String, is_default: bool) -> Result<i64, Error> {
|
pub fn add(&self, query: String, is_default: bool) -> Result<i64> {
|
||||||
// Begin new transaction
|
|
||||||
let mut writable = self.connection.write().unwrap(); // @TODO handle
|
let mut writable = self.connection.write().unwrap(); // @TODO handle
|
||||||
let tx = writable.transaction()?;
|
let tx = writable.transaction()?;
|
||||||
|
|
||||||
// Create new record
|
|
||||||
if is_default {
|
if is_default {
|
||||||
// make sure only one default provider in set
|
|
||||||
reset(&tx, *self.profile_id, !is_default)?;
|
reset(&tx, *self.profile_id, !is_default)?;
|
||||||
}
|
}
|
||||||
insert(&tx, *self.profile_id, query, is_default)?;
|
let id = insert(&tx, *self.profile_id, query, is_default)?;
|
||||||
|
tx.commit()?;
|
||||||
// Hold insert ID for result
|
Ok(id)
|
||||||
let id = last_insert_id(&tx);
|
|
||||||
|
|
||||||
// Done
|
|
||||||
match tx.commit() {
|
|
||||||
Ok(_) => Ok(id),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete record from database
|
/// Delete record from database
|
||||||
pub fn delete(&self, id: i64) -> Result<(), Error> {
|
pub fn delete(&self, id: i64) -> Result<()> {
|
||||||
// Begin new transaction
|
// Begin new transaction
|
||||||
let mut writable = self.connection.write().unwrap(); // @TODO
|
let mut writable = self.connection.write().unwrap(); // @TODO
|
||||||
let tx = writable.transaction()?;
|
let tx = writable.transaction()?;
|
||||||
|
|
@ -100,11 +90,12 @@ impl Database {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Done
|
// Done
|
||||||
tx.commit()
|
tx.commit()?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete record from database
|
/// Delete record from database
|
||||||
pub fn set_default(&self, id: i64) -> Result<(), Error> {
|
pub fn set_default(&self, id: i64) -> Result<()> {
|
||||||
// Begin new transaction
|
// Begin new transaction
|
||||||
let mut writable = self.connection.write().unwrap(); // @TODO
|
let mut writable = self.connection.write().unwrap(); // @TODO
|
||||||
let tx = writable.transaction()?;
|
let tx = writable.transaction()?;
|
||||||
|
|
@ -114,14 +105,15 @@ impl Database {
|
||||||
|
|
||||||
// Delete record by ID
|
// Delete record by ID
|
||||||
set_default(&tx, *self.profile_id, id, true)?;
|
set_default(&tx, *self.profile_id, id, true)?;
|
||||||
tx.commit()
|
tx.commit()?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Low-level DB API
|
// Low-level DB API
|
||||||
|
|
||||||
pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
pub fn init(tx: &Transaction) -> Result<usize> {
|
||||||
tx.execute(
|
Ok(tx.execute(
|
||||||
"CREATE TABLE IF NOT EXISTS `profile_search`
|
"CREATE TABLE IF NOT EXISTS `profile_search`
|
||||||
(
|
(
|
||||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
|
@ -132,15 +124,10 @@ pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
||||||
FOREIGN KEY (`profile_id`) REFERENCES `profile` (`id`)
|
FOREIGN KEY (`profile_id`) REFERENCES `profile` (`id`)
|
||||||
)",
|
)",
|
||||||
[],
|
[],
|
||||||
)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert(
|
fn insert(tx: &Transaction, profile_id: i64, query: String, is_default: bool) -> Result<i64> {
|
||||||
tx: &Transaction,
|
|
||||||
profile_id: i64,
|
|
||||||
query: String,
|
|
||||||
is_default: bool,
|
|
||||||
) -> Result<usize, Error> {
|
|
||||||
tx.execute(
|
tx.execute(
|
||||||
"INSERT INTO `profile_search` (
|
"INSERT INTO `profile_search` (
|
||||||
`profile_id`,
|
`profile_id`,
|
||||||
|
|
@ -148,10 +135,11 @@ fn insert(
|
||||||
`query`
|
`query`
|
||||||
) VALUES (?, ?, ?)",
|
) VALUES (?, ?, ?)",
|
||||||
(profile_id, is_default, query),
|
(profile_id, is_default, query),
|
||||||
)
|
)?;
|
||||||
|
Ok(tx.last_insert_rowid())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select(tx: &Transaction, profile_id: i64) -> Result<Vec<Row>, Error> {
|
fn select(tx: &Transaction, profile_id: i64) -> Result<Vec<Row>> {
|
||||||
let mut stmt = tx.prepare(
|
let mut stmt = tx.prepare(
|
||||||
"SELECT `id`, `profile_id`, `is_default`, `query`
|
"SELECT `id`, `profile_id`, `is_default`, `query`
|
||||||
FROM `profile_search`
|
FROM `profile_search`
|
||||||
|
|
@ -177,35 +165,26 @@ fn select(tx: &Transaction, profile_id: i64) -> Result<Vec<Row>, Error> {
|
||||||
Ok(records)
|
Ok(records)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete(tx: &Transaction, id: i64) -> Result<usize, Error> {
|
fn delete(tx: &Transaction, id: i64) -> Result<usize> {
|
||||||
tx.execute("DELETE FROM `profile_search` WHERE `id` = ?", [id])
|
Ok(tx.execute("DELETE FROM `profile_search` WHERE `id` = ?", [id])?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(tx: &Transaction, profile_id: i64, is_default: bool) -> Result<usize, Error> {
|
fn reset(tx: &Transaction, profile_id: i64, is_default: bool) -> Result<usize> {
|
||||||
tx.execute(
|
Ok(tx.execute(
|
||||||
"UPDATE `profile_search` SET `is_default` = ? WHERE `profile_id` = ?",
|
"UPDATE `profile_search` SET `is_default` = ? WHERE `profile_id` = ?",
|
||||||
(is_default, profile_id),
|
(is_default, profile_id),
|
||||||
)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_default(
|
fn set_default(tx: &Transaction, profile_id: i64, id: i64, is_default: bool) -> Result<usize> {
|
||||||
tx: &Transaction,
|
Ok(tx.execute(
|
||||||
profile_id: i64,
|
|
||||||
id: i64,
|
|
||||||
is_default: bool,
|
|
||||||
) -> Result<usize, Error> {
|
|
||||||
tx.execute(
|
|
||||||
"UPDATE `profile_search` SET `is_default` = ? WHERE `profile_id` = ? AND `id` = ?",
|
"UPDATE `profile_search` SET `is_default` = ? WHERE `profile_id` = ? AND `id` = ?",
|
||||||
(is_default, profile_id, id),
|
(is_default, profile_id, id),
|
||||||
)
|
)?)
|
||||||
}
|
|
||||||
|
|
||||||
fn last_insert_id(tx: &Transaction) -> i64 {
|
|
||||||
tx.last_insert_rowid()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Init default search providers list for given profile
|
/// Init default search providers list for given profile
|
||||||
fn add_defaults(tx: &Transaction, profile_id: i64) -> Result<(), Error> {
|
fn add_defaults(tx: &Transaction, profile_id: i64) -> Result<()> {
|
||||||
for (provider, is_default) in &[
|
for (provider, is_default) in &[
|
||||||
("gemini://tlgs.one/search/search", true),
|
("gemini://tlgs.one/search/search", true),
|
||||||
("gemini://kennedy.gemi.dev/search", false),
|
("gemini://kennedy.gemi.dev/search", false),
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
use std::fmt::{Display, Formatter, Result};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
Database(sqlite::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Error {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
|
||||||
match self {
|
|
||||||
Self::Database(e) => {
|
|
||||||
write!(f, "Database error: {e}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue