using Astralis; using Invercargill; using Invercargill.DataStructures; /** * PathRouting Example * * Demonstrates path component parsing and routing in Astralis. * Uses the Router's named segment feature for dynamic routing. */ // Root handler - shows available endpoints class RootHandler : Object, RouteHandler { public async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error { 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 GET /pathinfo - Path information demo """ ); } } // Simple hello handler class HelloHandler : Object, RouteHandler { public async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error { return new BufferedHttpResult.from_string("Hello, World!"); } } // Hello with name handler - uses named segment class HelloNameHandler : Object, RouteHandler { public async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error { var name = route_context.get_segment("name"); return new BufferedHttpResult.from_string(@"Hello, $name!"); } } // Users list handler class UsersHandler : Object, RouteHandler { public async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error { 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 handler - uses named segment class UserByIdHandler : Object, RouteHandler { public async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error { var id_str = route_context.get_segment("id"); 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 handler - uses named segment class UserPostsHandler : Object, RouteHandler { public async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error { var id_str = route_context.get_segment("id"); 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 ); } } // API status handler class ApiStatusHandler : Object, RouteHandler { public async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error { 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 item handler - uses named segment class ApiItemHandler : Object, RouteHandler { public async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error { var id_str = route_context.get_segment("id"); 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 handler - uses two named segments class FilesHandler : Object, RouteHandler { public async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error { var category = route_context.get_segment("category"); var name = route_context.get_segment("name"); // Simulated file lookup var files = new Dictionary>(); var docs = new Dictionary(); docs.set("readme.txt", new ExampleFile("readme.txt", "docs", "This is the readme file")); docs.set("guide.pdf", new ExampleFile("guide.pdf", "docs", "User guide")); var images = new Dictionary(); images.set("logo.png", new ExampleFile("logo.png", "images", "Company logo")); images.set("banner.jpg", new ExampleFile("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)) { ExampleFile? 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 info handler class PathInfoHandler : Object, RouteHandler { public async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error { var parts = new Series(); parts.add("Path Information:\n"); parts.add(@" Raw path: $(http_context.request.raw_path)\n"); parts.add(@" Path components: $(http_context.request.path_components.count())\n"); parts.add("\n Components:\n"); http_context.request.path_components .with_positions() .iterate((pair) => { parts.add(@" [$((int)pair.position)]: $(pair.item)\n"); }); parts.add(@"\n Query string: $(http_context.request.query_string)\n"); // Show named segments from route context parts.add("\n Named segments:\n"); route_context.named_segments.to_immutable_buffer() .iterate((kv) => { parts.add(@" $(kv.key): $(kv.value)\n"); }); var result = parts.to_immutable_buffer() .aggregate("", (acc, s) => acc + s); return new BufferedHttpResult.from_string(result); } } void main() { var router = new Router(); var server = new Server(8082, router); // Register routes with named segments router.get("/", new RootHandler()); router.get("/hello", new HelloHandler()); router.get("/hello/{name}", new HelloNameHandler()); router.get("/users", new UsersHandler()); router.get("/users/{id}", new UserByIdHandler()); router.get("/users/{id}/posts", new UserPostsHandler()); router.get("/api/v1/status", new ApiStatusHandler()); router.get("/api/v1/items/{id}", new ApiItemHandler()); router.get("/files/{category}/{name}", new FilesHandler()); router.get("/pathinfo", new PathInfoHandler()); 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 ExampleFile { public string name { get; private set; } public string category { get; private set; } public string description { get; private set; } public ExampleFile(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\" }"; } }