articles_rs/articles/
models.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::article_repository::DbArticle;

/// Represents the type of article
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub enum ArticleKind {
    /// Standard blog post article (default)
    #[default]
    POST,
    /// Link-type article that references external content
    LINK,
}

/// Represents an article with all its metadata and content
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct Article {
    pub id: Option<Uuid>,
    pub title: String,
    pub hero_image: String,
    pub slug: String,
    pub description: String,
    pub author: Vec<String>,
    pub status: String,
    pub created: DateTime<Utc>,
    pub content: String,
    pub images: Vec<String>,
    pub source: String,
    pub published: Option<DateTime<Utc>>,
    pub kind: ArticleKind,
}

impl Article {
    /// Creates a new Article with the given basic metadata
    ///
    /// # Arguments
    ///
    /// * `title` - The article title
    /// * `slug` - URL-friendly version of the title
    /// * `description` - Brief description or summary
    /// * `author` - List of article authors
    ///
    /// # Returns
    ///
    /// A new `Article` instance with default values for other fields
    pub fn new(title: &str, slug: &str, description: &str, author: Vec<&str>) -> Self {
        let now = Utc::now();
        Article {
            id: None,
            title: title.to_string(),
            hero_image: "".to_string(),
            slug: slug.to_string(),
            description: description.to_string(),
            author: author.iter().map(|s| s.to_string()).collect(),
            status: "NEW".to_string(),
            created: now,
            content: String::new(),
            images: Vec::new(),
            source: "".to_string(),
            published: None,
            kind: ArticleKind::POST,
        }
    }

    /// Adds a single image URL to the article's image collection
    ///
    /// # Arguments
    ///
    /// * `image_url` - URL of the image to add
    pub fn add_image(&mut self, image_url: String) {
        self.images.push(image_url);
    }

    /// Adds multiple image URLs to the article's image collection
    ///
    /// # Arguments
    ///
    /// * `image_urls` - List of image URLs to add
    pub fn add_images(&mut self, image_urls: Vec<String>) {
        self.images.extend(image_urls);
    }
}

/// Implements conversion from database model to Article
impl From<DbArticle> for Article {
    fn from(db_article: DbArticle) -> Self {
        Article {
            id: db_article.id,
            title: db_article.title,
            hero_image: db_article.hero_image,
            slug: db_article.slug,
            description: db_article.description,
            author: db_article
                .author
                .split(",")
                .map(|s| s.to_string())
                .collect(),
            status: db_article.status,
            created: db_article.created,
            content: db_article.content,
            images: Vec::new(),
            source: db_article.source.clone(),
            published: db_article.published,
            kind: if db_article.kind.is_empty() {
                ArticleKind::POST
            } else {
                ArticleKind::LINK
            },
        }
    }
}

/// Implements conversion from Article to database model
impl From<Article> for DbArticle {
    fn from(article: Article) -> Self {
        DbArticle {
            id: article.id,
            title: article.title,
            hero_image: article.hero_image,
            slug: article.slug,
            description: article.description,
            author: article.author.join(","),
            status: article.status,
            created: article.created,
            content: article.content,
            source: article.source,
            published: article.published,
            kind: match article.kind {
                ArticleKind::POST => String::from(""),
                ArticleKind::LINK => String::from("LINK"),
            },
        }
    }
}