articles_rs/articles/
models.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use uuid::Uuid;
4
5use crate::article_repository::DbArticle;
6
7/// Represents the type of article
8#[derive(Debug, Serialize, Deserialize, Clone, Default)]
9pub enum ArticleKind {
10    /// Standard blog post article (default)
11    #[default]
12    POST,
13    /// Link-type article that references external content
14    LINK,
15}
16
17/// Represents an article with all its metadata and content
18#[derive(Debug, Serialize, Deserialize, Clone, Default)]
19pub struct Article {
20    pub id: Option<Uuid>,
21    pub title: String,
22    pub hero_image: String,
23    pub slug: String,
24    pub description: String,
25    pub author: Vec<String>,
26    pub status: String,
27    pub created: DateTime<Utc>,
28    pub content: String,
29    pub images: Vec<String>,
30    pub source: String,
31    pub published: Option<DateTime<Utc>>,
32    pub kind: ArticleKind,
33}
34
35impl Article {
36    /// Creates a new Article with the given basic metadata
37    ///
38    /// # Arguments
39    ///
40    /// * `title` - The article title
41    /// * `slug` - URL-friendly version of the title
42    /// * `description` - Brief description or summary
43    /// * `author` - List of article authors
44    ///
45    /// # Returns
46    ///
47    /// A new `Article` instance with default values for other fields
48    pub fn new(title: &str, slug: &str, description: &str, author: Vec<&str>) -> Self {
49        let now = Utc::now();
50        Article {
51            id: None,
52            title: title.to_string(),
53            hero_image: "".to_string(),
54            slug: slug.to_string(),
55            description: description.to_string(),
56            author: author.iter().map(|s| s.to_string()).collect(),
57            status: "NEW".to_string(),
58            created: now,
59            content: String::new(),
60            images: Vec::new(),
61            source: "".to_string(),
62            published: None,
63            kind: ArticleKind::POST,
64        }
65    }
66
67    /// Adds a single image URL to the article's image collection
68    ///
69    /// # Arguments
70    ///
71    /// * `image_url` - URL of the image to add
72    pub fn add_image(&mut self, image_url: String) {
73        self.images.push(image_url);
74    }
75
76    /// Adds multiple image URLs to the article's image collection
77    ///
78    /// # Arguments
79    ///
80    /// * `image_urls` - List of image URLs to add
81    pub fn add_images(&mut self, image_urls: Vec<String>) {
82        self.images.extend(image_urls);
83    }
84}
85
86/// Implements conversion from database model to Article
87impl From<DbArticle> for Article {
88    fn from(db_article: DbArticle) -> Self {
89        Article {
90            id: db_article.id,
91            title: db_article.title,
92            hero_image: db_article.hero_image,
93            slug: db_article.slug,
94            description: db_article.description,
95            author: db_article
96                .author
97                .split(",")
98                .map(|s| s.to_string())
99                .collect(),
100            status: db_article.status,
101            created: db_article.created,
102            content: db_article.content,
103            images: Vec::new(),
104            source: db_article.source.clone(),
105            published: db_article.published,
106            kind: if db_article.kind == "LINK" {
107                ArticleKind::LINK
108            } else {
109                ArticleKind::POST
110            },
111        }
112    }
113}
114
115/// Implements conversion from Article to database model
116impl From<Article> for DbArticle {
117    fn from(article: Article) -> Self {
118        DbArticle {
119            id: article.id,
120            title: article.title,
121            hero_image: article.hero_image,
122            slug: article.slug,
123            description: article.description,
124            author: article.author.join(","),
125            status: article.status,
126            created: article.created,
127            content: article.content,
128            source: article.source,
129            published: article.published,
130            kind: match article.kind {
131                ArticleKind::POST => String::from("POST"),
132                ArticleKind::LINK => String::from("LINK"),
133            },
134        }
135    }
136}