refonte
This commit is contained in:
@ -1,6 +1,5 @@
|
||||
use crate::models::{ClipboardData, ClipboardEntry, Image};
|
||||
use image::codecs::jpeg::JpegEncoder;
|
||||
use image::codecs::png::PngEncoder;
|
||||
use image::{ExtendedColorType, ImageEncoder};
|
||||
use rusqlite::Connection;
|
||||
use std::error::Error;
|
||||
@ -17,23 +16,32 @@ pub struct Database {
|
||||
impl Database {
|
||||
pub fn init(dir_path: &str) -> Result<Self, Box<dyn Error>> {
|
||||
let base_path = Path::new(dir_path);
|
||||
let images_path = base_path.join("images");
|
||||
std::fs::create_dir_all(&images_path)?;
|
||||
fs::create_dir_all(base_path.join("images"))?;
|
||||
|
||||
let db_path = base_path.join("clipboard.db");
|
||||
let conn = Connection::open(base_path.join("clipboard.db"))?;
|
||||
|
||||
let conn = Connection::open(&db_path)?;
|
||||
conn.execute_batch(
|
||||
"CREATE TABLE IF NOT EXISTS schema_version (version INTEGER NOT NULL);
|
||||
INSERT OR IGNORE INTO schema_version (version) SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM schema_version);",
|
||||
)?;
|
||||
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS history (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
type TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
timestamp INTEGER NOT NULL
|
||||
)",
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
type TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
timestamp INTEGER NOT NULL
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
conn.execute_batch(
|
||||
"DELETE FROM history WHERE id NOT IN (
|
||||
SELECT MAX(id) FROM history GROUP BY content
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_history_content ON history(content);",
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
conn,
|
||||
dir_path: dir_path.to_string(),
|
||||
@ -41,61 +49,62 @@ impl Database {
|
||||
}
|
||||
|
||||
pub fn append(&self, entry: ClipboardEntry) -> Result<(), Box<dyn Error>> {
|
||||
let timestamp_millis = entry.timestamp.duration_since(UNIX_EPOCH)?.as_millis() as i64;
|
||||
let ts = entry.timestamp.duration_since(UNIX_EPOCH)?.as_millis() as i64;
|
||||
|
||||
let (entry_type, content) = match &entry.content {
|
||||
ClipboardData::Text(text) => ("text", text.clone()),
|
||||
let (kind, content) = match &entry.content {
|
||||
ClipboardData::Text(t) => {
|
||||
if t.trim().is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
("text", t.clone())
|
||||
}
|
||||
ClipboardData::Image(img) => {
|
||||
if let Some(raw_pixels) = &img.raw_pixels {
|
||||
let img_path = img.file_path(&self.dir_path);
|
||||
|
||||
let file = fs::File::create(&img_path)?;
|
||||
let rgb_pixels: Vec<u8> = raw_pixels
|
||||
if let Some(px) = &img.raw_pixels {
|
||||
let path = img.file_path(&self.dir_path);
|
||||
let file = fs::File::create(&path)?;
|
||||
let rgb: Vec<u8> = px
|
||||
.chunks_exact(4)
|
||||
.flat_map(|rgba| [rgba[0], rgba[1], rgba[2]])
|
||||
.collect();
|
||||
let encoder = JpegEncoder::new_with_quality(file, 70);
|
||||
encoder.write_image(
|
||||
&rgb_pixels,
|
||||
JpegEncoder::new_with_quality(file, 70).write_image(
|
||||
&rgb,
|
||||
img.width,
|
||||
img.height,
|
||||
ExtendedColorType::Rgb8,
|
||||
)?;
|
||||
}
|
||||
("image", img.id.to_string())
|
||||
("image", format!("{}.jpg", img.id))
|
||||
}
|
||||
};
|
||||
|
||||
self.conn.execute(
|
||||
"INSERT INTO history (type, content, timestamp) VALUES (?1, ?2, ?3)",
|
||||
(entry_type, content, timestamp_millis),
|
||||
"INSERT OR REPLACE INTO history (type, content, timestamp) VALUES (?1, ?2, ?3)",
|
||||
(kind, &content, ts),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_history(&self) -> Result<Vec<ClipboardEntry>, Box<dyn Error>> {
|
||||
let mut stmt = self
|
||||
.conn
|
||||
.prepare("SELECT type, content, timestamp FROM history ORDER BY timestamp ASC")?;
|
||||
pub fn read_history(&self, limit: usize) -> Result<Vec<ClipboardEntry>, Box<dyn Error>> {
|
||||
let mut stmt = self.conn.prepare(
|
||||
"SELECT type, content, timestamp FROM history ORDER BY timestamp DESC LIMIT ?1",
|
||||
)?;
|
||||
|
||||
let rows = stmt.query_map([], |row| {
|
||||
let ty: String = row.get(0)?;
|
||||
let content: String = row.get(1)?;
|
||||
let timestamp: i64 = row.get(2)?;
|
||||
Ok((ty, content, timestamp))
|
||||
let rows = stmt.query_map([limit as i64], |row| {
|
||||
Ok((
|
||||
row.get::<_, String>(0)?,
|
||||
row.get::<_, String>(1)?,
|
||||
row.get::<_, i64>(2)?,
|
||||
))
|
||||
})?;
|
||||
|
||||
let mut entries = Vec::new();
|
||||
|
||||
for row in rows {
|
||||
let (ty, content, timestamp) = row?;
|
||||
|
||||
let timestamp = UNIX_EPOCH + Duration::from_millis(timestamp as u64);
|
||||
let (ty, content, ts_ms) = row?;
|
||||
let timestamp = UNIX_EPOCH + Duration::from_millis(ts_ms as u64);
|
||||
let data = if ty == "text" {
|
||||
ClipboardData::Text(content)
|
||||
} else {
|
||||
let id = Uuid::parse_str(&content)?;
|
||||
let id = Uuid::parse_str(content.trim_end_matches(".jpg"))?;
|
||||
ClipboardData::Image(Image {
|
||||
id,
|
||||
raw_pixels: None,
|
||||
@ -103,13 +112,11 @@ impl Database {
|
||||
height: 0,
|
||||
})
|
||||
};
|
||||
|
||||
entries.push(ClipboardEntry {
|
||||
content: data,
|
||||
timestamp,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
@ -118,4 +125,27 @@ impl Database {
|
||||
.execute("DELETE FROM history WHERE content = ?1", [content])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_entry_content(&self, old: &str, new: &str) -> Result<(), Box<dyn Error>> {
|
||||
let rows_affected = self.conn.execute(
|
||||
"UPDATE history SET content = ?1 WHERE content = ?2",
|
||||
[new, old],
|
||||
)?;
|
||||
if rows_affected == 0 {
|
||||
return Err(format!("Entrée introuvable : {old}").into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn clear_history(&self) -> Result<(), Box<dyn Error>> {
|
||||
let images_dir = Path::new(&self.dir_path).join("images");
|
||||
|
||||
if images_dir.exists() {
|
||||
fs::remove_dir_all(&images_dir)?;
|
||||
}
|
||||
fs::create_dir_all(&images_dir)?;
|
||||
|
||||
self.conn.execute("DELETE FROM history", [])?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user