systemprompt_models/api/responses/
markdown.rs1use serde::{Deserialize, Serialize};
5
6#[cfg(feature = "web")]
7use axum::http::StatusCode;
8#[cfg(feature = "web")]
9use axum::response::IntoResponse;
10#[cfg(feature = "web")]
11use http::header;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct MarkdownFrontmatter {
15 pub title: String,
16 pub slug: String,
17 #[serde(skip_serializing_if = "Option::is_none")]
18 pub description: Option<String>,
19 #[serde(skip_serializing_if = "Option::is_none")]
20 pub author: Option<String>,
21 #[serde(skip_serializing_if = "Option::is_none")]
22 pub published_at: Option<String>,
23 #[serde(default, skip_serializing_if = "Vec::is_empty")]
24 pub tags: Vec<String>,
25 #[serde(skip_serializing_if = "Option::is_none")]
26 pub url: Option<String>,
27}
28
29impl MarkdownFrontmatter {
30 pub fn new(title: impl Into<String>, slug: impl Into<String>) -> Self {
31 Self {
32 title: title.into(),
33 slug: slug.into(),
34 description: None,
35 author: None,
36 published_at: None,
37 tags: Vec::new(),
38 url: None,
39 }
40 }
41
42 #[must_use]
43 pub fn with_description(mut self, description: impl Into<String>) -> Self {
44 self.description = Some(description.into());
45 self
46 }
47
48 #[must_use]
49 pub fn with_author(mut self, author: impl Into<String>) -> Self {
50 self.author = Some(author.into());
51 self
52 }
53
54 #[must_use]
55 pub fn with_published_at(mut self, published_at: impl Into<String>) -> Self {
56 self.published_at = Some(published_at.into());
57 self
58 }
59
60 #[must_use]
61 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
62 self.tags = tags;
63 self
64 }
65
66 #[must_use]
67 pub fn with_url(mut self, url: impl Into<String>) -> Self {
68 self.url = Some(url.into());
69 self
70 }
71
72 pub fn to_yaml(&self) -> Result<String, serde_yaml::Error> {
73 serde_yaml::to_string(self)
74 }
75}
76
77#[derive(Debug, Clone)]
78pub struct MarkdownResponse {
79 pub frontmatter: MarkdownFrontmatter,
80 pub body: String,
81}
82
83impl MarkdownResponse {
84 pub fn new(frontmatter: MarkdownFrontmatter, body: impl Into<String>) -> Self {
85 Self {
86 frontmatter,
87 body: body.into(),
88 }
89 }
90
91 pub fn to_markdown(&self) -> Result<String, serde_yaml::Error> {
92 let yaml = self.frontmatter.to_yaml()?;
93 Ok(format!("---\n{}---\n\n{}", yaml, self.body))
94 }
95}
96
97#[cfg(feature = "web")]
98impl IntoResponse for MarkdownResponse {
99 fn into_response(self) -> axum::response::Response {
100 match self.to_markdown() {
101 Ok(body) => (
102 StatusCode::OK,
103 [(header::CONTENT_TYPE, "text/markdown; charset=utf-8")],
104 body,
105 )
106 .into_response(),
107 Err(e) => {
108 tracing::error!(error = %e, "MarkdownResponse frontmatter serialization failed");
109 StatusCode::INTERNAL_SERVER_ERROR.into_response()
110 },
111 }
112 }
113}