Skip to main content

systemprompt_content/models/builders/
content.rs

1//! Parameter builders for content mutations.
2//!
3//! [`CreateContentParams`] and [`UpdateContentParams`] carry the field set for
4//! creating and updating content rows. [`CategoryIdUpdate`] distinguishes the
5//! three update intents for an optional category — leave unchanged, clear, or
6//! set — so a partial update can express "do not touch" separately from "set to
7//! null".
8//!
9//! Copyright (c) systemprompt.io — Business Source License 1.1.
10//! See <https://systemprompt.io> for licensing details.
11
12use chrono::{DateTime, Utc};
13use systemprompt_identifiers::{CategoryId, ContentId, LocaleCode, SourceId};
14
15#[derive(Debug, Clone)]
16pub enum CategoryIdUpdate {
17    Unchanged,
18    Clear,
19    Set(CategoryId),
20}
21
22impl From<Option<Option<CategoryId>>> for CategoryIdUpdate {
23    fn from(value: Option<Option<CategoryId>>) -> Self {
24        match value {
25            None => Self::Unchanged,
26            Some(None) => Self::Clear,
27            Some(Some(id)) => Self::Set(id),
28        }
29    }
30}
31
32#[derive(Debug, Clone)]
33pub struct CreateContentParams {
34    pub slug: String,
35    pub locale: LocaleCode,
36    pub title: String,
37    pub description: String,
38    pub body: String,
39    pub author: String,
40    pub published_at: DateTime<Utc>,
41    pub keywords: String,
42    pub kind: String,
43    pub image: Option<String>,
44    pub category_id: Option<CategoryId>,
45    pub source_id: SourceId,
46    pub version_hash: String,
47    pub links: serde_json::Value,
48    pub public: bool,
49}
50
51impl CreateContentParams {
52    pub fn new(
53        slug: String,
54        title: String,
55        description: String,
56        body: String,
57        source_id: SourceId,
58    ) -> Self {
59        Self {
60            slug,
61            locale: LocaleCode::new("en"),
62            title,
63            description,
64            body,
65            author: String::new(),
66            published_at: Utc::now(),
67            keywords: String::new(),
68            kind: "article".into(),
69            image: None,
70            category_id: None,
71            source_id,
72            version_hash: String::new(),
73            links: serde_json::Value::Array(vec![]),
74            public: true,
75        }
76    }
77
78    pub fn with_author(mut self, author: String) -> Self {
79        self.author = author;
80        self
81    }
82
83    pub fn with_locale(mut self, locale: LocaleCode) -> Self {
84        self.locale = locale;
85        self
86    }
87
88    pub const fn with_published_at(mut self, published_at: DateTime<Utc>) -> Self {
89        self.published_at = published_at;
90        self
91    }
92
93    pub fn with_keywords(mut self, keywords: String) -> Self {
94        self.keywords = keywords;
95        self
96    }
97
98    pub fn with_kind(mut self, kind: String) -> Self {
99        self.kind = kind;
100        self
101    }
102
103    pub fn with_image(mut self, image: Option<String>) -> Self {
104        self.image = image;
105        self
106    }
107
108    pub fn with_category_id(mut self, category_id: Option<CategoryId>) -> Self {
109        self.category_id = category_id;
110        self
111    }
112
113    pub fn with_version_hash(mut self, version_hash: String) -> Self {
114        self.version_hash = version_hash;
115        self
116    }
117
118    pub fn with_links(mut self, links: serde_json::Value) -> Self {
119        self.links = links;
120        self
121    }
122
123    pub const fn with_public(mut self, public: bool) -> Self {
124        self.public = public;
125        self
126    }
127}
128
129#[derive(Debug, Clone)]
130pub struct UpdateContentParams {
131    pub id: ContentId,
132    pub title: String,
133    pub description: String,
134    pub body: String,
135    pub keywords: String,
136    pub image: Option<String>,
137    pub version_hash: String,
138    pub category_id: CategoryIdUpdate,
139    pub public: Option<bool>,
140    pub kind: Option<String>,
141    pub author: Option<String>,
142    pub published_at: Option<DateTime<Utc>>,
143    pub links: Option<serde_json::Value>,
144}
145
146impl UpdateContentParams {
147    pub const fn new(id: ContentId, title: String, description: String, body: String) -> Self {
148        Self {
149            id,
150            title,
151            description,
152            body,
153            keywords: String::new(),
154            image: None,
155            version_hash: String::new(),
156            category_id: CategoryIdUpdate::Unchanged,
157            public: None,
158            kind: None,
159            author: None,
160            published_at: None,
161            links: None,
162        }
163    }
164
165    pub fn with_keywords(mut self, keywords: String) -> Self {
166        self.keywords = keywords;
167        self
168    }
169
170    pub fn with_image(mut self, image: Option<String>) -> Self {
171        self.image = image;
172        self
173    }
174
175    pub fn with_version_hash(mut self, version_hash: String) -> Self {
176        self.version_hash = version_hash;
177        self
178    }
179
180    pub fn with_category_id(mut self, category_id: impl Into<CategoryIdUpdate>) -> Self {
181        self.category_id = category_id.into();
182        self
183    }
184
185    pub const fn with_public(mut self, public: Option<bool>) -> Self {
186        self.public = public;
187        self
188    }
189
190    pub fn with_kind(mut self, kind: Option<String>) -> Self {
191        self.kind = kind;
192        self
193    }
194
195    pub fn with_author(mut self, author: Option<String>) -> Self {
196        self.author = author;
197        self
198    }
199
200    pub const fn with_published_at(mut self, published_at: Option<DateTime<Utc>>) -> Self {
201        self.published_at = published_at;
202        self
203    }
204
205    pub fn with_links(mut self, links: Option<serde_json::Value>) -> Self {
206        self.links = links;
207        self
208    }
209}