Skip to content

Commit d92984b

Browse files
committed
claude fix
1 parent b366586 commit d92984b

6 files changed

Lines changed: 122 additions & 88 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ env:
1212
jobs:
1313
build:
1414
name: build test fmt etc
15-
runs-on: ubuntu-latest # TODO: self-hosted explainer/link
15+
runs-on: ubuntu-latest # TODO: self-hosted explainer/link for folks interested in running their own
1616

1717
steps:
1818
- name: checkout code
@@ -65,7 +65,7 @@ jobs:
6565

6666
build-release:
6767
name: Build Release
68-
runs-on: self-hosted
68+
runs-on: ubuntu-latest # TODO: self-hosted
6969
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
7070

7171
steps:

README.md

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# MADstack
22

3+
[![ci](https://github.com/pgray/MADstack/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/pgray/MADstack/actions/workflows/ci.yml)
4+
35
> ai stands for: angry! irate!
46
57
MADstack (or madstack or mAdStAcK) is a small rust project template/example for use with [claude](https://claude.ai)
@@ -8,7 +10,7 @@ if you're going to make things easy with AI,
810

911
why not also make other things "hard" with rust and as much compile-time checking as possible?
1012

11-
## MAD! MAD?
13+
## MAD! MAD? >:|
1214

1315
- M: [maud](https://maud.lambda.xyz/)
1416
- A: [axum](https://github.com/tokio-rs/axum)
@@ -20,21 +22,26 @@ why not also make other things "hard" with rust and as much compile-time checkin
2022
- copy this repo and start your own project
2123
- tell us about it or don't
2224
- open issues to improve or update dependencies as needed
23-
- tell us about amazing new dependenciess to replace old ones
25+
- tell us about amazing new dependencies to replace old ones
26+
- no flaming
2427

2528
## FUTURE
2629

2730
it would be swell if folks find this useful at all
2831

29-
a long term goal could be to figure out a great fast and simple stack for web app dev for rust
32+
a long term goal could be:
33+
34+
- figuring out the fastest/simplest/safest stack for web app dev for rust
35+
- a living repo of current-favorite+fastest+safest web app dependencies
3036

3137
right now it exists as a pile of awesome dependencies
3238

3339
thank you to you if you read this and please be grateful of all the amazing software that exists
3440

3541
## INSTALL?
3642

37-
idk friend, maybe ask claude?
43+
1. install linux
44+
2. ask claude
3845

3946
## inspiration:
4047

@@ -43,3 +50,13 @@ idk friend, maybe ask claude?
4350
- [arch linux](https://archlinux.org/)
4451
- [humanlayer's claude writeup](https://www.humanlayer.dev/blog/writing-a-good-claude-md)
4552
- experience with a few madstack apps
53+
54+
## build/run
55+
56+
```
57+
docker compose build
58+
docker compose up
59+
curl localhost:3000/echoes
60+
```
61+
62+
## TODO: turn into a template repo? maybe?

bin/ccc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env bash
22

33
function c {
4-
nice -n 19 cargo "$*"
4+
nice -n 19 cargo "$@"
55
}
66
c fmt \
77
&& c check \

src/echoes.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use axum::{
2+
extract::{Form, State},
3+
response::{Html, Redirect},
4+
};
5+
use diesel::prelude::*;
6+
use maud::html;
7+
use serde::Deserialize;
8+
use tracing::{debug, info, instrument};
9+
10+
use crate::db::DbPool;
11+
use crate::handlers::layout;
12+
use crate::models::{Message, NewMessage};
13+
use crate::schema::messages;
14+
15+
#[instrument(skip(pool))]
16+
pub async fn list(State(pool): State<DbPool>) -> Html<String> {
17+
info!("GET /echoes route called");
18+
debug!("Getting database connection from pool");
19+
let mut conn = pool.get().expect("Failed to get DB connection");
20+
21+
debug!("Loading messages from database");
22+
let messages_list = messages::table
23+
.order(messages::created_at.desc())
24+
.limit(50)
25+
.load::<Message>(&mut conn)
26+
.expect("Error loading messages");
27+
28+
info!(
29+
message_count = messages_list.len(),
30+
"Messages loaded successfully"
31+
);
32+
33+
let content = html! {
34+
h1 { "Echo Server" }
35+
36+
form method="post" action="/echoes" {
37+
textarea name="content" rows="5" placeholder="Type your message here..." required {}
38+
br;
39+
button type="submit" { "Send Echo" }
40+
}
41+
42+
h2 { "Recent Messages" }
43+
44+
@for message in messages_list {
45+
div.message {
46+
div.message-content { (message.content) }
47+
div.message-time {
48+
"Sent at: "
49+
@let time = message.created_at;
50+
(format!("{:?}", time))
51+
}
52+
}
53+
}
54+
};
55+
56+
Html(layout(content).into_string())
57+
}
58+
59+
#[derive(Deserialize)]
60+
pub struct EchoForm {
61+
content: String,
62+
}
63+
64+
#[instrument(skip(pool, form), fields(content_length = form.content.len()))]
65+
pub async fn create(State(pool): State<DbPool>, Form(form): Form<EchoForm>) -> Redirect {
66+
info!("POST /echoes route called");
67+
debug!("Getting database connection from pool");
68+
let mut conn = pool.get().expect("Failed to get DB connection");
69+
70+
let content_preview = if form.content.len() > 50 {
71+
format!("{}...", &form.content[..50])
72+
} else {
73+
form.content.clone()
74+
};
75+
debug!(content_preview = %content_preview, "Preparing to save message");
76+
77+
let new_message = NewMessage {
78+
content: form.content,
79+
};
80+
81+
debug!("Inserting message into database");
82+
diesel::insert_into(messages::table)
83+
.values(&new_message)
84+
.execute(&mut conn)
85+
.expect("Error saving message");
86+
87+
info!("Message saved successfully, redirecting to /echoes");
88+
Redirect::to("/echoes")
89+
}

src/handlers.rs

Lines changed: 6 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,7 @@
1-
use axum::{
2-
extract::{Form, State},
3-
response::{Html, Redirect},
4-
};
5-
use diesel::prelude::*;
1+
use axum::response::Html;
62
use maud::{DOCTYPE, Markup, html};
7-
use serde::Deserialize;
8-
use tracing::{debug, info, instrument};
93

10-
use crate::db::DbPool;
11-
use crate::models::{Message, NewMessage};
12-
use crate::schema::messages;
13-
14-
fn layout(content: Markup) -> Markup {
4+
pub fn layout(content: Markup) -> Markup {
155
html! {
166
(DOCTYPE)
177
html {
@@ -83,78 +73,14 @@ fn layout(content: Markup) -> Markup {
8373
}
8474
}
8575

86-
#[instrument(skip(pool))]
87-
pub async fn index(State(pool): State<DbPool>) -> Html<String> {
88-
info!("Index route called");
89-
debug!("Getting database connection from pool");
90-
let mut conn = pool.get().expect("Failed to get DB connection");
91-
92-
debug!("Loading messages from database");
93-
let messages_list = messages::table
94-
.order(messages::created_at.desc())
95-
.limit(50)
96-
.load::<Message>(&mut conn)
97-
.expect("Error loading messages");
98-
99-
info!(
100-
message_count = messages_list.len(),
101-
"Messages loaded successfully"
102-
);
103-
76+
pub async fn index() -> Html<String> {
10477
let content = html! {
10578
h1 { "Echo Server" }
106-
107-
form method="post" action="/echo" {
108-
textarea name="content" rows="5" placeholder="Type your message here..." required {}
109-
br;
110-
button type="submit" { "Send Echo" }
111-
}
112-
113-
h2 { "Recent Messages" }
114-
115-
@for message in messages_list {
116-
div.message {
117-
div.message-content { (message.content) }
118-
div.message-time {
119-
"Sent at: "
120-
@let time = message.created_at;
121-
(format!("{:?}", time))
122-
}
123-
}
79+
p {
80+
"Welcome to the Echo Server! "
81+
a href="/echoes" { "View echoes" }
12482
}
12583
};
12684

12785
Html(layout(content).into_string())
12886
}
129-
130-
#[derive(Deserialize)]
131-
pub struct EchoForm {
132-
content: String,
133-
}
134-
135-
#[instrument(skip(pool, form), fields(content_length = form.content.len()))]
136-
pub async fn echo(State(pool): State<DbPool>, Form(form): Form<EchoForm>) -> Redirect {
137-
info!("Echo route called");
138-
debug!("Getting database connection from pool");
139-
let mut conn = pool.get().expect("Failed to get DB connection");
140-
141-
let content_preview = if form.content.len() > 50 {
142-
format!("{}...", &form.content[..50])
143-
} else {
144-
form.content.clone()
145-
};
146-
debug!(content_preview = %content_preview, "Preparing to save message");
147-
148-
let new_message = NewMessage {
149-
content: form.content,
150-
};
151-
152-
debug!("Inserting message into database");
153-
diesel::insert_into(messages::table)
154-
.values(&new_message)
155-
.execute(&mut conn)
156-
.expect("Error saving message");
157-
158-
info!("Message saved successfully, redirecting to index");
159-
Redirect::to("/")
160-
}

src/main.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod db;
2+
mod echoes;
23
mod handlers;
34
mod models;
45
mod schema;
@@ -38,7 +39,8 @@ async fn main() {
3839

3940
let app = Router::new()
4041
.route("/", get(handlers::index))
41-
.route("/echo", post(handlers::echo))
42+
.route("/echoes", get(echoes::list))
43+
.route("/echoes", post(echoes::create))
4244
.with_state(pool);
4345

4446
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();

0 commit comments

Comments
 (0)