2026-01-25 15:23:57 +05:30
//! Pugz Store Demo - A comprehensive e-commerce demo showcasing Pugz capabilities
2026-01-22 11:10:47 +05:30
//!
2026-01-25 15:23:57 +05:30
//! Features demonstrated:
//! - Template inheritance (extends/block)
//! - Partial includes (header, footer)
//! - Mixins with parameters (product-card, rating, forms)
//! - Conditionals and loops
//! - Data binding
//! - Pretty printing
2026-01-22 11:10:47 +05:30
const std = @import ( " std " ) ;
const httpz = @import ( " httpz " ) ;
const pugz = @import ( " pugz " ) ;
const Allocator = std . mem . Allocator ;
2026-01-25 15:23:57 +05:30
// ============================================================================
// Data Types
// ============================================================================
const Product = struct {
id : [ ] const u8 ,
name : [ ] const u8 ,
price : [ ] const u8 ,
image : [ ] const u8 ,
rating : u8 ,
category : [ ] const u8 ,
categorySlug : [ ] const u8 ,
sale : bool = false ,
description : [ ] const u8 = " " ,
reviewCount : [ ] const u8 = " 0 " ,
} ;
const Category = struct {
name : [ ] const u8 ,
slug : [ ] const u8 ,
icon : [ ] const u8 ,
count : [ ] const u8 ,
active : bool = false ,
} ;
const CartItem = struct {
id : [ ] const u8 ,
name : [ ] const u8 ,
price : [ ] const u8 ,
image : [ ] const u8 ,
variant : [ ] const u8 ,
quantity : [ ] const u8 ,
total : [ ] const u8 ,
} ;
const Cart = struct {
items : [ ] const CartItem ,
subtotal : [ ] const u8 ,
shipping : [ ] const u8 ,
discount : ? [ ] const u8 = null ,
discountCode : ? [ ] const u8 = null ,
tax : [ ] const u8 ,
total : [ ] const u8 ,
} ;
const ShippingMethod = struct {
id : [ ] const u8 ,
name : [ ] const u8 ,
time : [ ] const u8 ,
price : [ ] const u8 ,
} ;
const State = struct {
code : [ ] const u8 ,
name : [ ] const u8 ,
} ;
// ============================================================================
// Sample Data
// ============================================================================
const sample_products = [ _ ] Product {
. {
. id = " 1 " ,
. name = " Wireless Headphones " ,
. price = " 79.99 " ,
. image = " /images/headphones.jpg " ,
. rating = 4 ,
. category = " Electronics " ,
. categorySlug = " electronics " ,
. sale = true ,
. description = " Premium wireless headphones with noise cancellation " ,
. reviewCount = " 128 " ,
} ,
. {
. id = " 2 " ,
. name = " Smart Watch Pro " ,
. price = " 199.99 " ,
. image = " /images/watch.jpg " ,
. rating = 5 ,
. category = " Electronics " ,
. categorySlug = " electronics " ,
. description = " Advanced fitness tracking and notifications " ,
. reviewCount = " 256 " ,
} ,
. {
. id = " 3 " ,
. name = " Laptop Stand " ,
. price = " 49.99 " ,
. image = " /images/stand.jpg " ,
. rating = 4 ,
. category = " Accessories " ,
. categorySlug = " accessories " ,
. description = " Ergonomic aluminum laptop stand " ,
. reviewCount = " 89 " ,
} ,
. {
. id = " 4 " ,
. name = " USB-C Hub " ,
. price = " 39.99 " ,
. image = " /images/hub.jpg " ,
. rating = 4 ,
. category = " Accessories " ,
. categorySlug = " accessories " ,
. sale = true ,
. description = " 7-in-1 USB-C hub with HDMI and card reader " ,
. reviewCount = " 312 " ,
} ,
. {
. id = " 5 " ,
. name = " Mechanical Keyboard " ,
. price = " 129.99 " ,
. image = " /images/keyboard.jpg " ,
. rating = 5 ,
. category = " Electronics " ,
. categorySlug = " electronics " ,
. description = " RGB mechanical keyboard with Cherry MX switches " ,
. reviewCount = " 445 " ,
} ,
. {
. id = " 6 " ,
. name = " Desk Lamp " ,
. price = " 34.99 " ,
. image = " /images/lamp.jpg " ,
. rating = 4 ,
. category = " Home Office " ,
. categorySlug = " home-office " ,
. description = " LED desk lamp with adjustable brightness " ,
. reviewCount = " 67 " ,
} ,
} ;
const sample_categories = [ _ ] Category {
. { . name = " Electronics " , . slug = " electronics " , . icon = " E " , . count = " 24 " } ,
. { . name = " Accessories " , . slug = " accessories " , . icon = " A " , . count = " 18 " } ,
. { . name = " Home Office " , . slug = " home-office " , . icon = " H " , . count = " 12 " } ,
. { . name = " Clothing " , . slug = " clothing " , . icon = " C " , . count = " 36 " } ,
} ;
const sample_cart_items = [ _ ] CartItem {
. {
. id = " 1 " ,
. name = " Wireless Headphones " ,
. price = " 79.99 " ,
. image = " /images/headphones.jpg " ,
. variant = " Black " ,
. quantity = " 1 " ,
. total = " 79.99 " ,
} ,
. {
. id = " 5 " ,
. name = " Mechanical Keyboard " ,
. price = " 129.99 " ,
. image = " /images/keyboard.jpg " ,
. variant = " RGB " ,
. quantity = " 1 " ,
. total = " 129.99 " ,
} ,
} ;
const sample_cart = Cart {
. items = & sample_cart_items ,
. subtotal = " 209.98 " ,
. shipping = " 0 " ,
. tax = " 18.90 " ,
. total = " 228.88 " ,
} ;
const shipping_methods = [ _ ] ShippingMethod {
. { . id = " standard " , . name = " Standard Shipping " , . time = " 5-7 business days " , . price = " 0 " } ,
. { . id = " express " , . name = " Express Shipping " , . time = " 2-3 business days " , . price = " 9.99 " } ,
. { . id = " overnight " , . name = " Overnight Shipping " , . time = " Next business day " , . price = " 19.99 " } ,
} ;
const us_states = [ _ ] State {
. { . code = " CA " , . name = " California " } ,
. { . code = " NY " , . name = " New York " } ,
. { . code = " TX " , . name = " Texas " } ,
. { . code = " FL " , . name = " Florida " } ,
. { . code = " WA " , . name = " Washington " } ,
} ;
// ============================================================================
// Application
// ============================================================================
2026-01-22 11:10:47 +05:30
const App = struct {
allocator : Allocator ,
view : pugz . ViewEngine ,
2026-01-25 15:23:57 +05:30
pub fn init ( allocator : Allocator ) ! App {
2026-01-22 11:10:47 +05:30
return . {
. allocator = allocator ,
2026-01-27 16:04:02 +05:30
. view = pugz . ViewEngine . init ( . {
2026-01-22 11:10:47 +05:30
. views_dir = " views " ,
2026-01-25 15:23:57 +05:30
. pretty = true ,
2026-01-22 11:10:47 +05:30
} ) ,
} ;
}
2026-01-25 15:23:57 +05:30
pub fn deinit ( self : * App ) void {
self . view . deinit ( ) ;
}
2026-01-22 11:10:47 +05:30
} ;
2026-01-25 15:23:57 +05:30
// ============================================================================
// Request Handlers
// ============================================================================
2026-01-22 11:10:47 +05:30
2026-01-25 15:23:57 +05:30
fn home ( app : * App , _ : * httpz . Request , res : * httpz . Response ) ! void {
const html = app . view . render ( res . arena , " pages/home " , . {
. title = " Home " ,
. cartCount = " 2 " ,
. authenticated = true ,
. items = & [ _ ] [ ] const u8 { " Wireless Headphones " , " Smart Watch " , " Laptop Stand " , " USB-C Hub " } ,
} ) catch | err | {
return renderError ( res , err ) ;
} ;
2026-01-22 11:10:47 +05:30
2026-01-25 15:23:57 +05:30
res . content_type = . HTML ;
res . body = html ;
}
2026-01-22 11:10:47 +05:30
2026-01-25 15:23:57 +05:30
fn products ( app : * App , _ : * httpz . Request , res : * httpz . Response ) ! void {
const html = app . view . render ( res . arena , " pages/products " , . {
. title = " All Products " ,
. cartCount = " 2 " ,
. productCount = " 6 " ,
} ) catch | err | {
return renderError ( res , err ) ;
} ;
2026-01-22 11:10:47 +05:30
2026-01-25 15:23:57 +05:30
res . content_type = . HTML ;
res . body = html ;
}
2026-01-22 11:10:47 +05:30
2026-01-25 15:23:57 +05:30
fn productDetail ( app : * App , req : * httpz . Request , res : * httpz . Response ) ! void {
const id = req . param ( " id " ) orelse " 1 " ;
_ = id ;
2026-01-22 11:10:47 +05:30
2026-01-25 15:23:57 +05:30
const html = app . view . render ( res . arena , " pages/product-detail " , . {
. cartCount = " 2 " ,
. productName = " Wireless Headphones " ,
. category = " Electronics " ,
. price = " 79.99 " ,
. description = " Premium wireless headphones with active noise cancellation. Experience crystal-clear audio whether you're working, traveling, or relaxing at home. " ,
. sku = " WH-001-BLK " ,
} ) catch | err | {
return renderError ( res , err ) ;
} ;
2026-01-22 11:10:47 +05:30
2026-01-25 15:23:57 +05:30
res . content_type = . HTML ;
res . body = html ;
2026-01-22 11:10:47 +05:30
}
2026-01-25 15:23:57 +05:30
fn cart ( app : * App , _ : * httpz . Request , res : * httpz . Response ) ! void {
const html = app . view . render ( res . arena , " pages/cart " , . {
. title = " Shopping Cart " ,
. cartCount = " 2 " ,
. cartItems = & sample_cart_items ,
. subtotal = sample_cart . subtotal ,
. shipping = sample_cart . shipping ,
. tax = sample_cart . tax ,
. total = sample_cart . total ,
2026-01-22 11:10:47 +05:30
} ) catch | err | {
2026-01-25 15:23:57 +05:30
return renderError ( res , err ) ;
2026-01-22 11:10:47 +05:30
} ;
res . content_type = . HTML ;
res . body = html ;
}
2026-01-25 15:23:57 +05:30
fn about ( app : * App , _ : * httpz . Request , res : * httpz . Response ) ! void {
const html = app . view . render ( res . arena , " pages/about " , . {
. title = " About " ,
. cartCount = " 2 " ,
2026-01-22 11:10:47 +05:30
} ) catch | err | {
2026-01-25 15:23:57 +05:30
return renderError ( res , err ) ;
2026-01-22 11:10:47 +05:30
} ;
res . content_type = . HTML ;
res . body = html ;
}
2026-01-25 16:35:27 +05:30
fn includeDemo ( app : * App , _ : * httpz . Request , res : * httpz . Response ) ! void {
const html = app . view . render ( res . arena , " pages/include-demo " , . {
. title = " Include Demo " ,
. cartCount = " 2 " ,
} ) catch | err | {
return renderError ( res , err ) ;
} ;
res . content_type = . HTML ;
res . body = html ;
}
2026-01-25 15:23:57 +05:30
fn notFound ( app : * App , _ : * httpz . Request , res : * httpz . Response ) ! void {
res . status = 404 ;
const html = app . view . render ( res . arena , " pages/404 " , . {
. title = " Page Not Found " ,
. cartCount = " 2 " ,
2026-01-22 11:10:47 +05:30
} ) catch | err | {
2026-01-25 15:23:57 +05:30
return renderError ( res , err ) ;
2026-01-22 11:10:47 +05:30
} ;
res . content_type = . HTML ;
res . body = html ;
}
2026-01-25 15:23:57 +05:30
fn renderError ( res : * httpz . Response , err : anyerror ) void {
res . status = 500 ;
res . content_type = . HTML ;
res . body = std . fmt . allocPrint ( res . arena ,
\\<!DOCTYPE html>
\\<html>
\\<head><title>Error</title></head>
\\<body>
\\<h1>500 - Server Error</h1>
\\<p>Error: {s}</p>
\\</body>
\\</html>
, . { @errorName ( err ) } ) catch " Internal Server Error " ;
}
// ============================================================================
// Static Files
// ============================================================================
fn serveStatic ( _ : * App , req : * httpz . Request , res : * httpz . Response ) ! void {
const path = req . url . path ;
// Strip leading slash and prepend public folder
const rel_path = if ( path . len > 0 and path [ 0 ] = = '/' ) path [ 1 . . ] else path ;
const full_path = std . fmt . allocPrint ( res . arena , " public/{s} " , . { rel_path } ) catch {
2026-01-22 11:10:47 +05:30
res . status = 500 ;
2026-01-25 15:23:57 +05:30
res . body = " Internal Server Error " ;
2026-01-22 11:10:47 +05:30
return ;
} ;
2026-01-25 15:23:57 +05:30
// Read file from disk
const content = std . fs . cwd ( ) . readFileAlloc ( res . arena , full_path , 10 * 1024 * 1024 ) catch {
res . status = 404 ;
res . body = " Not Found " ;
return ;
} ;
// Set content type based on extension
if ( std . mem . endsWith ( u8 , path , " .css " ) ) {
res . content_type = . CSS ;
} else if ( std . mem . endsWith ( u8 , path , " .js " ) ) {
res . content_type = . JS ;
} else if ( std . mem . endsWith ( u8 , path , " .html " ) ) {
res . content_type = . HTML ;
}
res . body = content ;
}
// ============================================================================
// Main
// ============================================================================
pub fn main ( ) ! void {
var gpa = std . heap . GeneralPurposeAllocator ( . { } ) { } ;
defer if ( gpa . deinit ( ) = = . leak ) @panic ( " leak " ) ;
const allocator = gpa . allocator ( ) ;
var app = try App . init ( allocator ) ;
defer app . deinit ( ) ;
const port = 8081 ;
var server = try httpz . Server ( * App ) . init ( allocator , . { . port = port } , & app ) ;
defer server . deinit ( ) ;
var router = try server . router ( . { } ) ;
// Pages
router . get ( " / " , home , . { } ) ;
router . get ( " /products " , products , . { } ) ;
router . get ( " /products/:id " , productDetail , . { } ) ;
router . get ( " /cart " , cart , . { } ) ;
router . get ( " /about " , about , . { } ) ;
2026-01-25 16:35:27 +05:30
router . get ( " /include-demo " , includeDemo , . { } ) ;
2026-01-25 15:23:57 +05:30
// Static files
router . get ( " /css/* " , serveStatic , . { } ) ;
std . debug . print (
\\
\\ ____ ____ _
\\ | _ \ _ _ __ _ ____ / ___|| |_ ___ _ __ ___
\\ | |_) | | | |/ _` |_ / \___ \| __/ _ \| '__/ _ \
\\ | __/| |_| | (_| |/ / ___) | || (_) | | | __/
\\ |_| \__,_|\__, /___| |____/ \__\___/|_| \___|
\\ |___/
\\
\\ Server running at http://localhost:{d}
\\
\\ Routes:
\\ GET / - Home page
\\ GET /products - Products page
\\ GET /products/:id - Product detail
\\ GET /cart - Shopping cart
\\ GET /about - About page
2026-01-25 16:35:27 +05:30
\\ GET /include-demo - Include directive demo
2026-01-25 15:23:57 +05:30
\\
\\ Press Ctrl+C to stop.
\\
, . { port } ) ;
try server . listen ( ) ;
2026-01-22 11:10:47 +05:30
}