## Home made web framework written in Rust What you are looking at is my homemade web framework. It started as pet project and probably continue to be one, but now it has enough features to start simple web page, it even has templates. Is it ready for production? Not in any case. It doesn't have escaping. It doesn't parse input headers. No middleware. List could go on and on. But, I have personal page that runs on this framework at http://glotitude.datamonkey.pro/. Feel free to look at it. ### Fast start * You should use nightly build because of `concat_idents!` macro * Add dependencies to ctchi framework ``` [dependencies] ctchi = "0.18.0" ctchi_codegen = "0.2.0" regex = "1" ``` * Write your controller and main function. You need all imports below ```rust #![feature(concat_idents)] #[macro_use] extern crate ctchi; use ctchi::core::app::Ctchi; use ctchi::core::routes::{Routes, Route}; use ctchi_codegen::route; #[route("/")] fn index() -> String { render!("index.html") } fn main() { let mut routes = Routes::new(); // add route to your controller routes.add_route(routes!(index)()); // create and run local server let server = Ctchi::new(routes); let server_result = match server.start() { Ok(()) => "Ctchi application server is successfully running!".to_string(), Err(err) => format!("Can't start server! Because '{}'", err) }; } ``` * Run it :) ```shell script cargo run ``` ### Configuration Ctchi has just a few configuration options: 1. `bind_path` - ip address and port for the server (default is `127.0.0.1:8080`) 2. `base_path` - path to the folder with templates (default is current_dir + `/src/pages/`) 3. `static_uri_pref` - url prefix for static files, css/js/images etc (default is `static`) 4. `log_path` - path on the file system to the log file. There are several ways to change configuration of the server: #### Change configuration file You should create it at `/etc/ctchi/conf.txt`. You can overwrite only 2 properties there `bind_path` and `base_path`. #### Configuration singleton You can get ctchi configuration in any place of your program by importing `use ctchi::core::config::get_configuration;` Next you can get a reader and then mutex for configuration: ```rust // get reader let config_reader = get_configuration(); // get mutex let config = config_reader.inner.lock().unwrap(); // read some propeties let prefix = &config.static_uri_pref; // drop mutex drop(config); ``` `drop` is very important. You can omit it and it will be automatically called at the end of the scope, but, if you try to get another config inside the scope program wouldn't work. So, if you absolutely sure that there wouldn't be second call for configuration you don't need `drop`, if not, better to call it explicitly. ### Template Ctchi has html templates engine. It isn't sophisticated, but it has all core features you need. What kind of tags it has. 1. [template][endtemplate] 2. [for i in values][endfor] 3. [if value][endif] 4. [code][endcode] 5. [import "./base.html" /] 6. [[value]] `[template]` is root tag, if you have it on the page it is html page with tags, otherwise ctchi would consider it plane html page. `[for]` tag is for loops. You can pass values (vector of strings) in context and it write inner part so many time as values length. `[if]` takes boolean value from context and writes inner block if values is true. It hasn't `else` clause. Is can't take expression. So, the whole logic should be on backend. `[code]` consider everything inside as pure html. Great for long code snippets, since you don't need to escape every square bracket in it. `[import]` gets page from specified page and import it into current template. Every rule about tags applies to that page as well. `[[value]]`. Plain value or variable of for loop should be taken in double square brackets. Every tag except import and value tags, should has closing part. #### Logging If you want to write a log of your requests and responses, you should add logger.init into your main function. ```rust use ctchi::log::logger; fn main() { logger::init(); // ... other code } ``` Configuration has a `log_path` field, you can change it either through configuration file, either through configuration object :) ```rust fn main() { // ... other code let config_reader = get_configuration(); let mut config = config_reader.inner.lock().unwrap(); config.log_path = "/Users/glotitude/log/ctchi/server.log".to_string(); drop(config); // ... other code } ``` #### Example Remember, all html pages should be in `src/pages` folder by default. Now, say, we have same header for every page. Let's put it into `header.html`: ```html Imported Header ``` And we have index.html: ```html [template] [import "header.html"/] [if authorized] Hello [[user_name]]. [endif] Your options for today are: [endtemplate] ``` So, how our controller would look for such template: ```rust #[route("/")] fn index()-> String { let mut context = HashMap::::new(); context.insert("authorized".to_string(), Context::BooleanValue(true)); context.insert("user_name".to_string(), Context::SingleValue("Leonid Toshchev".to_string())); context.insert("options".to_string(), Context::MultiValue(vec!("Eat".to_string(), "Code".to_string(), "Sleep".to_string()))); render!("index.html", context) } ```