gdritter repos cg / af10ced
Actual templating progress Getty Ritter 6 years ago
11 changed file(s) with 297 addition(s) and 77 deletion(s). Collapse all Expand all
1 [root]
1 [[package]]
2 name = "base64"
3 version = "0.6.0"
4 source = "registry+https://github.com/rust-lang/crates.io-index"
5 dependencies = [
6 "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
7 "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
8 ]
9
10 [[package]]
11 name = "bitflags"
12 version = "0.7.0"
13 source = "registry+https://github.com/rust-lang/crates.io-index"
14
15 [[package]]
16 name = "bitflags"
17 version = "1.0.1"
18 source = "registry+https://github.com/rust-lang/crates.io-index"
19
20 [[package]]
21 name = "byteorder"
22 version = "1.1.0"
23 source = "registry+https://github.com/rust-lang/crates.io-index"
24
25 [[package]]
26 name = "bytes"
27 version = "0.4.5"
28 source = "registry+https://github.com/rust-lang/crates.io-index"
29 dependencies = [
30 "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
31 "iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
32 ]
33
34 [[package]]
35 name = "cfg-if"
36 version = "0.1.2"
37 source = "registry+https://github.com/rust-lang/crates.io-index"
38
39 [[package]]
240 name = "cg"
341 version = "0.1.0"
442 dependencies = [
543 "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
644 "hyper 0.11.7 (registry+https://github.com/rust-lang/crates.io-index)",
45 "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
746 "mustache 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
847 "rusqlite 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
48 "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
949 "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
1050 ]
11
12 [[package]]
13 name = "base64"
14 version = "0.6.0"
15 source = "registry+https://github.com/rust-lang/crates.io-index"
16 dependencies = [
17 "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
18 "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
19 ]
20
21 [[package]]
22 name = "bitflags"
23 version = "0.7.0"
24 source = "registry+https://github.com/rust-lang/crates.io-index"
25
26 [[package]]
27 name = "bitflags"
28 version = "1.0.1"
29 source = "registry+https://github.com/rust-lang/crates.io-index"
30
31 [[package]]
32 name = "byteorder"
33 version = "1.1.0"
34 source = "registry+https://github.com/rust-lang/crates.io-index"
35
36 [[package]]
37 name = "bytes"
38 version = "0.4.5"
39 source = "registry+https://github.com/rust-lang/crates.io-index"
40 dependencies = [
41 "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
42 "iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
43 ]
44
45 [[package]]
46 name = "cfg-if"
47 version = "0.1.2"
48 source = "registry+https://github.com/rust-lang/crates.io-index"
4951
5052 [[package]]
5153 name = "fuchsia-zircon"
126128 [[package]]
127129 name = "language-tags"
128130 version = "0.2.2"
131 source = "registry+https://github.com/rust-lang/crates.io-index"
132
133 [[package]]
134 name = "lazy_static"
135 version = "0.2.11"
129136 source = "registry+https://github.com/rust-lang/crates.io-index"
130137
131138 [[package]]
423430 "checksum iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6e8b9c2247fcf6c6a1151f1156932be5606c9fd6f55a2d7f9fc1cb29386b2f7"
424431 "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
425432 "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
433 "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
426434 "checksum lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b585b7a6811fb03aa10e74b278a0f00f8dd9b45dc681f148bb29fa5cb61859b"
427435 "checksum libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "5ba3df4dcb460b9dfbd070d41c94c19209620c191b0340b929ce748a2bcd42d2"
428436 "checksum libsqlite3-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1bad50e980f2a86252585f7522a92f17672b1ab5c7087db6a0e566ae6294b8c9"
99 time = "*"
1010 mustache = "*"
1111 futures = "*"
12 # rustc_serialize = "*"
12 rustc-serialize = "*"
13 lazy_static = "0.2"
Binary diff not shown
3939 CGError::SQLite(err)
4040 }
4141 }
42
43 #[derive(Debug)]
44 pub enum RuntimeError {
45 SQLite(::rusqlite::Error),
46 Mustache(::mustache::Error),
47 IOError(::std::io::Error),
48 UnknownPath(String),
49 }
50
51 impl From<::rusqlite::Error> for RuntimeError {
52 fn from(err: ::rusqlite::Error) -> Self {
53 RuntimeError::SQLite(err)
54 }
55 }
56
57 impl From<::mustache::Error> for RuntimeError {
58 fn from(err: ::mustache::Error) -> Self {
59 RuntimeError::Mustache(err)
60 }
61 }
62
63 impl From<::std::io::Error> for RuntimeError {
64 fn from(err: ::std::io::Error) -> Self {
65 RuntimeError::IOError(err)
66 }
67 }
1 #![feature(slice_patterns)]
2
13 extern crate futures;
24 extern crate hyper;
35 extern crate rusqlite;
46 extern crate time;
57 extern crate mustache;
8 extern crate rustc_serialize;
9
10 #[macro_use]
11 extern crate lazy_static;
612
713 pub mod errors;
814 pub mod model;
15 pub mod template;
916 #[macro_use]
1017 pub mod util;
1118
1219 use futures::future::Future;
1320 use hyper::Method;
14 use hyper::header::ContentLength;
21 use hyper::header::{ContentLength,ContentType};
1522 use hyper::server::{Http, Request, Response, Service};
23
24 use std::io::Read;
1625
1726 struct CG {
1827 db: rusqlite::Connection,
2635
2736 fn call(&self, req: Request) -> Self::Future {
2837 util::log_request(&req);
29 let path: Vec<&str> = req.path().split("/").skip(1).collect();
30
31 match (req.method(), path.get(0), path.get(1)) {
32 (&Method::Get, Some(&""), None) => {
33 match model::get_cocktail_list(&self.db) {
34 Ok(ck) => CG::respond(format!("cocktails: {:?}", ck)),
35 Err(err) => {
36 error!("Database problem: {}", err);
37 CG::respond(format!("database problem yo"))
38 }
39 }
40 }
41
42 (&Method::Get, Some(&"favicon.ico"), None) =>
43 CG::respond("icon".to_owned()),
44
45 (&Method::Get, Some(&"cocktail"), Some(name)) =>
46 CG::respond(format!("cocktail {}", name)),
47
48 (&Method::Get, Some(&"tagged"), Some(tag)) =>
49 CG::respond(format!("tag {}", tag)),
50
51 (&Method::Get, Some(&"imgs"), Some(filename)) =>
52 CG::respond(format!("image {}", filename)),
53
54 (_, _, _) => {
55 error!("unknown path: {}", req.path());
56 CG::respond(format!("path not found: {:?}", req.path()))
57
38 match self.route(req) {
39 Ok(pg) =>
40 CG::respond(pg),
41 Err(err) => {
42 error!("Found error: {:?}", err);
43 CG::respond_404(format!("error: {:?}", err))
5844 }
5945 }
6046 }
47 }
48
49 enum Page {
50 HTML(String),
51 PNG(Vec<u8>),
52 StaticPNG(&'static [u8]),
6153 }
6254
6355 impl CG {
7163 Ok(server.run()?)
7264 }
7365
74 fn respond(s: String) -> <CG as Service>::Future {
66 fn route(&self, req: Request) -> Result<Page, errors::RuntimeError> {
67 let path: Vec<&str> = req.path().split("/").skip(1).collect();
68
69 Ok(match (req.method(), path.as_slice()) {
70 (&Method::Get, &[""]) => {
71 let ck = model::get_cocktail_list(&self.db)?;
72 Page::HTML(template::render_cocktails(ck)?)
73 }
74
75 (&Method::Get, &["favicon.ico"]) =>
76 Page::HTML("icon".to_owned()),
77
78 (&Method::Get, &["cocktail", name]) =>
79 Page::HTML(format!("cocktail {}", name)),
80
81 (&Method::Get, &["tagged", tag]) =>
82 Page::HTML(format!("tag {}", tag)),
83
84 (&Method::Get, &["static", "header-logo.png"]) =>
85 Page::StaticPNG(template::HEADER_IMAGE),
86
87 (&Method::Get, &["imgs", filename]) => {
88 let mut img_path = std::path::PathBuf::from("imgs");
89 img_path.push(filename);
90 let mut f = std::fs::File::open(img_path.as_path())?;
91 let mut vec = Vec::new();
92 let _ = f.read_to_end(&mut vec)?;
93 Page::PNG(vec)
94 }
95
96 (_, _) => {
97 return Err(errors::RuntimeError::UnknownPath(
98 req.path().to_owned()));
99
100 }
101 })
102 }
103
104 fn respond(p: Page) -> <CG as Service>::Future {
105 match p {
106 Page::HTML(s) =>
107 Box::new(futures::future::ok(
108 Response::new()
109 .with_header(ContentLength(s.len() as u64))
110 .with_header(ContentType::html())
111 .with_body(s))),
112 Page::PNG(s) =>
113 Box::new(futures::future::ok(
114 Response::new()
115 .with_header(ContentLength(s.len() as u64))
116 .with_header(ContentType::png())
117 .with_body(s))),
118 Page::StaticPNG(s) =>
119 Box::new(futures::future::ok(
120 Response::new()
121 .with_header(ContentLength(s.len() as u64))
122 .with_header(ContentType::png())
123 .with_body(s))),
124 }
125 }
126
127 fn respond_404(s: String) -> <CG as Service>::Future {
75128 Box::new(futures::future::ok(
76129 Response::new()
130 .with_status(hyper::StatusCode::NotFound)
77131 .with_header(ContentLength(s.len() as u64))
78132 .with_body(s)))
79133 }
1 // extern crate rustc_serialize;
21 use rusqlite::Connection;
32
4 #[derive(Debug, Clone)]
3 #[derive(Debug, Clone, RustcEncodable)]
54 pub struct Cocktail {
65 pub name: String,
76 pub image: String,
87 pub created: String, // ::time::Timespec,
98
10 pub tags: Vec<String>,
9 pub tags: Vec<Tag>,
1110 pub ingredients: Vec<Ingredient>,
1211 }
1312
14 #[derive(Debug, Clone)]
13
14 #[derive(Debug, Clone, RustcEncodable)]
15 pub struct Tag {
16 pub name: String,
17 }
18
19 #[derive(Debug, Clone, RustcEncodable)]
1520 pub struct Ingredient {
1621 pub name: String,
1722 pub amount: String,
4146 let mut c = c.clone();
4247 let mut tag_stmt = conn.prepare(
4348 "SELECT name FROM Tag WHERE cocktail_id = ?")?;
44 let tags: Result<Vec<String>, ::rusqlite::Error> =
49 let tags: Result<Vec<Tag>, ::rusqlite::Error> =
4550 tag_stmt.query_and_then(&[&id], |row| {
46 row.get_checked(0)
51 Ok(Tag { name: row.get_checked(0)? })
4752 })?.collect();
4853 c.tags = tags?;
4954 Ok(c)
1 use mustache::{Template, compile_str};
2 use model::Cocktail;
3
4 pub const HEADER_IMAGE: &'static [u8] =
5 include_bytes!("../static/header-logo.png");
6
7 lazy_static! {
8 static ref MAIN_TEMPLATE: Template =
9 compile_str(include_str!("../template/main.mustache")).unwrap();
10 static ref COCKTAILS_TEMPLATE: Template =
11 compile_str(include_str!("../template/cocktails.mustache")).unwrap();
12 }
13
14 #[derive(RustcEncodable)]
15 struct MainContent {
16 content: String,
17 og_data: Option<()>,
18 }
19
20 pub fn render_main(content: String, og_data: Option<()>) ->
21 Result<String, ::mustache::Error>
22 {
23 MAIN_TEMPLATE.render_to_string(&MainContent {
24 content, og_data
25 })
26 }
27
28 #[derive(RustcEncodable)]
29 struct CocktailList {
30 cocktail: Vec<Cocktail>,
31 }
32
33 pub fn render_cocktails(cocktails: Vec<Cocktail>) -> Result<String, ::mustache::Error> {
34 let content = COCKTAILS_TEMPLATE.render_to_string(&CocktailList { cocktail: cocktails })?;
35 render_main(content, None)
36 }
Binary diff not shown
1 {{#cocktail}}
2 <div class="cocktail">
3 <a href="/cocktail/{{slug}}"><img src="/imgs/{{image}}"/></a>
4 <div class="tags">
5 {{#tags}}<span class="tag"><a href="/tagged/{{name}}">#{{name}}</a></span> {{/tags}}
6 </div>
7 </div>
8 {{/cocktail}}
1 <?xml version="1.0" encoding="utf-8"?>
2 <feed xmlns="http://w3.org/2005/Atom">
3 <title>Cocktail Diagrams</title>
4 <link href="http://cocktail.graphics/feed/" ref="self" />
5 <link href="http://cocktail.graphics/" />
6 <updated>{{updated}}</updated>
7 {{#entry}}
8 <entry>
9 <title>{{title}}</title>
10 <link href="{{link}}" />
11 <updated>{{updated}}</updated>
12 <summary>Some text.</summary>
13 <content type="xhtml">
14 {{content}}
15 </content>
16 <author>
17 <name>Getty Ritter</name>
18 <email>gettyritter@gmail.com</email>
19 </author>
20 </entry>
21 {{/entry}}
22 </feed>
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <title>visual cocktail recipes</title>
5 <meta http-equiv="Content-type" content="text/html; charset=utf-8;"/>
6 {{#ogdata}}
7 <meta property="og:title" content="{{name}}" />
8 <meta property="og:type" content="website" />
9 <meta property="og:image" content="http://cocktail.graphics/imgs/{{image}}" />
10 <meta property="og:url" content="http://cocktail.graphics/cocktail/{{slug}}" />
11 <meta property="og:description" content="{{name}} recipe graph" />
12 <meta name="twitter:card" content="summary_large_image" />
13 <meta name="twitter:creator" content="@aisamanra" />
14 <meta name="twitter:title" content="{{name}}" />
15 <meta name="twitter:image" content="http://cocktail.graphics/imgs/{{image}}" />
16 <meta name="twitter:description" content="{{name}} recipe graph" />
17 {{/ogdata}}
18 <style type="text/css">
19 body {
20 font-family: "Fira Sans", "Arial", sans-serif;
21 margin-left: 0px;
22 margin-right: 0px;
23 }
24 .header {
25 text-align: center;
26 color: #fff;
27 background-color: #3a73af;
28 margin: 0 auto;
29 marign-top: 20px;
30 height: 80px;
31 }
32 .footer {
33 color: #fff;
34 background-color: #3a73af;
35 margin-left: 0;
36 margin-right: 0;
37 margin-top: 40px;
38 margin-bottom: 10px;
39 padding-top: 5px;
40 padding-bottom: 3px;
41 text-align: center;
42 }
43 .cocktail {
44 width: 50%;
45 margin-left: auto;
46 margin-right: auto;
47 margin-top: 40px;
48 }
49 h1 { margin-bottom: 0px; }
50 a { color: #3a73af; }
51 .tags { margin-left: 40px; }
52 .tag { margin-right: 10px; }
53 </style>
54 </head>
55 <body>
56 <div class="header"><a href="/"><img src="/static/header-logo.png"></a></div>
57 <div class="content">{{{content}}}</div>
58 <div class="footer">©2017 Getty Ritter</div>
59 </body>
60 </html>