# Rocket
## 環境
* rustc 1.52.0-nightly (fe1bf8e05 2021-02-23)
## インストール
* rustはnightlyが必要(20210226時点)
```bash
mkdir -p app
cd app
cargo init
```
`Cargo.toml`の`[dependencies]`に追加
```toml
rocket = "0.4.6"
```
## HelloWorld
* デフォルトポートは8000
```rust
////src/main.rs
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use] extern crate rocket;
#[get("/")]
fn index() -> &'static str {
"HelloWorld"
}
fn main() {
rocket::ignite()
.mount("/", routes![index])
.launch();
}
```
```bash
cargo build
cargo run
```
## Step2
md/[name].htmlで[name].mdのマークダウンを変換して表示
トップページはmd/README.mdを表示
サイドバーにMarkdownの一覧表示
* Markdownを表示させる
* ポートを`3000`に変更
* 正規表現のためにregexクレートを追加
* jsonのシリアライズにserdeクレートを追加
* ビューのテンプレートエンジンにHandlebarを利用
`dependencies`を修正
```toml
~~~
[dependencies]
regex = "0.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.0"
pulldown-cmark = "0.1.2"
rocket = "0.4.6"
rocket_contrib = { version = "0.4", features = ["json","handlebars_templates"] }
handlebars = "3.5"
```
```toml
////Rocket.toml
[global]
address = "0.0.0.0"
port = 3000
template_dir = "templates/"
```
```hbs
{{!--templates/md.hbs--}}
{{#*inline "content"}}
{{{text}}}
{{/inline}}
{{~> layouts/default~}}
```
```hbs
{{!--templates/layouts/default.hbs--}}
```
```css
/*static/style.css*/
* {
--bg: rgba(255, 255, 255, 0.7);
--vp: 10px;
--hp: 20px;
}
/**/
body {
margin: 0;
overflow-y: scroll;
font-family: "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", "Yu Gothic Medium", "游ゴシック Medium", YuGothic, "游ゴシック体", "メイリオ", sans-serif;
}
#root {
display: flex;
min-height: 100vh;
flex-direction: column;
}
/**/
a:hover {
opacity: 0.7;
}
.container {
margin: 0 auto;
width: 100%;
max-width: 960px;
box-shadow: 0 0 2px 0 rgb(0 0 0 / 25%);
}
/**/
#main {
flex: 1 0 auto;
background: var(--bg);
}
#main article {
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
}
article > section {
order: 1;
box-sizing: border-box;
padding: var(--vp) var(--hp) var(--vp) var(--hp);
width: 640px;
}
article > #side {
order: 2;
box-sizing: border-box;
padding: var(--vp) var(--hp) var(--vp) var(--hp);
border-left: 1px solid #eaeaea;
width: 320px;
}
article > #side #side_menu {}
article > #side #side_menu_title {
font-size: 20px;
font-weight: bold;
border-bottom: 1px solid lightgray;
}
article > #side #side_menu ul {
padding: 15px 0px 15px 30px;
margin: 0px 0px 0px 0px;
}
article > #side #side_menu ul > li {
font-size: 14px;
}
article > #side #side_twitter {
padding: var(--hp) 0px 0px 0px;
}
#markdown {}
@media (max-width: 320px) {
html {
width: 320px !important;
overflow: scroll !important;
}
}
```
```rust
////src/main.rs
#![feature(proc_macro_hygiene)]
#![feature(decl_macro)]
#[macro_use]
extern crate rocket;
mod markdown;
use rocket_contrib::serve::StaticFiles;
use rocket_contrib::templates::Template;
use markdown::*;
#[catch(404)]
pub fn not_found()->&'static str{"Error:NotFound"}
fn rocket() -> rocket::Rocket {
rocket::ignite()
.mount("/", StaticFiles::from("static"))
.mount("/", routes![index,md])
.attach(Template::fairing())
.register(catchers![not_found])
}
fn main() {
rocket().launch();
}
```
```rust
////src/markdown.rs
extern crate regex;
extern crate pulldown_cmark;
use std::io;
use std::fs;
use std::env;
use std::path::Path;
use regex::Regex;
use serde::Serialize;
use pulldown_cmark::{html,Parser};
use rocket_contrib::templates::Template;
#[derive(Serialize)]
pub struct TemplateContext {
text: String,
items: Vec
}
pub fn md_ctx(markdown: String) -> TemplateContext {
return TemplateContext {
text: markdown,
items: md_list()
};
}
pub fn pwd() -> String {
return env::current_dir().unwrap().display().to_string();
}
pub fn filename(file: &str) -> &str {
////Remove(LastOnly)Extention
Regex::new(r"(.+?)(\.[^.]*$|$)").unwrap().captures(file).unwrap().at(1).unwrap()
}
pub fn read_dir>(path: P) -> io::Result> {
Ok(fs::read_dir(path)?
.filter_map(|entry| {
let entry = entry.ok()?;
if entry.file_type().ok()?.is_file() {
Some(entry.file_name().to_string_lossy().into_owned())
} else {
None
}
})
.collect())
}
pub fn md_list() -> Vec {
let dirs=read_dir(pwd()+"/md").unwrap();
dirs.iter().map(|i| String::from(filename(&i))).collect::>()
}
pub fn md_load(path:&str) -> String {
if Path::new(path).exists() {
let markdown_str = fs::read_to_string(path).expect("FileReadError");
let mut html_buf = String::new();
html::push_html(&mut html_buf,Parser::new(&markdown_str));
return html_buf;
}else{
return String::from("NotFoundMarkdown");
}
}
pub fn md_render(file: &str) -> Template {
Template::render("md",md_ctx(md_load(&(pwd()+"/md/"+filename(file)+".md"))))
}
#[get("/md/")]
pub fn md(name: String) -> Template {
md_render(&name)
}
#[get("/")]
pub fn index() -> Template {
md_render("README")
}
```
md/配下にマークダウンファイルを配置
```md
# md/README.md
## タイトル
テキスト
```
```bash
cargo run
```
```
cargo build --release
pm2 start target/release/app --name app --watch
```
## Reference
* [Rocket](https://rocket.rs/v0.4/guide/)