229 lines
6.5 KiB
Markdown
229 lines
6.5 KiB
Markdown
|
|
# Template/Layout Feature Implementation
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
Successfully implemented a flexible template/layout system that allows loading custom HTML templates from the working directory's `./layout` folder.
|
||
|
|
|
||
|
|
## Key Features
|
||
|
|
|
||
|
|
### ✅ **Template Loading from Filesystem**
|
||
|
|
- **Custom Template Support**: Loads templates from `./layout/{template_name}.hbs`
|
||
|
|
- **Handlebars Integration**: Uses the `handlebars` crate for powerful templating
|
||
|
|
- **Fallback Mechanism**: Gracefully falls back to built-in default template if custom template not found
|
||
|
|
- **Runtime Configuration**: Template name configurable via `--template` flag (default: "default")
|
||
|
|
|
||
|
|
### ✅ **Template Variables**
|
||
|
|
- `{{title}}` - Page title derived from Markdown filename
|
||
|
|
- `{{content}}` - Rendered HTML content from Markdown
|
||
|
|
- `{{path}}` - Current URL path
|
||
|
|
|
||
|
|
### ✅ **Error Handling**
|
||
|
|
- **File Not Found**: Falls back to built-in template if custom template doesn't exist
|
||
|
|
- **Parse Errors**: Falls back to simple HTML if template rendering fails
|
||
|
|
- **Directory Creation**: Automatically handles missing layout directories
|
||
|
|
|
||
|
|
### ✅ **User Experience**
|
||
|
|
- **Clear Logging**: Shows which template is being used on startup
|
||
|
|
- **Help Documentation**: Updated `--help` shows template option
|
||
|
|
- **Flexible Configuration**: Easy to switch between different templates
|
||
|
|
|
||
|
|
## Implementation Details
|
||
|
|
|
||
|
|
### Template Loading Logic
|
||
|
|
|
||
|
|
```rust
|
||
|
|
fn load_template(workdir: &PathBuf, template_name: &str) -> Handlebars<'static> {
|
||
|
|
// 1. Try to load from ./layout/{template_name}.hbs
|
||
|
|
// 2. If found and valid, use it
|
||
|
|
// 3. Otherwise, use built-in default template
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Template Rendering
|
||
|
|
|
||
|
|
```rust
|
||
|
|
let template_data = json!({
|
||
|
|
"title": title,
|
||
|
|
"content": html_output,
|
||
|
|
"path": path.into_inner(),
|
||
|
|
});
|
||
|
|
|
||
|
|
match data.template_engine.render("layout", &template_data) {
|
||
|
|
Ok(rendered) => /* Use rendered template */,
|
||
|
|
Err(_) => /* Fallback to simple HTML */
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## File Structure
|
||
|
|
|
||
|
|
```
|
||
|
|
mhmmcontent/
|
||
|
|
├── layout/ # Template directory
|
||
|
|
│ ├── default.hbs # Custom default template
|
||
|
|
│ ├── minimal.hbs # Minimal template example
|
||
|
|
│ └── *.hbs # Additional custom templates
|
||
|
|
├── content/ # Markdown content
|
||
|
|
│ └── *.md # Markdown files
|
||
|
|
└── src/main.rs # Server implementation
|
||
|
|
```
|
||
|
|
|
||
|
|
## Usage Examples
|
||
|
|
|
||
|
|
### Default Template (Built-in)
|
||
|
|
```bash
|
||
|
|
./mhmmcontent
|
||
|
|
# Uses built-in default template
|
||
|
|
```
|
||
|
|
|
||
|
|
### Custom Template from Filesystem
|
||
|
|
```bash
|
||
|
|
./mhmmcontent --template default
|
||
|
|
# Loads from ./layout/default.hbs if it exists
|
||
|
|
```
|
||
|
|
|
||
|
|
### Different Custom Template
|
||
|
|
```bash
|
||
|
|
./mhmmcontent --template minimal
|
||
|
|
# Loads from ./layout/minimal.hbs
|
||
|
|
```
|
||
|
|
|
||
|
|
### Non-existent Template (Error)
|
||
|
|
```bash
|
||
|
|
./mhmmcontent --template nonexistent
|
||
|
|
# Fails with clear error message indicating missing template file
|
||
|
|
```
|
||
|
|
|
||
|
|
## Template Examples
|
||
|
|
|
||
|
|
### Basic Template (`layout/basic.hbs`)
|
||
|
|
```html
|
||
|
|
<!DOCTYPE html>
|
||
|
|
<html>
|
||
|
|
<head>
|
||
|
|
<title>{{title}}</title>
|
||
|
|
<style>
|
||
|
|
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
|
||
|
|
pre { background: #f0f0f0; padding: 10px; overflow-x: auto; }
|
||
|
|
</style>
|
||
|
|
</head>
|
||
|
|
<body>
|
||
|
|
<h1>{{title}}</h1>
|
||
|
|
<div>{{{content}}}</div>
|
||
|
|
<footer><small>Path: {{path}}</small></footer>
|
||
|
|
</body>
|
||
|
|
</html>
|
||
|
|
```
|
||
|
|
|
||
|
|
### Advanced Template (`layout/advanced.hbs`)
|
||
|
|
```html
|
||
|
|
<!DOCTYPE html>
|
||
|
|
<html>
|
||
|
|
<head>
|
||
|
|
<title>{{title}} | My Site</title>
|
||
|
|
<meta charset="utf-8">
|
||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
|
<style>
|
||
|
|
:root {
|
||
|
|
--primary: #2c3e50;
|
||
|
|
--secondary: #3498db;
|
||
|
|
}
|
||
|
|
body {
|
||
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
|
|
line-height: 1.6;
|
||
|
|
color: #333;
|
||
|
|
max-width: 1000px;
|
||
|
|
margin: 0 auto;
|
||
|
|
padding: 20px;
|
||
|
|
}
|
||
|
|
header {
|
||
|
|
background: var(--primary);
|
||
|
|
color: white;
|
||
|
|
padding: 1rem;
|
||
|
|
margin-bottom: 2rem;
|
||
|
|
border-radius: 5px;
|
||
|
|
}
|
||
|
|
nav {
|
||
|
|
display: flex;
|
||
|
|
gap: 1rem;
|
||
|
|
margin: 1rem 0;
|
||
|
|
}
|
||
|
|
nav a {
|
||
|
|
color: white;
|
||
|
|
text-decoration: none;
|
||
|
|
}
|
||
|
|
main {
|
||
|
|
min-height: 300px;
|
||
|
|
}
|
||
|
|
footer {
|
||
|
|
margin-top: 2rem;
|
||
|
|
padding-top: 1rem;
|
||
|
|
border-top: 1px solid #eee;
|
||
|
|
color: #666;
|
||
|
|
font-size: 0.9rem;
|
||
|
|
}
|
||
|
|
</style>
|
||
|
|
</head>
|
||
|
|
<body>
|
||
|
|
<header>
|
||
|
|
<h1>My Site</h1>
|
||
|
|
<nav>
|
||
|
|
<a href="/">Home</a>
|
||
|
|
<a href="/about">About</a>
|
||
|
|
<a href="/contact">Contact</a>
|
||
|
|
</nav>
|
||
|
|
<div style="font-size: 0.9rem; opacity: 0.8;">
|
||
|
|
Current page: {{path}}
|
||
|
|
</div>
|
||
|
|
</header>
|
||
|
|
<main>
|
||
|
|
<h2>{{title}}</h2>
|
||
|
|
{{{content}}}
|
||
|
|
</main>
|
||
|
|
<footer>
|
||
|
|
© 2023 My Site. Powered by Rust Markdown Server.
|
||
|
|
</footer>
|
||
|
|
</body>
|
||
|
|
</html>
|
||
|
|
```
|
||
|
|
|
||
|
|
## Benefits
|
||
|
|
|
||
|
|
### For Users
|
||
|
|
- **Custom Branding**: Easy to add logos, colors, and branding
|
||
|
|
- **Consistent Layout**: Maintain consistent header/footer across all pages
|
||
|
|
- **Navigation**: Add navigation menus to all pages automatically
|
||
|
|
- **Responsive Design**: Create mobile-friendly layouts
|
||
|
|
|
||
|
|
### For Developers
|
||
|
|
- **Separation of Concerns**: Content (Markdown) separate from presentation (HTML)
|
||
|
|
- **Easy Customization**: No need to modify server code to change appearance
|
||
|
|
- **Multiple Themes**: Support different templates for different use cases
|
||
|
|
- **Fallback Safety**: Server continues to work even if templates are missing
|
||
|
|
|
||
|
|
## Technical Implementation
|
||
|
|
|
||
|
|
### Dependencies Added
|
||
|
|
```toml
|
||
|
|
[dependencies]
|
||
|
|
handlebars = "4.0" # Templating engine
|
||
|
|
```
|
||
|
|
|
||
|
|
### Key Functions
|
||
|
|
- `load_template()`: Loads and registers templates
|
||
|
|
- `serve_markdown()`: Renders content using templates
|
||
|
|
- Template fallback mechanism for robustness
|
||
|
|
|
||
|
|
### Error Handling
|
||
|
|
- **File system errors**: Clear error messages for missing template files
|
||
|
|
- **Template parsing errors**: Detailed error messages for invalid template syntax
|
||
|
|
- **Rendering errors**: Fallback to simple HTML if template rendering fails
|
||
|
|
- **Strict Requirements**: Server fails fast if required template is missing
|
||
|
|
|
||
|
|
## Performance
|
||
|
|
|
||
|
|
- **Fast Template Loading**: Templates loaded once at startup
|
||
|
|
- **Efficient Rendering**: Handlebars provides fast template rendering
|
||
|
|
- **Minimal Overhead**: Template processing adds negligible latency
|
||
|
|
- **Memory Efficient**: Templates stored in memory after initial load
|
||
|
|
|
||
|
|
The template feature makes the Markdown server highly customizable while maintaining robustness and ease of use.
|