Files
IdeA/crates/domain/src/chat_draft.rs

85 lines
2.3 KiB
Rust

//! Persisted custom chat draft model.
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::ids::{LayoutId, NodeId, ProjectId};
/// Maximum draft text size persisted by IdeA.
pub const CHAT_DRAFT_MAX_BYTES: usize = 100 * 1024;
/// Stable scope for one custom chat draft.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ChatDraftKey {
/// Project owning the layout.
pub project_id: ProjectId,
/// Named layout containing the chat cell.
pub layout_id: LayoutId,
/// Layout node hosting the chat cell.
pub node_id: NodeId,
}
impl ChatDraftKey {
/// Builds a draft key.
#[must_use]
pub const fn new(project_id: ProjectId, layout_id: LayoutId, node_id: NodeId) -> Self {
Self {
project_id,
layout_id,
node_id,
}
}
}
/// Durable text draft for one custom chat composer.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ChatDraft {
/// Stable draft scope.
pub key: ChatDraftKey,
/// Draft text. Attachments are deliberately excluded.
pub text: String,
/// Last update time, epoch milliseconds.
pub updated_at_ms: u64,
}
impl ChatDraft {
/// Builds validated draft metadata.
///
/// # Errors
/// Returns [`ChatDraftError::TooLarge`] when `text` exceeds the maximum
/// persisted draft size.
pub fn new(
key: ChatDraftKey,
text: impl Into<String>,
updated_at_ms: u64,
) -> Result<Self, ChatDraftError> {
let text = text.into();
if text.len() > CHAT_DRAFT_MAX_BYTES {
return Err(ChatDraftError::TooLarge {
max: CHAT_DRAFT_MAX_BYTES,
actual: text.len(),
});
}
Ok(Self {
key,
text,
updated_at_ms,
})
}
}
/// Chat draft validation errors.
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum ChatDraftError {
/// Draft content exceeds the persisted size limit.
#[error("chat draft exceeds {max} bytes: {actual}")]
TooLarge {
/// Maximum allowed bytes.
max: usize,
/// Actual text bytes.
actual: usize,
},
}