use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use std::collections::HashMap;
use super::auth_context::AuthContext;
use super::datetime_format;
use super::Tag;
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct Asset {
#[serde(rename = "_id")]
pub id: String,
pub title: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(default)]
pub tags: Vec<Tag>, #[serde(default)]
pub metadata: HashMap<String, serde_json::Value>,
#[serde(with = "datetime_format")]
pub created_at: DateTime<Utc>,
#[serde(with = "datetime_format")]
pub updated_at: DateTime<Utc>,
pub created_by: String,
pub updated_by: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub deleted_at: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth_context: Option<AuthContext>,
}
#[derive(Debug, Clone, Deserialize, ToSchema)]
pub struct CreateAssetRequest {
pub title: String,
#[serde(default)]
pub content: Option<String>,
#[serde(default)]
pub tags: Vec<super::AddTagRequest>, #[serde(default)]
pub metadata: HashMap<String, serde_json::Value>,
#[serde(default)]
pub auth_context: Option<super::AuthContext>,
}
#[derive(Debug, Clone, Deserialize, ToSchema)]
pub struct UpdateAssetRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Deserialize, ToSchema)]
pub struct UpdateAuthContextRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub visibility: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub owner_groups: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub confidentiality: Option<String>,
#[serde(default)]
pub cascade: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct SearchResult {
#[serde(rename = "_id")]
pub id: String,
pub title: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub snippet: Option<String>,
pub tags: Vec<TagSummary>,
#[serde(with = "datetime_format")]
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct TagSummary {
pub category: String,
pub value: String,
}
pub fn generate_snippet(content: &str, max_length: usize) -> Option<String> {
if content.is_empty() {
return None;
}
let text: String = content
.lines()
.map(|l| l.trim_start_matches('#').trim_start())
.collect::<Vec<_>>()
.join(" ")
.replace("**", "")
.replace("*", "")
.replace("`", "")
.replace("```", "")
.split_whitespace()
.collect::<Vec<_>>()
.join(" ");
if text.is_empty() {
return None;
}
if text.len() <= max_length {
return Some(text);
}
let mut end = max_length;
while end > 0 && !text.is_char_boundary(end) {
end -= 1;
}
if end == 0 {
return Some(text);
}
let truncated = &text[..end];
Some(format!(
"{}...",
truncated.rsplit_once(' ').map(|(w, _)| w).unwrap_or(truncated)
))
}