mirror of
https://github.com/NotAShelf/stash.git
synced 2026-04-12 14:07:42 +00:00
treewide: make logging format more consistent; make clipboard persistence opt-in
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I9092f93c29fcbe99c90483875f4acd0c6a6a6964
This commit is contained in:
parent
9702e67599
commit
da9bf5ea3e
11 changed files with 44 additions and 32 deletions
|
|
@ -175,7 +175,7 @@ unsafe fn fork_and_serve(prepared: PreparedCopy) -> PersistenceResult<()> {
|
||||||
|
|
||||||
pid => {
|
pid => {
|
||||||
// Parent process, store child PID for loop detection
|
// Parent process, store child PID for loop detection
|
||||||
log::debug!("Forked clipboard persistence process (pid: {pid})");
|
log::debug!("forked clipboard persistence process (pid: {pid})");
|
||||||
SERVING_PID.store(pid, Ordering::SeqCst);
|
SERVING_PID.store(pid, Ordering::SeqCst);
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|
|
@ -185,18 +185,18 @@ unsafe fn fork_and_serve(prepared: PreparedCopy) -> PersistenceResult<()> {
|
||||||
/// Child process entry point for serving clipboard data.
|
/// Child process entry point for serving clipboard data.
|
||||||
fn serve_clipboard_child(prepared: PreparedCopy) {
|
fn serve_clipboard_child(prepared: PreparedCopy) {
|
||||||
let pid = std::process::id() as i32;
|
let pid = std::process::id() as i32;
|
||||||
log::debug!("Clipboard persistence child process started (pid: {pid})");
|
log::debug!("clipboard persistence child process started (pid: {pid})");
|
||||||
|
|
||||||
// Serve clipboard requests. The PreparedCopy::serve() method blocks and
|
// Serve clipboard requests. The PreparedCopy::serve() method blocks and
|
||||||
// handles all the Wayland protocol interactions internally via
|
// handles all the Wayland protocol interactions internally via
|
||||||
// wl-clipboard-rs
|
// wl-clipboard-rs
|
||||||
match prepared.serve() {
|
match prepared.serve() {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
log::debug!("Clipboard persistence: serve completed normally");
|
log::debug!("clipboard persistence: serve completed normally");
|
||||||
},
|
},
|
||||||
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Clipboard persistence: serve failed: {e}");
|
log::error!("clipboard persistence: serve failed: {e}");
|
||||||
exit(1);
|
exit(1);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ impl DecodeCommand for SqliteClipboardDb {
|
||||||
|
|
||||||
// If input is empty or whitespace, treat as error and trigger fallback
|
// If input is empty or whitespace, treat as error and trigger fallback
|
||||||
if input_str.trim().is_empty() {
|
if input_str.trim().is_empty() {
|
||||||
log::debug!("No input provided to decode; relaying clipboard to stdout");
|
log::debug!("no input provided to decode; relaying clipboard to stdout");
|
||||||
if let Ok((mut reader, _mime)) =
|
if let Ok((mut reader, _mime)) =
|
||||||
get_contents(ClipboardType::Regular, Seat::Unspecified, MimeType::Any)
|
get_contents(ClipboardType::Regular, Seat::Unspecified, MimeType::Any)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ pub trait DeleteCommand {
|
||||||
impl DeleteCommand for SqliteClipboardDb {
|
impl DeleteCommand for SqliteClipboardDb {
|
||||||
fn delete(&self, input: impl Read) -> Result<usize, StashError> {
|
fn delete(&self, input: impl Read) -> Result<usize, StashError> {
|
||||||
let deleted = self.delete_entries(input)?;
|
let deleted = self.delete_entries(input)?;
|
||||||
log::info!("Deleted {deleted} entries");
|
log::info!("deleted {deleted} entries");
|
||||||
Ok(deleted)
|
Ok(deleted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,11 +55,11 @@ impl ImportCommand for SqliteClipboardDb {
|
||||||
imported += 1;
|
imported += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!("Imported {imported} records from TSV into SQLite database.");
|
log::info!("imported {imported} records from TSV into SQLite database.");
|
||||||
|
|
||||||
// Trim database to max_items after import
|
// Trim database to max_items after import
|
||||||
self.trim_db(max_items)?;
|
self.trim_db(max_items)?;
|
||||||
log::info!("Trimmed clipboard database to max_items = {max_items}");
|
log::info!("trimmed clipboard database to max_items = {max_items}");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -710,7 +710,7 @@ impl SqliteClipboardDb {
|
||||||
.show();
|
.show();
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Failed to copy entry to clipboard: {e}");
|
log::error!("failed to copy entry to clipboard: {e}");
|
||||||
let _ = Notification::new()
|
let _ = Notification::new()
|
||||||
.summary("Stash")
|
.summary("Stash")
|
||||||
.body(&format!("Failed to copy to clipboard: {e}"))
|
.body(&format!("Failed to copy to clipboard: {e}"))
|
||||||
|
|
@ -719,7 +719,7 @@ impl SqliteClipboardDb {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Failed to fetch entry {id}: {e}");
|
log::error!("failed to fetch entry {id}: {e}");
|
||||||
let _ = Notification::new()
|
let _ = Notification::new()
|
||||||
.summary("Stash")
|
.summary("Stash")
|
||||||
.body(&format!("Failed to fetch entry: {e}"))
|
.body(&format!("Failed to fetch entry: {e}"))
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ impl StoreCommand for SqliteClipboardDb {
|
||||||
) -> Result<(), crate::db::StashError> {
|
) -> Result<(), crate::db::StashError> {
|
||||||
if let Some("sensitive" | "clear") = state.as_deref() {
|
if let Some("sensitive" | "clear") = state.as_deref() {
|
||||||
self.delete_last()?;
|
self.delete_last()?;
|
||||||
log::info!("Entry deleted");
|
log::info!("entry deleted");
|
||||||
} else {
|
} else {
|
||||||
self.store_entry(
|
self.store_entry(
|
||||||
input,
|
input,
|
||||||
|
|
@ -41,7 +41,7 @@ impl StoreCommand for SqliteClipboardDb {
|
||||||
None, // no pre-computed hash for CLI store
|
None, // no pre-computed hash for CLI store
|
||||||
None, // no mime types for CLI store
|
None, // no mime types for CLI store
|
||||||
)?;
|
)?;
|
||||||
log::info!("Entry stored");
|
log::info!("entry stored");
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -204,6 +204,7 @@ pub trait WatchCommand {
|
||||||
mime_type_preference: &str,
|
mime_type_preference: &str,
|
||||||
min_size: Option<usize>,
|
min_size: Option<usize>,
|
||||||
max_size: usize,
|
max_size: usize,
|
||||||
|
persist: bool,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,6 +218,7 @@ impl WatchCommand for SqliteClipboardDb {
|
||||||
mime_type_preference: &str,
|
mime_type_preference: &str,
|
||||||
min_size: Option<usize>,
|
min_size: Option<usize>,
|
||||||
max_size: usize,
|
max_size: usize,
|
||||||
|
persist: bool,
|
||||||
) {
|
) {
|
||||||
let async_db = AsyncClipboardDb::new(self.db_path.clone());
|
let async_db = AsyncClipboardDb::new(self.db_path.clone());
|
||||||
log::info!(
|
log::info!(
|
||||||
|
|
@ -224,6 +226,10 @@ impl WatchCommand for SqliteClipboardDb {
|
||||||
{mime_type_preference}"
|
{mime_type_preference}"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if persist {
|
||||||
|
log::info!("clipboard persistence enabled");
|
||||||
|
}
|
||||||
|
|
||||||
// Build expiration queue from existing entries
|
// Build expiration queue from existing entries
|
||||||
let mut exp_queue = ExpirationQueue::new();
|
let mut exp_queue = ExpirationQueue::new();
|
||||||
|
|
||||||
|
|
@ -234,11 +240,11 @@ impl WatchCommand for SqliteClipboardDb {
|
||||||
exp_queue.push(expires_at, id);
|
exp_queue.push(expires_at, id);
|
||||||
}
|
}
|
||||||
if !exp_queue.is_empty() {
|
if !exp_queue.is_empty() {
|
||||||
log::info!("Loaded {} expirations from database", exp_queue.len());
|
log::info!("loaded {} expirations from database", exp_queue.len());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("Failed to load expirations: {e}");
|
log::warn!("failed to load expirations: {e}");
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -277,7 +283,7 @@ impl WatchCommand for SqliteClipboardDb {
|
||||||
match async_db.get_content_hash(id).await {
|
match async_db.get_content_hash(id).await {
|
||||||
Ok(hash) => hash,
|
Ok(hash) => hash,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("Failed to get content hash for entry {id}: {e}");
|
log::warn!("failed to get content hash for entry {id}: {e}");
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -285,9 +291,9 @@ impl WatchCommand for SqliteClipboardDb {
|
||||||
if let Some(stored_hash) = expired_hash {
|
if let Some(stored_hash) = expired_hash {
|
||||||
// Mark as expired
|
// Mark as expired
|
||||||
if let Err(e) = async_db.mark_expired(id).await {
|
if let Err(e) = async_db.mark_expired(id).await {
|
||||||
log::warn!("Failed to mark entry {id} as expired: {e}");
|
log::warn!("failed to mark entry {id} as expired: {e}");
|
||||||
} else {
|
} else {
|
||||||
log::info!("Entry {id} marked as expired");
|
log::info!("entry {id} marked as expired");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this expired entry is currently in the clipboard
|
// Check if this expired entry is currently in the clipboard
|
||||||
|
|
@ -315,12 +321,12 @@ impl WatchCommand for SqliteClipboardDb {
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
log::info!(
|
log::info!(
|
||||||
"Cleared clipboard containing expired entry {id}"
|
"cleared clipboard containing expired entry {id}"
|
||||||
);
|
);
|
||||||
last_hash = None; // reset tracked hash
|
last_hash = None; // reset tracked hash
|
||||||
} else {
|
} else {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"Failed to clear clipboard for expired entry {id}"
|
"failed to clear clipboard for expired entry {id}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -337,7 +343,7 @@ impl WatchCommand for SqliteClipboardDb {
|
||||||
Ok((mut reader, _mime_type, _all_mimes)) => {
|
Ok((mut reader, _mime_type, _all_mimes)) => {
|
||||||
buf.clear();
|
buf.clear();
|
||||||
if let Err(e) = reader.read_to_end(&mut buf) {
|
if let Err(e) = reader.read_to_end(&mut buf) {
|
||||||
log::error!("Failed to read clipboard contents: {e}");
|
log::error!("failed to read clipboard contents: {e}");
|
||||||
Timer::after(Duration::from_millis(500)).await;
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -370,13 +376,13 @@ impl WatchCommand for SqliteClipboardDb {
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(id) => {
|
Ok(id) => {
|
||||||
log::info!("Stored new clipboard entry (id: {id})");
|
log::info!("stored new clipboard entry (id: {id})");
|
||||||
last_hash = Some(current_hash);
|
last_hash = Some(current_hash);
|
||||||
|
|
||||||
// Persist clipboard: fork child to serve data
|
// Persist clipboard: fork child to serve data
|
||||||
// This keeps the clipboard alive when source app closes
|
// This keeps the clipboard alive when source app closes
|
||||||
// Check if we're already serving to avoid duplicate processes
|
// Check if we're already serving to avoid duplicate processes
|
||||||
if get_serving_pid().is_none() {
|
if persist && get_serving_pid().is_none() {
|
||||||
let clipboard_data = ClipboardData::new(
|
let clipboard_data = ClipboardData::new(
|
||||||
buf_for_persist,
|
buf_for_persist,
|
||||||
mime_types_for_persist,
|
mime_types_for_persist,
|
||||||
|
|
@ -393,12 +399,12 @@ impl WatchCommand for SqliteClipboardDb {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
log::debug!("Clipboard persistence failed: {e}");
|
log::debug!("clipboard persistence failed: {e}");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
} else {
|
} else if persist {
|
||||||
log::trace!(
|
log::trace!(
|
||||||
"Already serving clipboard, skipping persistence fork"
|
"Already serving clipboard, skipping persistence fork"
|
||||||
);
|
);
|
||||||
|
|
@ -420,17 +426,17 @@ impl WatchCommand for SqliteClipboardDb {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(crate::db::StashError::ExcludedByApp(_)) => {
|
Err(crate::db::StashError::ExcludedByApp(_)) => {
|
||||||
log::info!("Clipboard entry excluded by app filter");
|
log::info!("clipboard entry excluded by app filter");
|
||||||
last_hash = Some(current_hash);
|
last_hash = Some(current_hash);
|
||||||
},
|
},
|
||||||
Err(crate::db::StashError::Store(ref msg))
|
Err(crate::db::StashError::Store(ref msg))
|
||||||
if msg.contains("Excluded by app filter") =>
|
if msg.contains("Excluded by app filter") =>
|
||||||
{
|
{
|
||||||
log::info!("Clipboard entry excluded by app filter");
|
log::info!("clipboard entry excluded by app filter");
|
||||||
last_hash = Some(current_hash);
|
last_hash = Some(current_hash);
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Failed to store clipboard entry: {e}");
|
log::error!("failed to store clipboard entry: {e}");
|
||||||
last_hash = Some(current_hash);
|
last_hash = Some(current_hash);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -440,7 +446,7 @@ impl WatchCommand for SqliteClipboardDb {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let error_msg = e.to_string();
|
let error_msg = e.to_string();
|
||||||
if !error_msg.contains("empty") {
|
if !error_msg.contains("empty") {
|
||||||
log::error!("Failed to get clipboard contents: {e}");
|
log::error!("failed to get clipboard contents: {e}");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ pub trait WipeCommand {
|
||||||
impl WipeCommand for SqliteClipboardDb {
|
impl WipeCommand for SqliteClipboardDb {
|
||||||
fn wipe(&self) -> Result<(), StashError> {
|
fn wipe(&self) -> Result<(), StashError> {
|
||||||
self.wipe_db()?;
|
self.wipe_db()?;
|
||||||
log::info!("Database wiped");
|
log::info!("database wiped");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -875,7 +875,7 @@ impl ClipboardDb for SqliteClipboardDb {
|
||||||
out
|
out
|
||||||
.write_all(&contents)
|
.write_all(&contents)
|
||||||
.map_err(|e| StashError::DecodeWrite(e.to_string().into()))?;
|
.map_err(|e| StashError::DecodeWrite(e.to_string().into()))?;
|
||||||
log::info!("Decoded entry with id {id}");
|
log::info!("decoded entry with id {id}");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,10 @@ enum Command {
|
||||||
/// MIME type preference for clipboard reading.
|
/// MIME type preference for clipboard reading.
|
||||||
#[arg(short = 't', long, default_value = "any")]
|
#[arg(short = 't', long, default_value = "any")]
|
||||||
mime_type: String,
|
mime_type: String,
|
||||||
|
|
||||||
|
/// Persist clipboard contents after the source application closes.
|
||||||
|
#[arg(long)]
|
||||||
|
persist: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,7 +205,7 @@ fn confirm(prompt: &str) -> bool {
|
||||||
.with_default(false)
|
.with_default(false)
|
||||||
.prompt()
|
.prompt()
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
log::error!("Confirmation prompt failed: {e}");
|
log::error!("confirmation prompt failed: {e}");
|
||||||
false
|
false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -477,6 +481,7 @@ fn main() -> eyre::Result<()> {
|
||||||
Some(Command::Watch {
|
Some(Command::Watch {
|
||||||
expire_after,
|
expire_after,
|
||||||
mime_type,
|
mime_type,
|
||||||
|
persist,
|
||||||
}) => {
|
}) => {
|
||||||
db.watch(
|
db.watch(
|
||||||
cli.max_dedupe_search,
|
cli.max_dedupe_search,
|
||||||
|
|
@ -489,6 +494,7 @@ fn main() -> eyre::Result<()> {
|
||||||
&mime_type,
|
&mime_type,
|
||||||
cli.min_size,
|
cli.min_size,
|
||||||
cli.max_size,
|
cli.max_size,
|
||||||
|
persist,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -421,7 +421,7 @@ fn handle_regular_paste(
|
||||||
let selected_type = available_types.as_ref().and_then(select_best_mime_type);
|
let selected_type = available_types.as_ref().and_then(select_best_mime_type);
|
||||||
|
|
||||||
let mime_type = if let Some(ref best) = selected_type {
|
let mime_type = if let Some(ref best) = selected_type {
|
||||||
log::debug!("Auto-selecting MIME type: {best}");
|
log::debug!("auto-selecting MIME type: {best}");
|
||||||
PasteMimeType::Specific(best)
|
PasteMimeType::Specific(best)
|
||||||
} else {
|
} else {
|
||||||
get_paste_mime_type(args.mime_type.as_deref())
|
get_paste_mime_type(args.mime_type.as_deref())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue