Browse Source

Rust framework Anansi (not accepting requests from client machine) (#7842)

* added framework Anansi

* made server bind to 0.0.0.0

* fixed db tests

* added raw tests

* added cache test

* updated README

---------

Co-authored-by: sarutora <example.com>
saru-tora 2 years ago
parent
commit
a3dac7455e
30 changed files with 621 additions and 0 deletions
  1. 19 0
      frameworks/Rust/anansi/Cargo.toml
  2. 36 0
      frameworks/Rust/anansi/README.md
  3. 11 0
      frameworks/Rust/anansi/anansi-raw.dockerfile
  4. 14 0
      frameworks/Rust/anansi/anansi.dockerfile
  5. 52 0
      frameworks/Rust/anansi/benchmark_config.json
  6. 19 0
      frameworks/Rust/anansi/settings.toml
  7. 3 0
      frameworks/Rust/anansi/src/hello/migrations/mod.rs
  8. 7 0
      frameworks/Rust/anansi/src/hello/mod.rs
  9. 47 0
      frameworks/Rust/anansi/src/hello/records.rs
  10. 3 0
      frameworks/Rust/anansi/src/hello/urls.rs
  11. 5 0
      frameworks/Rust/anansi/src/hello/world/mod.rs
  12. 133 0
      frameworks/Rust/anansi/src/hello/world/raw.rs
  13. 9 0
      frameworks/Rust/anansi/src/hello/world/templates/.parsed/base.in
  14. 1 0
      frameworks/Rust/anansi/src/hello/world/templates/.parsed/base_args.in
  15. 6 0
      frameworks/Rust/anansi/src/hello/world/templates/.parsed/fortunes.in
  16. 1 0
      frameworks/Rust/anansi/src/hello/world/templates/.parsed/index.in
  17. 5 0
      frameworks/Rust/anansi/src/hello/world/templates/.parsed/raw_fortunes.in
  18. 9 0
      frameworks/Rust/anansi/src/hello/world/templates/base.rs.html
  19. 10 0
      frameworks/Rust/anansi/src/hello/world/templates/fortunes.rs.html
  20. 10 0
      frameworks/Rust/anansi/src/hello/world/templates/raw_fortunes.rs.html
  21. 16 0
      frameworks/Rust/anansi/src/hello/world/util.rs
  22. 90 0
      frameworks/Rust/anansi/src/hello/world/views.rs
  23. 11 0
      frameworks/Rust/anansi/src/http_errors/500.html
  24. 1 0
      frameworks/Rust/anansi/src/http_errors/mod.rs
  25. 11 0
      frameworks/Rust/anansi/src/http_errors/templates/.parsed/not_found.in
  26. 11 0
      frameworks/Rust/anansi/src/http_errors/templates/not_found.rs.html
  27. 12 0
      frameworks/Rust/anansi/src/http_errors/views.rs
  28. 33 0
      frameworks/Rust/anansi/src/main.rs
  29. 11 0
      frameworks/Rust/anansi/src/project.rs
  30. 25 0
      frameworks/Rust/anansi/src/urls.rs

+ 19 - 0
frameworks/Rust/anansi/Cargo.toml

@@ -0,0 +1,19 @@
+[workspace]
+members = [
+    ".",
+]
+
+[package]
+name = "tfb-anansi"
+version = "0.1.0"
+edition = "2021"
+
+[features]
+raw = []
+
+[dependencies]
+anansi = { git = "https://github.com/saru-tora/anansi", rev = "2567157", features = ["postgres", "minimal", "redis"] }
+async-trait = "0.1.57"
+rand = "0.8.4"
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"

+ 36 - 0
frameworks/Rust/anansi/README.md

@@ -0,0 +1,36 @@
+# [Anansi](https://saru-tora.github.io/anansi/) Benchmarking Test
+
+Anansi is a simple full-stack web framework for Rust.
+
+### Test Type Implementation Source Code
+
+All tests can be found in: src/hello/world/
+
+## Test URLs
+### JSON
+
+http://localhost:8080/json
+
+### PLAINTEXT
+
+http://localhost:8080/plaintext
+
+### DB
+
+http://localhost:8080/db
+
+### QUERY
+
+http://localhost:8080/query?queries=
+
+### CACHED QUERY
+
+http://localhost:8080/cached_query?queries=
+
+### UPDATE
+
+http://localhost:8080/update?queries=
+
+### FORTUNES
+
+http://localhost:8080/fortunes

+ 11 - 0
frameworks/Rust/anansi/anansi-raw.dockerfile

@@ -0,0 +1,11 @@
+FROM rust:1.64
+
+ADD ./ /anansi
+WORKDIR /anansi
+
+RUN cargo clean
+RUN RUSTFLAGS="-C target-cpu=native" cargo build --release --features raw
+
+EXPOSE 8080
+
+CMD RUST_LOG=off ./target/release/tfb-anansi 0.0.0.0:8080

+ 14 - 0
frameworks/Rust/anansi/anansi.dockerfile

@@ -0,0 +1,14 @@
+FROM rust:1.64
+
+RUN apt-get update -yqq && apt-get install -yqq --no-install-recommends redis-server
+
+ADD ./ /anansi
+WORKDIR /anansi
+
+RUN cargo clean
+RUN RUSTFLAGS="-C target-cpu=native" cargo build --release
+
+EXPOSE 8080
+
+ENV RUST_LOG=off
+CMD service redis-server start && ./target/release/tfb-anansi 0.0.0.0:8080

+ 52 - 0
frameworks/Rust/anansi/benchmark_config.json

@@ -0,0 +1,52 @@
+{
+  "framework": "anansi",
+  "tests": [
+    {
+      "default": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "fortune_url": "/fortunes",
+        "query_url": "/queries?q=",
+        "update_url": "/updates?q=",
+        "cached_query_url": "/cached-queries?q=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Fullstack",
+        "database": "postgres",
+        "framework": "Anansi",
+        "language": "Rust",
+        "flavor": "None",
+        "orm": "Full",
+        "platform": "None",
+        "webserver": "hyper",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "Anansi",
+        "notes": "",
+        "versus": "None"
+      },
+      "raw": {
+        "db_url": "/db",
+        "fortune_url": "/fortunes",
+        "query_url": "/queries?q=",
+        "update_url": "/updates?q=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Fullstack",
+        "database": "postgres",
+        "framework": "Anansi",
+        "language": "Rust",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "hyper",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "Anansi [raw]",
+        "notes": "",
+        "versus": "None"
+      }
+    }
+  ]
+}

+ 19 - 0
frameworks/Rust/anansi/settings.toml

@@ -0,0 +1,19 @@
+secret_key = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+login_url = "/login"
+smtp_relay = ""
+smtp_username = ""
+smtp_password = ""
+
+[caches]
+
+[caches.default]
+location = "redis://127.0.0.1/"
+
+[databases]
+
+[databases.default]
+name = "hello_world"
+user = "benchmarkdbuser"
+password = "benchmarkdbpass"
+address = "tfb-database"
+max_conn = 512

+ 3 - 0
frameworks/Rust/anansi/src/hello/migrations/mod.rs

@@ -0,0 +1,3 @@
+use anansi::migrations::prelude::*;
+
+local_migrations! {}

+ 7 - 0
frameworks/Rust/anansi/src/hello/mod.rs

@@ -0,0 +1,7 @@
+pub mod urls;
+#[cfg(not(feature = "raw"))]
+pub mod records;
+pub mod migrations;
+
+pub const APP_NAME: &'static str = "hello";
+pub mod world;

+ 47 - 0
frameworks/Rust/anansi/src/hello/records.rs

@@ -0,0 +1,47 @@
+#![allow(non_snake_case)]
+use async_trait::async_trait;
+use anansi::web::{Result, BaseRequest};
+use anansi::record;
+use anansi::records::{Relate, Int, Text, random_int};
+use serde::Serialize;
+
+#[record(table_name = "World")]
+#[derive(Serialize)]
+pub struct World {
+    #[field(primary_key = "true", default_fn = "random_int")]
+    pub id: Int,
+    pub randomNumber: Int,
+}
+
+#[async_trait]
+impl<R: BaseRequest> Relate<R> for World {
+    async fn on_save(&self, _req: &R) -> Result<()> {
+        unimplemented!();
+    }
+    async fn on_delete(&self, _req: &R) -> Result<()> {
+        unimplemented!();
+    }
+}
+
+#[record(table_name = "Fortune")]
+pub struct Fortune {
+    #[field(primary_key = "true", default_fn = "random_int")]
+    pub id: Int,
+    pub message: Text,
+}
+
+impl Fortune {
+    pub fn additional() -> Self {
+        Self {id: Int::new(0), message: Text::from("Additional fortune added at request time.".to_string())}
+    }
+}
+
+#[async_trait]
+impl<R: BaseRequest> Relate<R> for Fortune {
+    async fn on_save(&self, _req: &R) -> Result<()> {
+        unimplemented!();
+    }
+    async fn on_delete(&self, _req: &R) -> Result<()> {
+        unimplemented!();
+    }
+}

+ 3 - 0
frameworks/Rust/anansi/src/hello/urls.rs

@@ -0,0 +1,3 @@
+use anansi::web::prelude::*;
+
+routes! {}

+ 5 - 0
frameworks/Rust/anansi/src/hello/world/mod.rs

@@ -0,0 +1,5 @@
+#[cfg(not(feature = "raw"))]
+pub mod views;
+#[cfg(feature = "raw")]
+pub mod raw;
+pub mod util;

+ 133 - 0
frameworks/Rust/anansi/src/hello/world/raw.rs

@@ -0,0 +1,133 @@
+use crate::prelude::*;
+use serde::Serialize;
+use anansi::check;
+use anansi::db::postgres::{PgDbRow, PgQuery};
+use super::util::get_query;
+use std::borrow::Cow;
+use anansi::db::DbRow;
+use rand::Rng;
+use std::fmt::Write;
+
+thread_local!(static UPDATES: Vec<Cow<'static, str>> = {
+    let mut updates = vec![Cow::from("")];
+    for num in 1..=500u16 {
+        let mut pl = 1;
+        let mut q = "UPDATE world SET randomnumber = CASE id ".to_string();
+        for _ in 1..=num {
+            let _ = write!(q, "WHEN ${} THEN ${} ", pl, pl + 1);
+            pl += 2;
+        }
+
+        q.push_str("ELSE randomnumber END WHERE id IN (");
+
+        for _ in 1..=num {
+            let _ = write!(q, "${},", pl);
+            pl += 1;
+        }
+
+        q.pop();
+        q.push(')');
+
+        updates.push(Cow::from(q));
+    }
+    updates
+});
+
+fn random_num() -> i32 {
+    rand::thread_rng().gen_range(1..=10_000)
+}
+
+#[derive(Copy, Clone, Serialize, Debug)]
+pub struct World {
+    id: i32,
+    randomnumber: i32,
+}
+
+#[derive(Serialize, Debug)]
+pub struct Fortune {
+    id: i32,
+    message: Cow<'static, str>,
+}
+
+#[base_view]
+fn base<R: Request>(_req: &mut R) -> Result<Response> {}
+
+#[viewer]
+impl<R: Request> WorldView<R> {
+    async fn get_world(req: &R) -> Result<PgDbRow> {
+        PgQuery::new("SELECT * FROM world WHERE id = $1")
+            .bind(random_num())
+            .fetch_one(req)
+            .await
+    }
+    async fn get_worlds(req: &R) -> Result<Vec<World>> {
+        let q = get_query(req.params());
+        let mut worlds = Vec::with_capacity(q as usize);
+        for _ in 0..q {
+            let row = Self::get_world(req).await?;
+            let world = World {
+                id: row.try_i32("id")?,
+                randomnumber: row.try_i32("randomnumber")?,
+            };
+            worlds.push(world);
+        }
+        Ok(worlds)
+    }
+    #[check(Site::is_visitor)]
+    pub async fn db(req: &mut R) -> Result<Response> {
+        let row = Self::get_world(req).await?;
+        let world = World {
+            id: row.try_i32("id")?,
+            randomnumber: row.try_i32("randomnumber")?,
+        };
+        Response::json(&world)
+    }
+    #[check(Site::is_visitor)]
+    pub async fn queries(req: &mut R) -> Result<Response> {
+        let worlds = Self::get_worlds(req).await?;
+        Response::json(&worlds)
+    }
+    #[view(Site::is_visitor)]
+    pub async fn raw_fortunes(req: &mut R) -> Result<Response> {
+        let title = "Fortunes";
+        let rows = PgQuery::new("SELECT * FROM fortune")
+            .fetch_all(req)
+            .await?;
+        let mut fortunes = vec![Fortune {
+            id: 0,
+            message: Cow::Borrowed("Additional fortune added at request time.")
+        }];
+        for row in rows {
+            fortunes.push(Fortune {
+                id: row.try_i32("id")?,
+                message: Cow::Owned(row.try_string("message")?),
+            })
+        }
+        fortunes.sort_by(|it, next| it.message.cmp(&next.message));
+    }
+    #[check(Site::is_visitor)]
+    pub async fn updates(req: &mut R) -> Result<Response> {
+        let q = get_query(req.params()) as usize;
+        let mut worlds = Vec::with_capacity(q);
+        let mut update = Cow::from("");
+        UPDATES.with(|u| {
+            update = u[q].clone();
+        });
+        let mut updates = PgQuery::new(&update);
+        for _ in 0..q {
+            let row = Self::get_world(req).await?;
+            let world = World {
+                id: row.try_i32("id")?,
+                randomnumber: random_num(),
+            };
+            updates = updates.bind(world.id)
+                .bind(world.randomnumber);
+            worlds.push(world);
+        }
+        for world in &worlds {
+            updates = updates.bind(world.id);
+        }
+        updates.execute(req).await?;
+        Response::json(&worlds)
+    }
+}

+ 9 - 0
frameworks/Rust/anansi/src/hello/world/templates/.parsed/base.in

@@ -0,0 +1,9 @@
+{let mut _c = String::new();_c.push_str("<!DOCTYPE html>
+<html lang=\"en\">
+        <head>
+                <title>");_c.push_str(&_base_args._title);_c.push_str("</title>
+        </head>
+        <body>
+                ");_c.push_str(&_base_args._content);_c.push_str("
+        </body>
+</html>");Ok(anansi::web::Response::new(200, _c.into_bytes()))}

+ 1 - 0
frameworks/Rust/anansi/src/hello/world/templates/.parsed/base_args.in

@@ -0,0 +1 @@
+pub struct Args {pub _title: String,pub _content: String,}impl anansi::cache::Cacheable for Args {fn to_bytes(&self) -> Vec<u8> {let mut v = vec![];v.append(&mut self._title.len().to_ne_bytes().to_vec());v.append(&mut self._title.as_bytes().to_vec());v.append(&mut self._content.len().to_ne_bytes().to_vec());v.append(&mut self._content.as_bytes().to_vec());v} fn from_bytes(mut __b: Vec<u8>) -> anansi::web::Result<Self> {let mut buf = __b.split_off(8); let l = usize::from_ne_bytes(__b.try_into().unwrap()); let mut __b = buf.split_off(l); let _title = String::from_utf8(buf)?;let mut buf = __b.split_off(8); let l = usize::from_ne_bytes(__b.try_into().unwrap()); let mut __b = buf.split_off(l); let _content = String::from_utf8(buf)?;Ok(Self {_title, _content, })}} impl Args {pub fn new() -> Self {Self {_title: String::new(), _content: String::new(), }}}

+ 6 - 0
frameworks/Rust/anansi/src/hello/world/templates/.parsed/fortunes.in

@@ -0,0 +1,6 @@
+{_args._title = {let mut _c = String::new();_c.push_str("");_c.push_str(&anansi::web::html_escape(&format!("{}", title)));_c.push_str(""); _c};_args._content = {let mut _c = String::new();_c.push_str("    <table>
+    <tr><th>id</th><th>message</th></tr>
+    ");for fortune in fortunes {_c.push_str("
+        <tr><td>");_c.push_str(&anansi::web::html_escape(&format!("{}", fortune.pk())));_c.push_str("</td><td>");_c.push_str(&anansi::web::html_escape(&format!("{}", fortune.message)));_c.push_str("</td></tr>
+    ");}_c.push_str("
+    </table>"); _c};_args}

+ 1 - 0
frameworks/Rust/anansi/src/hello/world/templates/.parsed/index.in

@@ -0,0 +1 @@
+{_args._title = {let mut _c = String::new();_c.push_str("");_c.push_str(&anansi::web::html_escape(&format!("{}", title)));_c.push_str(""); _c};_args._content = {let mut _c = String::new();_c.push_str("	<h1>");_c.push_str(&anansi::web::html_escape(&format!("{}", title)));_c.push_str("</h1>"); _c};_args}

+ 5 - 0
frameworks/Rust/anansi/src/hello/world/templates/.parsed/raw_fortunes.in

@@ -0,0 +1,5 @@
+{_args._title = {let mut _c = String::new();_c.push_str(&anansi::web::html_escape(&format!("{}", title))); _c};
+_args._content = {let mut _c = String::new();_c.push_str("<table><tr><th>id</th><th>message</th></tr>");
+for fortune in fortunes {_c.push_str("<tr><td>"); _c.push_str(&anansi::web::html_escape(&format!("{}", fortune.id)));
+_c.push_str("</td><td>"); _c.push_str(&anansi::web::html_escape(&format!("{}", fortune.message))); _c.push_str("</td></tr>");
+} _c.push_str("</table>"); _c};_args}

+ 9 - 0
frameworks/Rust/anansi/src/hello/world/templates/base.rs.html

@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html lang="en">
+        <head>
+                <title>@block title</title>
+        </head>
+        <body>
+                @block content
+        </body>
+</html>

+ 10 - 0
frameworks/Rust/anansi/src/hello/world/templates/fortunes.rs.html

@@ -0,0 +1,10 @@
+@block title {@title}
+
+@block content {
+    <table>
+    <tr><th>id</th><th>message</th></tr>
+    @for fortune in fortunes {
+        <tr><td>@fortune.pk()</td><td>@fortune.message</td></tr>
+    }
+    </table>
+}

+ 10 - 0
frameworks/Rust/anansi/src/hello/world/templates/raw_fortunes.rs.html

@@ -0,0 +1,10 @@
+@block title {@title}
+
+@block content {
+    <table>
+    <tr><th>id</th><th>message</th></tr>
+    @for fortune in fortunes {
+        <tr><td>@fortune.id</td><td>@fortune.message</td></tr>
+    }
+    </table>
+}

+ 16 - 0
frameworks/Rust/anansi/src/hello/world/util.rs

@@ -0,0 +1,16 @@
+use anansi::web::Parameters;
+
+pub fn get_query(params: &Parameters) -> i16 {
+    if let Ok(q) = params.get("q") {
+        if let Ok(q) = q.parse() {
+            if q > 1 {
+                return if q <= 500 {
+                    q
+                } else {
+                    500
+                };
+            }
+        } 
+    }
+    1
+}

+ 90 - 0
frameworks/Rust/anansi/src/hello/world/views.rs

@@ -0,0 +1,90 @@
+use crate::prelude::*;
+use anansi::cache::prelude::*;
+use anansi::records::Int;
+use super::super::records::{World, Fortune};
+use super::util::get_query;
+use serde::Serialize;
+use anansi::{check, raw_bulk_update};
+use rand::Rng;
+
+fn random_i32() -> i32 {
+    rand::thread_rng().gen_range(1..=10_000)
+}
+
+fn random_int() -> Int {
+    Int::new(random_i32())
+}
+
+#[derive(Serialize)]
+struct Message {
+    message: &'static str,
+}
+
+#[base_view]
+fn base<R: Request>(_req: &mut R) -> Result<Response> {}
+
+#[viewer]
+impl<R: Request> WorldView<R> {
+    #[check(Site::is_visitor)]
+    pub async fn json(req: &mut R) -> Result<Response> {
+        let message = Message {message: "Hello, World!"};
+        Response::json(&message)
+    }
+    async fn get_world(req: &R) -> Result<World> {
+        World::find(random_int()).get(req).await
+    }
+    async fn get_worlds(req: &R) -> Result<Vec<World>> {
+        let q = get_query(req.params());
+        let mut worlds = Vec::with_capacity(q as usize);
+        for _ in 0..q {
+            let world = Self::get_world(req).await?;
+            worlds.push(world);
+        }
+        Ok(worlds)
+    }
+    #[check(Site::is_visitor)]
+    pub async fn db(req: &mut R) -> Result<Response> {
+        let world = Self::get_world(req).await?;
+        Response::json(&world)
+    }
+    #[check(Site::is_visitor)]
+    pub async fn queries(req: &mut R) -> Result<Response> {
+        let worlds = Self::get_worlds(req).await?;
+        Response::json(&worlds)
+    }
+    #[view(Site::is_visitor)]
+    pub async fn fortunes(req: &mut R) -> Result<Response> {
+        let title = "Fortunes";
+        let mut fortunes = Fortune::get_all().query(req).await?;
+        fortunes.push(Fortune::additional());
+        fortunes.sort_by(|it, next| it.message.cmp(&next.message));
+    }
+    #[check(Site::is_visitor)]
+    pub async fn updates(req: &mut R) -> Result<Response> {
+        let mut worlds = Self::get_worlds(req).await?;
+        for world in &mut worlds {
+            world.randomNumber = random_int();
+        }
+        transact!(req, raw_bulk_update!(req, World, &worlds, randomNumber).await)?;
+        Response::json(&worlds)
+    }
+    #[check(Site::is_visitor)]
+    pub async fn plaintext(req: &mut R) -> Result<Response> {
+        Ok(Response::text("Hello, World!".to_string()))
+    }
+    #[check(Site::is_visitor)]
+    pub async fn cached_queries(req: &mut R) -> Result<Response> {
+        let q = get_query(req.params());
+        let mut ids = vec![];
+        for _ in 0..q {
+            ids.push(random_i32().to_string());
+        }
+        let mut worlds = vec!['[' as u8];
+        for mut world in req.cache().get_many(ids).await? {
+            worlds.append(&mut world);
+        }
+        worlds.pop();
+        worlds.push(']' as u8);
+        Response::json_bytes(worlds)
+    }
+}

+ 11 - 0
frameworks/Rust/anansi/src/http_errors/500.html

@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8">
+		<title>500 Internal Server Error</title>
+	</head>
+	<body>
+		<h1>Internal Server Error</h1>
+		<p>Sorry, something went wrong.</p>
+	</body>
+</html>

+ 1 - 0
frameworks/Rust/anansi/src/http_errors/mod.rs

@@ -0,0 +1 @@
+pub mod views;

+ 11 - 0
frameworks/Rust/anansi/src/http_errors/templates/.parsed/not_found.in

@@ -0,0 +1,11 @@
+{let mut _c = String::new();_c.push_str("<!DOCTYPE html>
+<html lang=\"en\">
+	<head>
+		<meta charset=\"utf-8\">
+		<title>Not Found</title>
+	</head>
+	<body>
+		<h1>404</h1>
+		<p>Page not found.</p>
+	</body>
+</html>");Ok(anansi::web::Response::new(200, _c.into_bytes()))}

+ 11 - 0
frameworks/Rust/anansi/src/http_errors/templates/not_found.rs.html

@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8">
+		<title>Not Found</title>
+	</head>
+	<body>
+		<h1>404</h1>
+		<p>Page not found.</p>
+	</body>
+</html>

+ 12 - 0
frameworks/Rust/anansi/src/http_errors/views.rs

@@ -0,0 +1,12 @@
+use anansi::{check, viewer, render};
+use anansi::web::{Result, Response};
+use anansi::site::Site;
+use crate::project::Request;
+
+#[viewer]
+impl<R: Request> ErrorView<R> {
+    #[check(Site::is_visitor)]
+    pub async fn not_found(_req: &mut R) -> Result<Response> {
+        render!("not_found")
+    }
+}

+ 33 - 0
frameworks/Rust/anansi/src/main.rs

@@ -0,0 +1,33 @@
+use anansi::*;
+
+mod urls;
+mod project;
+mod http_errors;
+mod hello;
+
+apps! {
+    hello,
+}
+
+app_statics! {}
+
+#[cfg(feature = "raw")]
+min_main!();
+
+#[cfg(not(feature = "raw"))]
+min_main!(server, {
+    {
+        use anansi::cache::prelude::*;
+        use hello::records::World;
+        use anansi::records::Record;
+        let worlds = World::get_all().raw_query(&server.pool).await.expect("problem fetching worlds");
+        let mut items = vec![];
+        for world in worlds {
+            let id = world.pk().to_string();
+            let mut bytes = serde_json::to_vec(&world).expect("problem serializing world");
+            bytes.push(',' as u8);
+            items.push((id, bytes));
+        }
+        server.cache.set_many(&items).await.expect("problem caching world");
+    }
+});

+ 11 - 0
frameworks/Rust/anansi/src/project.rs

@@ -0,0 +1,11 @@
+use anansi::project::prelude::*;
+
+#[cfg(feature = "raw")]
+app_cache!(local);
+
+#[cfg(not(feature = "raw"))]
+app_cache!(redis);
+
+database!(postgres);
+
+middleware!();

+ 25 - 0
frameworks/Rust/anansi/src/urls.rs

@@ -0,0 +1,25 @@
+use anansi::web::prelude::*;
+#[cfg(not(feature = "raw"))]
+use crate::hello::world::views::WorldView;
+
+#[cfg(not(feature = "raw"))]
+routes! {
+    path!("/json", WorldView::json),
+    path!("/db", WorldView::db),
+    path!("/queries", WorldView::queries),
+    path!("/fortunes", WorldView::fortunes),
+    path!("/updates", WorldView::updates),
+    path!("/plaintext", WorldView::plaintext),
+    path!("/cached-queries", WorldView::cached_queries),
+}
+
+#[cfg(feature = "raw")]
+use crate::hello::world::raw::WorldView;
+
+#[cfg(feature = "raw")]
+routes! {
+    path!("/db", WorldView::db),
+    path!("/queries", WorldView::queries),
+    path!("/fortunes", WorldView::raw_fortunes),
+    path!("/updates", WorldView::updates),
+}