From 937220509ac254221e2ce79aa665f418664d0d85 Mon Sep 17 00:00:00 2001 From: wiiznokes <78230769+wiiznokes@users.noreply.github.com> Date: Fri, 19 Jul 2024 16:08:41 +0200 Subject: [PATCH 1/2] aa --- src/clipboard.rs | 53 ++++++++++------------- src/db.rs | 110 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 98 insertions(+), 65 deletions(-) diff --git a/src/clipboard.rs b/src/clipboard.rs index 8b89ac5..e99bced 100644 --- a/src/clipboard.rs +++ b/src/clipboard.rs @@ -13,7 +13,10 @@ use std::{ use cosmic::iced::{futures::SinkExt, subscription, Subscription}; use tokio::sync::mpsc; -use wl_clipboard_rs::{copy, paste_watch}; +use wl_clipboard_rs::{ + copy::{self, MimeSource}, + paste_watch, +}; use crate::config::PRIVATE_MODE; use crate::db::Entry; @@ -140,16 +143,7 @@ pub fn sub() -> Subscription { debug!("metadata = {}", metadata); - #[allow(clippy::assigning_clones)] - if mimitype == "text/html" { - if let Some(alt) = find_alt(&metadata) { - metadata = alt.to_owned(); - } - } - - debug!("final metadata = {}", metadata); - - Some(metadata) + Some((mimitype, metadata)) } else { None }; @@ -189,31 +183,28 @@ pub fn sub() -> Subscription { ) } -// currently best effort -fn find_alt(html: &str) -> Option<&str> { - const DEB: &str = "alt=\""; - - if let Some(pos) = html.find(DEB) { - const OFFSET: usize = DEB.as_bytes().len(); - - if let Some(pos_end) = html[pos + OFFSET..].find('"') { - return Some(&html[pos + OFFSET..pos + pos_end + OFFSET]); - } - } +pub fn copy(data: Entry) -> Result<(), copy::Error> { + debug!("copy {:?}", data); - None -} + let mut sources = Vec::with_capacity(if data.metadata.is_some() { 2 } else { 1 }); -pub fn copy(data: Entry) -> Result<(), copy::Error> { - //dbg!("copy", &data); - let options = copy::Options::default(); - let bytes = data.content.into_boxed_slice(); + let source = MimeSource { + source: copy::Source::Bytes(data.content.into_boxed_slice()), + mime_type: copy::MimeType::Specific(data.mime), + }; - let source = copy::Source::Bytes(bytes); + sources.push(source); - let mime_type = copy::MimeType::Specific(data.mime); + if let Some((mime, content)) = data.metadata { + let source = MimeSource { + source: copy::Source::Bytes(content.into_boxed_str().into_boxed_bytes()), + mime_type: copy::MimeType::Specific(mime), + }; + sources.push(source); + } - wl_clipboard_rs::copy::copy(options, source, mime_type)?; + let options = copy::Options::default(); + wl_clipboard_rs::copy::copy_multi(options, sources)?; Ok(()) } diff --git a/src/db.rs b/src/db.rs index b8bcee0..0d6c7e4 100644 --- a/src/db.rs +++ b/src/db.rs @@ -20,7 +20,9 @@ use nucleo::{ use chrono::Utc; use mime::Mime; -use rusqlite::{named_params, params, Connection, ErrorCode, OpenFlags, OptionalExtension, Row}; +use rusqlite::{ + ffi::sqlite3, named_params, params, Connection, ErrorCode, OpenFlags, OptionalExtension, Row, +}; use crate::{ app::{APP, APPID, ORG, QUALIFIER}, @@ -30,7 +32,7 @@ use crate::{ type TimeId = i64; -const DB_VERSION: &str = "2"; +const DB_VERSION: &str = "3"; const DB_PATH: &str = constcat::concat!(APPID, "-db-", DB_VERSION, ".sqlite"); // warning: if you change somethings in here, change the db version @@ -51,7 +53,8 @@ pub struct Entry { #[derivative(PartialEq = "ignore")] #[derivative(Hash = "ignore")] - pub metadata: Option, + /// (Mime, Content) + pub metadata: Option<(String, String)>, } impl Entry { @@ -61,7 +64,12 @@ impl Entry { hasher.finish() } - pub fn new(creation: i64, mime: String, content: Vec, metadata: Option) -> Self { + pub fn new( + creation: i64, + mime: String, + content: Vec, + metadata: Option<(String, String)>, + ) -> Self { Self { creation, mime, @@ -70,9 +78,21 @@ impl Entry { } } - pub fn new_now(mime: String, content: Vec, metadata: Option) -> Self { + pub fn new_now(mime: String, content: Vec, metadata: Option<(String, String)>) -> Self { Self::new(Utc::now().timestamp_millis(), mime, content, metadata) } + + // SELECT creation, mime, content, metadataMime, metadata + fn from_row(row: &Row) -> rusqlite::Result { + Ok(Entry::new( + row.get(0)?, + row.get(1)?, + row.get(2)?, + row.get(3) + .ok() + .map(|metadata_mime| (metadata_mime, row.get(4).unwrap())), + )) + } } impl Debug for Entry { @@ -125,15 +145,40 @@ impl Entry { bail!("unsupported mime type {}", self.mime) } - pub fn get_text(&self) -> Option<&str> { + fn get_searchable_text(&self) -> Option<&str> { if self.mime.starts_with("text/") { return core::str::from_utf8(&self.content).ok(); } - return self.metadata.as_deref(); + if let Some((mime, metadata)) = &self.metadata { + #[allow(clippy::assigning_clones)] + if mime == "text/html" { + if let Some(alt) = find_alt(metadata) { + return Some(alt); + } + } + return Some(metadata); + } + + None } } +// currently best effort +fn find_alt(html: &str) -> Option<&str> { + const DEB: &str = "alt=\""; + + if let Some(pos) = html.find(DEB) { + const OFFSET: usize = DEB.as_bytes().len(); + + if let Some(pos_end) = html[pos + OFFSET..].find('"') { + return Some(&html[pos + OFFSET..pos + pos_end + OFFSET]); + } + } + + None +} + pub struct Db { conn: Connection, hashs: HashMap, @@ -165,11 +210,13 @@ impl Db { )?; let query_create_table = r#" - CREATE TABLE data ( + CREATE TABLE ClipboardEntries ( creation INTEGER PRIMARY KEY, mime TEXT NOT NULL, content BLOB NOT NULL, - metadata TEXT + metadataMime TEXT, + metadata TEXT, + CHECK ((metadataMime IS NULL AND metadata IS NULL) OR (metadataMime IS NOT NULL AND metadata IS NOT NULL)) ) "#; @@ -190,7 +237,7 @@ impl Db { if let Some(max_duration) = &config.maximum_entries_lifetime { let query_delete_old_one = r#" - DELETE FROM data + DELETE FROM ClipboardEntries WHERE (:now - creation) >= :max; "#; @@ -205,9 +252,9 @@ impl Db { if let Some(max_number_of_entries) = &config.maximum_entries_number { let query_delete_old_one = r#" - DELETE FROM data + DELETE FROM ClipboardEntries WHERE creation NOT IN - (SELECT creation FROM data + (SELECT creation FROM ClipboardEntries ORDER BY creation DESC LIMIT :max_number_of_entries); "#; @@ -224,8 +271,8 @@ impl Db { let mut state = BTreeMap::default(); let query_load_table = r#" - SELECT creation, mime, content, metadata - FROM data + SELECT creation, mime, content, metadataMime, metadata + FROM ClipboardEntries "#; { @@ -234,7 +281,8 @@ impl Db { let mut rows = stmt.query(())?; while let Some(row) = rows.next()? { - let data = Entry::new(row.get(0)?, row.get(1)?, row.get(2)?, row.get(3).ok()); + let data = Entry::from_row(row)?; + hashs.insert(data.get_hash(), data.creation); state.insert(data.creation, data); } @@ -255,22 +303,15 @@ impl Db { fn get_last_row(&mut self) -> Result> { let query_get_last_row = r#" - SELECT creation, mime, content, metadata - FROM data + SELECT creation, mime, content, metadataMime, metadata + FROM ClipboardEntries ORDER BY creation DESC LIMIT 1 "#; let data = self .conn - .query_row(query_get_last_row, (), |row| { - Ok(Entry::new( - row.get(0)?, - row.get(1)?, - row.get(2)?, - row.get(3).ok(), - )) - }) + .query_row(query_get_last_row, (), Entry::from_row) .optional()?; Ok(data) @@ -282,13 +323,13 @@ impl Db { // insert a new data, only if the last row is not the same AND was not created recently let query_insert_if_not_exist = r#" WITH last_row AS ( - SELECT creation, mime, content, metadata - FROM data + SELECT creation, mime, content, metadataMime, metadata + FROM ClipboardEntries ORDER BY creation DESC LIMIT 1 ) - INSERT INTO data (creation, mime, content, metadata) - SELECT :new_id, :new_mime, :new_content, :new_metadata + INSERT INTO ClipboardEntries (creation, mime, content, metadataMime, metadata) + SELECT :new_id, :new_mime, :new_content, new_metadata_mime, :new_metadata WHERE NOT EXISTS ( SELECT 1 FROM last_row AS lr @@ -302,7 +343,8 @@ impl Db { ":new_id": data.creation, ":new_mime": data.mime, ":new_content": data.content, - ":new_metadata": data.metadata, + ":new_metadata_mime": data.metadata.as_ref().map(|m| &m.0), + ":new_metadata": data.metadata.as_ref().map(|m| &m.1), ":now": utils::now_millis(), }, ) { @@ -328,7 +370,7 @@ impl Db { // we don't want to remove the old_id if last_row.creation != old_id { let query_delete_old_id = r#" - DELETE FROM data + DELETE FROM ClipboardEntries WHERE creation = ?1; "#; @@ -347,7 +389,7 @@ impl Db { pub fn delete(&mut self, data: &Entry) -> Result<()> { let query = r#" - DELETE FROM data + DELETE FROM ClipboardEntries WHERE creation = ?1; "#; @@ -362,7 +404,7 @@ impl Db { pub fn clear(&mut self) -> Result<()> { let query_delete = r#" - DELETE FROM data + DELETE FROM ClipboardEntries "#; self.conn.execute(query_delete, [])?; @@ -382,7 +424,7 @@ impl Db { .iter() .rev() .filter_map(|(id, data)| { - data.get_text().and_then(|text| { + data.get_searchable_text().and_then(|text| { let mut buf = Vec::new(); let haystack = Utf32Str::new(text, &mut buf); From 65af39918ee76da03ed5e008c51e162d78c459d5 Mon Sep 17 00:00:00 2001 From: wiiznokes <78230769+wiiznokes@users.noreply.github.com> Date: Fri, 19 Jul 2024 16:11:45 +0200 Subject: [PATCH 2/2] Update db.rs --- src/db.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db.rs b/src/db.rs index 0d6c7e4..457406f 100644 --- a/src/db.rs +++ b/src/db.rs @@ -329,7 +329,7 @@ impl Db { LIMIT 1 ) INSERT INTO ClipboardEntries (creation, mime, content, metadataMime, metadata) - SELECT :new_id, :new_mime, :new_content, new_metadata_mime, :new_metadata + SELECT :new_id, :new_mime, :new_content, :new_metadata_mime, :new_metadata WHERE NOT EXISTS ( SELECT 1 FROM last_row AS lr