using Astralis; using Invercargill; using Invercargill.DataStructures; /** * PathRouting Example * * Demonstrates path component parsing and routing in Astralis. * Uses Invercargill Enumerable for path processing. */ void main() { var router = new Router(); var server = new Server(8082, router); // Root path router.map_func("/", (context) => { return new BufferedHttpResult.from_string( """Welcome to the Path Routing Example! Available endpoints: GET / - This message GET /hello - Simple greeting GET /hello/{name} - Greeting with name GET /users - List all users GET /users/{id} - Get user by ID GET /users/{id}/posts - Get posts for a user GET /api/v1/status - API status GET /api/v1/items/{id} - Get item by ID GET /files/{category}/{name} - Get file by category and name """ ); }); // Simple path router.map_func("/hello", (context) => { return new BufferedHttpResult.from_string("Hello, World!"); }); // Path with one component (simulated dynamic routing) router.map_func("/hello/", (context) => { var components = context.request.path_components.to_vector(); if (components.count() < 2) { return new BufferedHttpResult.from_string( "Please provide a name: /hello/{name}", StatusCode.BAD_REQUEST ); } var name = components[1]; return new BufferedHttpResult.from_string(@"Hello, $name!"); }); // Users list endpoint router.map_func("/users", (context) => { // Simulated user data using Invercargill data structures var users = new Series(); users.add(new User(1, "Alice", "alice@example.com")); users.add(new User(2, "Bob", "bob@example.com")); users.add(new User(3, "Charlie", "charlie@example.com")); var json_parts = new Series(); json_parts.add(@"{ \"users\": ["); bool first = true; users.to_immutable_buffer().iterate((user) => { if (!first) json_parts.add(", "); json_parts.add(user.to_json()); first = false; }); json_parts.add("] }"); var json_string = json_parts.to_immutable_buffer() .aggregate("", (acc, s) => acc + s); var headers = new Catalogue(); headers.add("Content-Type", "application/json"); return new BufferedHttpResult.from_string( json_string, StatusCode.OK, headers ); }); // User by ID endpoint (simulated) router.map_func("/users/", (context) => { var components = context.request.path_components.to_vector(); if (components.count() < 2) { var headers = new Catalogue(); headers.add("Content-Type", "application/json"); return new BufferedHttpResult.from_string( @"{ \"error\": \"User ID required\" }", StatusCode.BAD_REQUEST, headers ); } var id_str = components[1]; var id = int.parse(id_str); // Simulated user lookup var users = new Series(); users.add(new User(1, "Alice", "alice@example.com")); users.add(new User(2, "Bob", "bob@example.com")); users.add(new User(3, "Charlie", "charlie@example.com")); var user = users.to_immutable_buffer() .first_or_default(u => u.id == id); if (user == null) { var headers = new Catalogue(); headers.add("Content-Type", "application/json"); return new BufferedHttpResult.from_string( @"{ \"error\": \"User not found\" }", StatusCode.NOT_FOUND, headers ); } var headers = new Catalogue(); headers.add("Content-Type", "application/json"); return new BufferedHttpResult.from_string( user.to_json(), StatusCode.OK, headers ); }); // User posts endpoint (nested path) router.map_func("/users/", (context) => { var components = context.request.path_components.to_vector(); // Check for /users/{id}/posts pattern if (components.count() >= 3 && components[2] == "posts") { var id_str = components[1]; var id = int.parse(id_str); // Simulated posts for user var posts = new Series(); posts.add(new Post(101, id, "First Post", "This is my first post")); posts.add(new Post(102, id, "Second Post", "This is my second post")); var json_parts = new Series(); json_parts.add(@"{ \"user_id\": $id, \"posts\": ["); bool first = true; posts.to_immutable_buffer().iterate((post) => { if (!first) json_parts.add(", "); json_parts.add(post.to_json()); first = false; }); json_parts.add("] }"); var json_string = json_parts.to_immutable_buffer() .aggregate("", (acc, s) => acc + s); var headers = new Catalogue(); headers.add("Content-Type", "application/json"); return new BufferedHttpResult.from_string( json_string, StatusCode.OK, headers ); } // Fall back to user detail if (components.count() < 2) { var headers = new Catalogue(); headers.add("Content-Type", "application/json"); return new BufferedHttpResult.from_string( @"{ \"error\": \"User ID required\" }", StatusCode.BAD_REQUEST, headers ); } var id_str = components[1]; var id = int.parse(id_str); var users = new Series(); users.add(new User(1, "Alice", "alice@example.com")); users.add(new User(2, "Bob", "bob@example.com")); users.add(new User(3, "Charlie", "charlie@example.com")); var user = users.to_immutable_buffer() .first_or_default(u => u.id == id); if (user == null) { var headers = new Catalogue(); headers.add("Content-Type", "application/json"); return new BufferedHttpResult.from_string( @"{ \"error\": \"User not found\" }", StatusCode.NOT_FOUND, headers ); } var headers = new Catalogue(); headers.add("Content-Type", "application/json"); return new BufferedHttpResult.from_string( user.to_json(), StatusCode.OK, headers ); }); // API versioned endpoint router.map_func("/api/v1/status", (context) => { var status = new Dictionary(); status.set("status", "operational"); status.set("version", "1.0.0"); status.set("timestamp", new DateTime.now_local().format_iso8601()); var json_parts = new Series(); json_parts.add("{ "); bool first = true; status.to_immutable_buffer().iterate((kv) => { if (!first) json_parts.add(", "); json_parts.add(@"\"$(kv.key)\": \"$(kv.value)\""); first = false; }); json_parts.add(" }"); var json_string = json_parts.to_immutable_buffer() .aggregate("", (acc, s) => acc + s); var headers = new Catalogue(); headers.add("Content-Type", "application/json"); return new BufferedHttpResult.from_string( json_string, StatusCode.OK, headers ); }); // API items endpoint router.map_func("/api/v1/items/", (context) => { var components = context.request.path_components.to_vector(); if (components.count() < 4) { var headers = new Catalogue(); headers.add("Content-Type", "application/json"); return new BufferedHttpResult.from_string( @"{ \"error\": \"Item ID required\" }", StatusCode.BAD_REQUEST, headers ); } var id_str = components[3]; var id = int.parse(id_str); var items = new Dictionary(); items.set(1, new Item(1, "Widget", "A useful widget", 9.99)); items.set(2, new Item(2, "Gadget", "A fancy gadget", 19.99)); items.set(3, new Item(3, "Doohickey", "A mysterious doohickey", 29.99)); Item? item = null; if (items.try_get(id, out item)) { var headers = new Catalogue(); headers.add("Content-Type", "application/json"); return new BufferedHttpResult.from_string( item.to_json(), StatusCode.OK, headers ); } var headers = new Catalogue(); headers.add("Content-Type", "application/json"); return new BufferedHttpResult.from_string( @"{ \"error\": \"Item not found\" }", StatusCode.NOT_FOUND, headers ); }); // Files endpoint with category and name router.map_func("/files/", (context) => { var components = context.request.path_components.to_vector(); if (components.count() < 3) { var headers = new Catalogue(); headers.add("Content-Type", "application/json"); return new BufferedHttpResult.from_string( @"{ \"error\": \"Category and filename required\" }", StatusCode.BAD_REQUEST, headers ); } var category = components[1]; var name = components[2]; // Simulated file lookup var files = new Dictionary>(); var docs = new Dictionary(); docs.set("readme.txt", new File("readme.txt", "docs", "This is the readme file")); docs.set("guide.pdf", new File("guide.pdf", "docs", "User guide")); var images = new Dictionary(); images.set("logo.png", new File("logo.png", "images", "Company logo")); images.set("banner.jpg", new File("banner.jpg", "images", "Website banner")); files.set("docs", docs); files.set("images", images); Dictionary? category_files = null; if (files.try_get(category, out category_files)) { File? file = null; if (category_files.try_get(name, out file)) { var headers = new Catalogue(); headers.add("Content-Type", "application/json"); return new BufferedHttpResult.from_string( file.to_json(), StatusCode.OK, headers ); } } var headers = new Catalogue(); headers.add("Content-Type", "application/json"); return new BufferedHttpResult.from_string( @"{ \"error\": \"File not found in category '$category'\" }", StatusCode.NOT_FOUND, headers ); }); // Path information endpoint - shows all path components router.map_func("/pathinfo", (context) => { var parts = new Series(); parts.add("Path Information:\n"); parts.add(@" Raw path: $(context.request.raw_path)\n"); parts.add(@" Path components: $(context.request.path_components.count())\n"); parts.add("\n Components:\n"); context.request.path_components .with_positions() .iterate((pair) => { parts.add(@" [$((int)pair.position)]: $(pair.item)\n"); }); parts.add(@"\n Query string: $(context.request.query_string)\n"); var result = parts.to_immutable_buffer() .aggregate("", (acc, s) => acc + s); return new BufferedHttpResult.from_string(result); }); print("Path Routing Example Server running on port 8082\n"); print("Try these endpoints:\n"); print(" - http://localhost:8082/\n"); print(" - http://localhost:8082/hello\n"); print(" - http://localhost:8082/hello/Alice\n"); print(" - http://localhost:8082/users\n"); print(" - http://localhost:8082/users/1\n"); print(" - http://localhost:8082/users/1/posts\n"); print(" - http://localhost:8082/api/v1/status\n"); print(" - http://localhost:8082/api/v1/items/2\n"); print(" - http://localhost:8082/files/docs/readme.txt\n"); print(" - http://localhost:8082/pathinfo?test=1\n"); server.run(); } // Helper classes for the example class User { public int id { get; private set; } public string name { get; private set; } public string email { get; private set; } public User(int id, string name, string email) { this.id = id; this.name = name; this.email = email; } public string to_json() { return @"{ \"id\": $id, \"name\": \"$name\", \"email\": \"$email\" }"; } } class Post { public int id { get; private set; } public int user_id { get; private set; } public string title { get; private set; } public string content { get; private set; } public Post(int id, int user_id, string title, string content) { this.id = id; this.user_id = user_id; this.title = title; this.content = content; } public string to_json() { return @"{ \"id\": $id, \"user_id\": $user_id, \"title\": \"$title\", \"content\": \"$content\" }"; } } class Item { public int id { get; private set; } public string name { get; private set; } public string description { get; private set; } public double price { get; private set; } public Item(int id, string name, string description, double price) { this.id = id; this.name = name; this.description = description; this.price = price; } public string to_json() { return @"{ \"id\": $id, \"name\": \"$name\", \"description\": \"$description\", \"price\": $price }"; } } class File { public string name { get; private set; } public string category { get; private set; } public string description { get; private set; } public File(string name, string category, string description) { this.name = name; this.category = category; this.description = description; } public string to_json() { return @"{ \"name\": \"$name\", \"category\": \"$category\", \"description\": \"$description\" }"; } }