Skip to content

Commit bf78143

Browse files
refactor: better error handling
1 parent 7f4cd06 commit bf78143

3 files changed

Lines changed: 67 additions & 14 deletions

File tree

src/app.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,12 @@ impl AppState {
5151
pub fn render(&self, template: &str, context: &Context) -> Result<Response> {
5252
let mut context = context.clone();
5353
context.insert("build_id", &self.build_id);
54-
let rendered = self.tera.render(template, &context)?;
55-
Ok(axum::response::Html(rendered).into_response())
54+
match self.tera.render(template, &context) {
55+
Ok(rendered) => Ok(axum::response::Html(rendered).into_response()),
56+
Err(e) => {
57+
tracing::error!("Template rendering failed: {}", e);
58+
Err(anyhow::anyhow!("Template error: {}", e))
59+
}
60+
}
5661
}
5762
}

src/routes.rs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,27 @@ pub async fn posts_index(pagination: Query<Pagination>, state: State<AppState>)
4646
}
4747
}
4848

49-
pub async fn about(state: State<AppState>) -> Response {
50-
match state.post_service.get_special_page("about").await {
49+
async fn handle_special_page(state: State<AppState>, page_id: &str) -> Response {
50+
match state.post_service.get_special_page(page_id).await {
5151
Ok(post) => {
5252
let mut context = Context::new();
5353
context.insert("post", &post);
54-
state.render("post.html", &context).unwrap()
54+
state.render("post.html", &context).unwrap_or_else(|e| {
55+
tracing::error!("Rendering error: {}", e);
56+
StatusCode::INTERNAL_SERVER_ERROR.into_response()
57+
})
58+
}
59+
Err(e) => {
60+
tracing::error!("Database error: {}", e);
61+
StatusCode::INTERNAL_SERVER_ERROR.into_response()
5562
}
56-
Err(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
5763
}
5864
}
5965

66+
pub async fn about(state: State<AppState>) -> Response {
67+
handle_special_page(state, "about").await
68+
}
69+
6070
pub async fn contact(state: State<AppState>) -> Response {
6171
match state.post_service.get_special_page("contact").await {
6272
Ok(post) => {
@@ -69,13 +79,27 @@ pub async fn contact(state: State<AppState>) -> Response {
6979
}
7080

7181
pub async fn post(Path(id): Path<String>, state: State<AppState>) -> Response {
82+
if id.is_empty() || id.len() > 100 {
83+
return StatusCode::BAD_REQUEST.into_response();
84+
}
85+
7286
match state.post_service.get_post(&id).await {
7387
Ok(post) => {
7488
let mut context = Context::new();
7589
context.insert("post", &post);
76-
state.render("post.html", &context).unwrap()
90+
state.render("post.html", &context).unwrap_or_else(|e| {
91+
tracing::error!("Rendering error: {}", e);
92+
StatusCode::INTERNAL_SERVER_ERROR.into_response()
93+
})
94+
}
95+
Err(e) => {
96+
if e.to_string().contains("not found") {
97+
StatusCode::NOT_FOUND.into_response()
98+
} else {
99+
tracing::error!("Database error: {}", e);
100+
StatusCode::INTERNAL_SERVER_ERROR.into_response()
101+
}
77102
}
78-
Err(_) => (StatusCode::NOT_FOUND, "Post not found").into_response(),
79103
}
80104
}
81105

src/services/post.rs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,39 +109,63 @@ impl PostService {
109109
}
110110

111111
pub async fn get_post(&self, id: &str) -> Result<Post> {
112-
let id_clone = id.to_owned();
112+
let id_for_blocking = id.to_owned();
113+
let id_for_error = id.to_owned();
113114
let pool = self.pool.clone();
114115
let query = task::spawn_blocking(move || {
115116
let conn = pool.get()?;
116117
let result = conn.query_row(
117118
"SELECT * FROM posts WHERE id = ? AND content_type != 'special'",
118-
[&id_clone],
119+
[&id_for_blocking],
119120
Self::row_to_post,
120121
)?;
121122
anyhow::Result::<_>::Ok(result)
122123
})
123124
.await
124125
.context("Failed to join blocking task")?
125-
.context(format!("Failed to fetch post {}", id))?;
126+
.map_err(|e| {
127+
if let Some(sqlite_err) = e.downcast_ref::<rusqlite::Error>() {
128+
match sqlite_err {
129+
rusqlite::Error::QueryReturnedNoRows => {
130+
anyhow::anyhow!("Post not found: {}", id_for_error)
131+
}
132+
_ => e.into()
133+
}
134+
} else {
135+
e
136+
}
137+
})?;
126138

127139
self.convert_to_post(query).await
128140
}
129141

130142
pub async fn get_special_page(&self, id: &str) -> Result<Post> {
131-
let id_clone = id.to_owned();
143+
let id_for_blocking = id.to_owned();
144+
let id_for_error = id.to_owned();
132145
let pool = self.pool.clone();
133146
let query = task::spawn_blocking(move || {
134147
let conn = pool.get()?;
135148
let result = conn.query_row(
136149
"SELECT * FROM posts WHERE id = ? AND content_type = 'special'",
137-
[&id_clone],
150+
[&id_for_blocking],
138151
Self::row_to_post,
139152
)?;
140153
anyhow::Result::<_>::Ok(result)
141154
})
142155
.await
143156
.context("Failed to join blocking task")?
144-
.context(format!("Failed to fetch special page {}", id))?;
157+
.map_err(|e| {
158+
if let Some(sqlite_err) = e.downcast_ref::<rusqlite::Error>() {
159+
match sqlite_err {
160+
rusqlite::Error::QueryReturnedNoRows => {
161+
anyhow::anyhow!("Special page not found: {}", id_for_error)
162+
}
163+
_ => e.into()
164+
}
165+
} else {
166+
e
167+
}
168+
})?;
145169

146170
self.convert_to_post(query).await
147171
}

0 commit comments

Comments
 (0)