From 99f17aa87203467d9504d46edf980b41cb183ca5 Mon Sep 17 00:00:00 2001
From: peterrabbit <peterrabbit@msi.home>
Date: Mon, 22 Aug 2022 14:39:43 +0200
Subject: [PATCH] reorganized file structure

---
 example.json                        |   6 +-
 model.dia                           | Bin 3577 -> 3619 bytes
 src/app/args.rs                     |  26 +++++++++
 src/{app_state.rs => app/config.rs} |  45 +--------------
 src/app/mod.rs                      |   8 +++
 src/app/preferences.rs              |   2 +
 src/app/state.rs                    |  18 ++++++
 src/main.rs                         |   4 +-
 src/{service.rs => service/mod.rs}  |   0
 src/static_files.rs                 |   2 +-
 src/tls_config.rs                   |   2 +-
 src/website/html.rs                 |  46 ++++++++++++++++
 src/website/mod.rs                  |   5 ++
 src/website/page.rs                 |  47 ++++++++++++++++
 src/{ => website}/website.rs        |  82 +---------------------------
 templates/html_doc.html             |  17 ------
 templates/new_website.json          |  32 +----------
 17 files changed, 164 insertions(+), 178 deletions(-)
 create mode 100644 src/app/args.rs
 rename src/{app_state.rs => app/config.rs} (65%)
 create mode 100644 src/app/mod.rs
 create mode 100644 src/app/preferences.rs
 create mode 100644 src/app/state.rs
 rename src/{service.rs => service/mod.rs} (100%)
 create mode 100644 src/website/html.rs
 create mode 100644 src/website/mod.rs
 create mode 100644 src/website/page.rs
 rename src/{ => website}/website.rs (53%)
 delete mode 100644 templates/html_doc.html

diff --git a/example.json b/example.json
index 28042f4..eb31b63 100644
--- a/example.json
+++ b/example.json
@@ -6,7 +6,8 @@
             "description": "A website for Pijar",
             "image": "https://pijar.com/static/images/pijar_pic.png",
             "css": [],
-            "js": []
+            "js": [],
+            "url_slug": ""
         },
         "body": [
             {
@@ -30,7 +31,8 @@
                     }
                 ]
             }
-        ]
+        ],
+        "sub_pages": []
     },
     "assets_index": {
         "images": [
diff --git a/model.dia b/model.dia
index 597d424aac6e8a01527dacf244a11593bfc25138..6a0f8a70ac906e294656c8f730efb72eb6e387cd 100644
GIT binary patch
literal 3619
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
R>aMvVE%O*0(|bZp^gpL49+Ln7

literal 3577
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

diff --git a/src/app/args.rs b/src/app/args.rs
new file mode 100644
index 0000000..5deb639
--- /dev/null
+++ b/src/app/args.rs
@@ -0,0 +1,26 @@
+use std::path::PathBuf;
+use structopt::StructOpt;
+
+#[derive(Clone, StructOpt)]
+pub struct AppArgs {
+    #[structopt(short = "c", long = "ctx", default_value = "debug")]
+    pub context: String,
+
+    #[structopt(short = "d", long = "dir")]
+    pub app_storage_root: Option<PathBuf>,
+
+    #[structopt(long)]
+    pub load: Option<PathBuf>,
+
+    #[structopt(short, long, default_value = "localhost")]
+    pub host: String,
+
+    #[structopt(short, long, default_value = "8080")]
+    pub port: u16,
+
+    #[structopt(long = "ptls", default_value = "8443")]
+    pub port_tls: u16,
+
+    #[structopt(long = "certs_dir", default_value = "/etc/letsencrypt/live")]
+    pub ssl_certs_dir: PathBuf,
+}
diff --git a/src/app_state.rs b/src/app/config.rs
similarity index 65%
rename from src/app_state.rs
rename to src/app/config.rs
index 9cd3f67..42ce778 100644
--- a/src/app_state.rs
+++ b/src/app/config.rs
@@ -1,3 +1,4 @@
+use crate::app::AppArgs;
 use std::path::PathBuf;
 use structopt::StructOpt;
 
@@ -7,30 +8,6 @@ pub enum AppContext {
     Production,
 }
 
-#[derive(Clone, StructOpt)]
-pub struct AppArgs {
-    #[structopt(short = "c", long = "ctx", default_value = "debug")]
-    pub context: String,
-
-    #[structopt(short = "d", long = "dir")]
-    pub app_storage_root: Option<PathBuf>,
-
-    #[structopt(long)]
-    pub load: Option<PathBuf>,
-
-    #[structopt(short, long, default_value = "localhost")]
-    pub host: String,
-
-    #[structopt(short, long, default_value = "8080")]
-    pub port: u16,
-
-    #[structopt(long = "ptls", default_value = "8443")]
-    pub port_tls: u16,
-
-    #[structopt(long = "certs_dir", default_value = "/etc/letsencrypt/live")]
-    pub ssl_certs_dir: PathBuf,
-}
-
 #[derive(Clone)]
 pub struct AppConfig {
     pub exec_path: PathBuf,
@@ -91,23 +68,3 @@ impl AppConfig {
         }
     }
 }
-
-#[derive(Clone)]
-pub struct AppState {
-    pub config: AppConfig,
-    pub preferences: AppPreference,
-    // authentication
-    // ...
-}
-
-#[derive(Clone)]
-pub struct AppPreference {}
-
-impl AppState {
-    pub fn new() -> Self {
-        AppState {
-            config: AppConfig::new(),
-            preferences: AppPreference {},
-        }
-    }
-}
diff --git a/src/app/mod.rs b/src/app/mod.rs
new file mode 100644
index 0000000..e5bee9e
--- /dev/null
+++ b/src/app/mod.rs
@@ -0,0 +1,8 @@
+mod args;
+mod config;
+mod preferences;
+mod state;
+pub use args::*;
+pub use config::*;
+pub use preferences::*;
+pub use state::*;
diff --git a/src/app/preferences.rs b/src/app/preferences.rs
new file mode 100644
index 0000000..f097a65
--- /dev/null
+++ b/src/app/preferences.rs
@@ -0,0 +1,2 @@
+#[derive(Clone)]
+pub struct AppPreferences {}
diff --git a/src/app/state.rs b/src/app/state.rs
new file mode 100644
index 0000000..8d0dc75
--- /dev/null
+++ b/src/app/state.rs
@@ -0,0 +1,18 @@
+use crate::app::{AppConfig, AppPreferences};
+
+#[derive(Clone)]
+pub struct AppState {
+    pub config: AppConfig,
+    pub preferences: AppPreferences,
+    // authentication
+    // ...
+}
+
+impl AppState {
+    pub fn new() -> Self {
+        AppState {
+            config: AppConfig::new(),
+            preferences: AppPreferences {},
+        }
+    }
+}
diff --git a/src/main.rs b/src/main.rs
index 1bd1725..c996be1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,4 @@
-mod app_state;
+mod app;
 mod service;
 mod static_files;
 mod testing;
@@ -6,7 +6,7 @@ mod tls_config;
 mod website;
 use actix_web::{App, HttpServer};
 use actix_web_lab::middleware::RedirectHttps;
-use app_state::AppState;
+use app::AppState;
 use static_files::StaticFilesManager;
 use tls_config::tls_config;
 use website::WebSite;
diff --git a/src/service.rs b/src/service/mod.rs
similarity index 100%
rename from src/service.rs
rename to src/service/mod.rs
diff --git a/src/static_files.rs b/src/static_files.rs
index c4fbab6..4da405f 100644
--- a/src/static_files.rs
+++ b/src/static_files.rs
@@ -1,4 +1,4 @@
-use crate::app_state::AppState;
+use crate::app::AppState;
 use std::path::{Path, PathBuf};
 
 pub struct StaticFilesManager {
diff --git a/src/tls_config.rs b/src/tls_config.rs
index 61c23e8..b69f528 100644
--- a/src/tls_config.rs
+++ b/src/tls_config.rs
@@ -1,4 +1,4 @@
-use crate::app_state::AppConfig;
+use crate::app::AppConfig;
 
 pub fn tls_config(app_config: &AppConfig) -> rustls::ServerConfig {
     let certs_dir = app_config.ssl_certs_dir.clone();
diff --git a/src/website/html.rs b/src/website/html.rs
new file mode 100644
index 0000000..9385527
--- /dev/null
+++ b/src/website/html.rs
@@ -0,0 +1,46 @@
+use crate::website::page::PageData;
+use regex::{Captures, Regex};
+use serde::{Deserialize, Serialize};
+
+pub const HTML_DOC_TEMPLATE: &'static str = "
+<html lang='{lang}'>
+<head>
+    <meta charset='UTF-8'>
+    <meta http-equiv='X-UA-Compatible' content='IE=edge'>
+    <meta name='viewport' content='width=device-width, initial-scale=1.0'>
+    <meta name='description' content='{description}'>
+    <title>{title}</title>
+    <link rel='stylesheet' href='{css}'>
+</head>
+
+<body>
+    {body}
+</body>
+
+<script src='{js}'></script>
+
+</html>
+";
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct HtmlDoc(String);
+
+impl HtmlDoc {
+    pub fn from_page_data(page_data: &PageData) -> Self {
+        let re = Regex::new(r#"\{[a-z]+\}"#).unwrap();
+
+        let html = re
+            .replace_all(HTML_DOC_TEMPLATE, |captures: &Captures| {
+                let placeholder = captures.iter().next().unwrap().unwrap().as_str();
+                let placeholder = placeholder[1..placeholder.len() - 1].to_owned();
+                page_data.field_from_str_key(placeholder)
+            })
+            .to_string();
+
+        HtmlDoc(html)
+    }
+
+    pub fn to_string(&self) -> String {
+        self.0.clone()
+    }
+}
diff --git a/src/website/mod.rs b/src/website/mod.rs
new file mode 100644
index 0000000..fcd415f
--- /dev/null
+++ b/src/website/mod.rs
@@ -0,0 +1,5 @@
+mod html;
+mod page;
+mod website;
+
+pub use website::*;
diff --git a/src/website/page.rs b/src/website/page.rs
new file mode 100644
index 0000000..4f38e7a
--- /dev/null
+++ b/src/website/page.rs
@@ -0,0 +1,47 @@
+use crate::website::html::HtmlDoc;
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct PageData {
+    pub title: String,
+    pub lang: String,
+    pub description: String,
+    pub slug: String,
+    pub html_body: String,
+    pub css_src: Option<String>,
+    pub js_src: Option<String>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct WebPage {
+    pub page_data: PageData,
+    pub html_doc: HtmlDoc,
+}
+
+impl PageData {
+    pub fn to_web_page(&self) -> WebPage {
+        WebPage {
+            page_data: self.clone(),
+            html_doc: HtmlDoc::from_page_data(&self),
+        }
+    }
+
+    pub fn field_from_str_key(&self, key: String) -> String {
+        match &key[..] {
+            "title" => self.title.to_owned(),
+            "lang" => self.lang.to_owned(),
+            "description" => self.description.to_owned(),
+            "slug" => self.slug.to_owned(),
+            "body" => self.html_body.to_owned(),
+            "css" => self.css_src.as_ref().unwrap_or(&String::new()).to_owned(),
+            "js" => self.js_src.as_ref().unwrap_or(&String::new()).to_owned(),
+            _ => String::new(),
+        }
+    }
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct PagesTree {
+    pub page_data: PageData,
+    pub sub_pages: Option<Vec<PagesTree>>,
+}
diff --git a/src/website.rs b/src/website/website.rs
similarity index 53%
rename from src/website.rs
rename to src/website/website.rs
index c65d5c5..6a6cb1f 100644
--- a/src/website.rs
+++ b/src/website/website.rs
@@ -1,87 +1,9 @@
-use crate::app_state::AppConfig;
-use regex::{Captures, Regex};
+use crate::app::AppConfig;
+use crate::website::page::{PagesTree, WebPage};
 use serde::{Deserialize, Serialize};
 use std::collections::HashMap;
 use std::path::PathBuf;
 
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct PageData {
-    pub title: String,
-    pub lang: String,
-    pub description: String,
-    pub slug: String,
-    pub html_body: String,
-    pub css_src: Option<String>,
-    pub js_src: Option<String>,
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct HtmlDoc(String);
-
-impl HtmlDoc {
-    fn load_template() -> String {
-        std::fs::read_to_string(
-            std::env::current_dir()
-                .unwrap()
-                .join("templates")
-                .join("html_doc.html"),
-        )
-        .expect("Missing html_doc template")
-    }
-
-    pub fn from_page_data(page_data: &PageData) -> Self {
-        let re = Regex::new(r#"\{[a-z]+\}"#).unwrap();
-
-        let html = re
-            .replace_all(&HtmlDoc::load_template(), |captures: &Captures| {
-                let placeholder = captures.iter().next().unwrap().unwrap().as_str();
-                let placeholder = placeholder[1..placeholder.len() - 1].to_owned();
-                page_data.field_from_str_key(placeholder)
-            })
-            .to_string();
-
-        HtmlDoc(html)
-    }
-
-    pub fn to_string(&self) -> String {
-        self.0.clone()
-    }
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct WebPage {
-    pub page_data: PageData,
-    pub html_doc: HtmlDoc,
-}
-
-impl PageData {
-    pub fn to_web_page(&self) -> WebPage {
-        WebPage {
-            page_data: self.clone(),
-            html_doc: HtmlDoc::from_page_data(&self),
-        }
-    }
-
-    pub fn field_from_str_key(&self, key: String) -> String {
-        match &key[..] {
-            "title" => self.title.to_owned(),
-            "lang" => self.lang.to_owned(),
-            "description" => self.description.to_owned(),
-            "slug" => self.slug.to_owned(),
-            "body" => self.html_body.to_owned(),
-            "css" => self.css_src.as_ref().unwrap_or(&String::new()).to_owned(),
-            "js" => self.js_src.as_ref().unwrap_or(&String::new()).to_owned(),
-            _ => String::new(),
-        }
-    }
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct PagesTree {
-    pub page_data: PageData,
-    pub sub_pages: Option<Vec<PagesTree>>,
-}
-
 #[derive(Debug, Serialize, Deserialize, Clone)]
 pub struct WebSite {
     pages_tree: PagesTree,
diff --git a/templates/html_doc.html b/templates/html_doc.html
deleted file mode 100644
index e856898..0000000
--- a/templates/html_doc.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<html lang="{lang}">
-
-<head>
-    <meta charset="UTF-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <meta name="description" content="{description}">
-    <title>{title}</title>
-    <link rel="stylesheet" href="{css}">
-</head>
-
-<body>
-    {body}
-</body>
-<script src="{js}"></script>
-
-</html>
\ No newline at end of file
diff --git a/templates/new_website.json b/templates/new_website.json
index 92a78fa..c6c845b 100644
--- a/templates/new_website.json
+++ b/templates/new_website.json
@@ -6,35 +6,5 @@
         "description": "A new website",
         "html_body": "<h1>New Website</h1>"
     },
-    "sub_pages": [
-        {
-            "page_data": {
-                "title": "A sub page",
-                "lang": "en",
-                "slug": "subpage",
-                "description": "A sub page of the new web site",
-                "html_body": "<h1>A sub page</h1>"
-            }
-        },
-        {
-            "page_data": {
-                "title": "Some other page",
-                "lang": "en",
-                "slug": "otherpage",
-                "description": "Some other page of the new web site",
-                "html_body": "<h1>Another page</h1>"
-            },
-            "sub_pages": [
-                {
-                    "page_data": {
-                        "title": "A sub page of the other page",
-                        "lang": "en",
-                        "slug": "othersubpage",
-                        "description": "A sub page of the other page of the new web site",
-                        "html_body": "<h1>A subpage</h1>"
-                    }
-                }
-            ]
-        }
-    ]
+    "sub_pages": []
 }
\ No newline at end of file
-- 
GitLab