Markdown Converter
Agent skill for markdown-converter
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Sign in to like and favorite skills
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
A Rust web application for collecting user feedback via shareable links. Built with Axum web framework, SQLite database, and Askama templates.
Key workflow:
# Build project cargo build # Run application (starts on http://localhost:3000) cargo run # Run in release mode cargo build --release cargo run --release
# Run all tests cargo test # Run specific test cargo test test_name # Run tests with output cargo test -- --nocapture # Run tests for specific module cargo test db::tests cargo test tests::test_admin_list
# Format code cargo fmt # Check formatting without making changes cargo fmt -- --check # Run linter cargo clippy # Run linter with warnings as errors cargo clippy -- -D warnings
Two-module design:
src/main.rs: Web server, routes, handlers, templates, and integration testssrc/db.rs: Database layer with SQLite operations and unit testsData model:
Prompt: Feedback prompt with UUID, title, description, timestampFeedback: User submission linked to prompt with UUID, content, timestampState management:
AppState holds SQLite connection pool (SqlitePool)Arc<AppState> across handlersState(state): State<Arc<AppState>>Route structure:
GET / → Redirect to /admin GET /admin → List all prompts GET /admin/new → New prompt form POST /admin/new → Create prompt GET /admin/prompt/:id → View prompt and feedback responses GET /feedback/:id → Public feedback form POST /feedback/:id → Submit feedback
Handler patterns:
impl IntoResponse for return typesPath(id): Path<String>Form(form): Form<FormStruct>Host(host): HostTemplates in
templates/ directory use Askama's Jinja2-like syntax:
base.html: Base layout with embedded CSSadmin_list.html, admin_new.html, admin_detail.htmlfeedback_form.html, feedback_success.htmlTemplate usage:
#[derive(Template)] #[template(path = "template_name.html")] struct TemplateName { field: Type, }
Connection:
SqlitePool for connection poolingDATABASE_URL env var or sqlite:feedback.db?mode=rwcQuery patterns:
// Parameterized queries (SQL injection safe) sqlx::query("INSERT INTO table (col) VALUES (?)") .bind(value) .execute(pool) .await? // Fetch with mapping sqlx::query_as::<_, Struct>("SELECT * FROM table WHERE id = ?") .bind(id) .fetch_optional(pool) // or fetch_all() or fetch_one() .await?
Two test suites:
Database tests (
src/db.rs):
sqlite::memory:Integration tests (
src/main.rs):
tower::ServiceExt::oneshot() for request simulationsetup_test_app() creates fresh in-memory databaseTest patterns:
#[tokio::test] async fn test_name() { let (app, state) = setup_test_app().await; let response = app .oneshot( Request::builder() .uri("/path") .body(Body::empty()) .unwrap(), ) .await .unwrap(); assert_eq!(response.status(), StatusCode::OK); }
All IDs use UUID v4 via
uuid::Uuid::new_v4().to_string()
All timestamps use RFC3339 format via
chrono::Utc::now().to_rfc3339()
Admin detail page generates shareable URLs dynamically:
http://https://{protocol}://{host}/feedback/{prompt_id}create_router() function is extracted for testability - both main app and tests use it with different state instances.
CREATE TABLE prompts ( id TEXT PRIMARY KEY, title TEXT NOT NULL, description TEXT NOT NULL, created_at TEXT NOT NULL ); CREATE TABLE feedback ( id TEXT PRIMARY KEY, prompt_id TEXT NOT NULL, content TEXT NOT NULL, created_at TEXT NOT NULL, FOREIGN KEY (prompt_id) REFERENCES prompts(id) );
Both tables order results by
created_at DESC (newest first).