use crate::storage::PromptMetadata;
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Debug, Clone)]
pub struct ClipboardItem {
pub items: Vec<ClipboardEntry>,
pub operation: ClipboardOperation,
pub timestamp: u64,
}
#[derive(Debug, Clone)]
pub struct ClipboardEntry {
pub name: String,
pub content: String,
pub metadata: PromptMetadata,
pub source_bank: Option<String>,
pub item_type: ClipboardItemType,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ClipboardOperation {
Cut, Copy, }
#[derive(Debug, Clone, PartialEq)]
pub enum ClipboardItemType {
Prompt,
Bank,
}
impl ClipboardItem {
pub fn new_cut(items: Vec<ClipboardEntry>) -> Self {
Self {
items,
operation: ClipboardOperation::Cut,
timestamp: current_timestamp(),
}
}
pub fn new_copy(items: Vec<ClipboardEntry>) -> Self {
Self {
items,
operation: ClipboardOperation::Copy,
timestamp: current_timestamp(),
}
}
pub fn is_cut(&self) -> bool {
self.operation == ClipboardOperation::Cut
}
pub fn is_copy(&self) -> bool {
self.operation == ClipboardOperation::Copy
}
pub fn count(&self) -> usize {
self.items.len()
}
pub fn is_prompts_only(&self) -> bool {
self.items
.iter()
.all(|item| item.item_type == ClipboardItemType::Prompt)
}
pub fn is_banks_only(&self) -> bool {
self.items
.iter()
.all(|item| item.item_type == ClipboardItemType::Bank)
}
pub fn is_mixed_content(&self) -> bool {
!self.is_prompts_only() && !self.is_banks_only()
}
pub fn display(&self) -> String {
let op_symbol = match self.operation {
ClipboardOperation::Cut => "✂",
ClipboardOperation::Copy => "📋",
};
if self.items.len() == 1 {
format!("{} {}", op_symbol, self.items[0].name)
} else {
format!("{} {} items", op_symbol, self.items.len())
}
}
pub fn detailed_display(&self) -> String {
let operation = match self.operation {
ClipboardOperation::Cut => "cut",
ClipboardOperation::Copy => "copied",
};
if self.items.len() == 1 {
let item = &self.items[0];
let type_str = match item.item_type {
ClipboardItemType::Prompt => "prompt",
ClipboardItemType::Bank => "bank",
};
format!("{} {} '{}'", operation, type_str, item.name)
} else {
let prompt_count = self
.items
.iter()
.filter(|i| i.item_type == ClipboardItemType::Prompt)
.count();
let bank_count = self
.items
.iter()
.filter(|i| i.item_type == ClipboardItemType::Bank)
.count();
match (prompt_count, bank_count) {
(p, 0) => format!("{} {} prompts", operation, p),
(0, b) => format!("{} {} banks", operation, b),
(p, b) => format!("{} {} prompts and {} banks", operation, p, b),
}
}
}
pub fn is_expired(&self) -> bool {
let current = current_timestamp();
current.saturating_sub(self.timestamp) > 3600 }
pub fn age_seconds(&self) -> u64 {
let current = current_timestamp();
current.saturating_sub(self.timestamp)
}
pub fn prompts(&self) -> Vec<&ClipboardEntry> {
self.items
.iter()
.filter(|item| item.item_type == ClipboardItemType::Prompt)
.collect()
}
pub fn banks(&self) -> Vec<&ClipboardEntry> {
self.items
.iter()
.filter(|item| item.item_type == ClipboardItemType::Bank)
.collect()
}
pub fn can_paste_into(&self, target_bank: Option<&str>) -> (bool, Option<String>) {
for item in &self.items {
if item.source_bank.as_deref() == target_bank {
if self.is_cut() {
return (
false,
Some("Cannot move items to the same location".to_string()),
);
}
return (
false,
Some(format!(
"Item '{}' already exists in target location",
item.name
)),
);
}
}
(true, None)
}
}
impl ClipboardEntry {
pub fn new_prompt(
name: String,
content: String,
metadata: PromptMetadata,
source_bank: Option<String>,
) -> Self {
Self {
name,
content,
metadata,
source_bank,
item_type: ClipboardItemType::Prompt,
}
}
pub fn new_bank(name: String, source_bank: Option<String>) -> Self {
let metadata = PromptMetadata {
id: name.clone(),
description: format!("Bank: {}", name),
tags: None,
created_at: None,
updated_at: None,
version: None,
git_hash: None,
parent_version: None,
};
Self {
name,
content: String::new(), metadata,
source_bank,
item_type: ClipboardItemType::Bank,
}
}
pub fn full_name(&self) -> String {
if let Some(bank) = &self.source_bank {
format!("{}/{}", bank, self.name)
} else {
self.name.clone()
}
}
pub fn display_name(&self) -> String {
match self.item_type {
ClipboardItemType::Prompt => {
if let Some(bank) = &self.source_bank {
format!("{}/{}", bank, self.name)
} else {
self.name.clone()
}
}
ClipboardItemType::Bank => self.name.clone(),
}
}
}
fn current_timestamp() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
}