Forráskód Böngészése

Fixes #8: Add templates (handlebars). Related to #6 front-end.

Alexandre Leblanc 3 éve
szülő
commit
e6bbfe296f

+ 2 - 0
.gitignore

@@ -11,3 +11,5 @@
 # Generated by Cargo
 /target/
 /www/
+/articles-repo
+/not-git

+ 1 - 0
.idea/vcs.xml

@@ -2,5 +2,6 @@
 <project version="4">
   <component name="VcsDirectoryMappings">
     <mapping directory="$PROJECT_DIR$" vcs="Git" />
+    <mapping directory="$PROJECT_DIR$/articles-repo" vcs="Git" />
   </component>
 </project>

+ 521 - 0
Cargo.lock

@@ -245,6 +245,12 @@ dependencies = [
  "alloc-no-stdlib",
 ]
 
+[[package]]
+name = "arc-swap"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164"
+
 [[package]]
 name = "askama_escape"
 version = "0.10.3"
@@ -299,6 +305,21 @@ dependencies = [
  "alloc-stdlib",
 ]
 
+[[package]]
+name = "bstr"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
+
 [[package]]
 name = "bytes"
 version = "1.1.0"
@@ -377,6 +398,16 @@ dependencies = [
  "cfg-if",
 ]
 
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
 [[package]]
 name = "crypto-common"
 version = "0.1.4"
@@ -410,6 +441,12 @@ dependencies = [
  "crypto-common",
 ]
 
+[[package]]
+name = "doc-comment"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
+
 [[package]]
 name = "encoding_rs"
 version = "0.8.31"
@@ -444,6 +481,103 @@ dependencies = [
  "miniz_oxide",
 ]
 
+[[package]]
+name = "fluent"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61f69378194459db76abd2ce3952b790db103ceb003008d3d50d97c41ff847a7"
+dependencies = [
+ "fluent-bundle",
+ "unic-langid",
+]
+
+[[package]]
+name = "fluent-bundle"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e242c601dec9711505f6d5bbff5bedd4b61b2469f2e8bb8e57ee7c9747a87ffd"
+dependencies = [
+ "fluent-langneg",
+ "fluent-syntax",
+ "intl-memoizer",
+ "intl_pluralrules",
+ "rustc-hash",
+ "self_cell",
+ "smallvec",
+ "unic-langid",
+]
+
+[[package]]
+name = "fluent-langneg"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94"
+dependencies = [
+ "unic-langid",
+]
+
+[[package]]
+name = "fluent-syntax"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0abed97648395c902868fee9026de96483933faa54ea3b40d652f7dfe61ca78"
+dependencies = [
+ "thiserror",
+]
+
+[[package]]
+name = "fluent-template-macros"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0209363718816f86738043b0e337119133a6135bde1c25e654a563c8a666addb"
+dependencies = [
+ "flume",
+ "ignore",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "unic-langid",
+]
+
+[[package]]
+name = "fluent-templates"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "968769c4890659e8262fae40c43fb63b9d59c8e205d7696bda75e89e5a371154"
+dependencies = [
+ "arc-swap",
+ "fluent",
+ "fluent-bundle",
+ "fluent-langneg",
+ "fluent-syntax",
+ "fluent-template-macros",
+ "flume",
+ "handlebars",
+ "heck",
+ "ignore",
+ "intl-memoizer",
+ "lazy_static",
+ "log",
+ "once_cell",
+ "serde_json",
+ "snafu",
+ "unic-langid",
+]
+
+[[package]]
+name = "flume"
+version = "0.10.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+ "nanorand",
+ "pin-project",
+ "spin",
+]
+
 [[package]]
 name = "fnv"
 version = "1.0.7"
@@ -516,8 +650,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
 dependencies = [
  "cfg-if",
+ "js-sys",
  "libc",
  "wasi 0.11.0+wasi-snapshot-preview1",
+ "wasm-bindgen",
 ]
 
 [[package]]
@@ -535,6 +671,19 @@ dependencies = [
  "url",
 ]
 
+[[package]]
+name = "globset"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a"
+dependencies = [
+ "aho-corasick",
+ "bstr",
+ "fnv",
+ "log",
+ "regex",
+]
+
 [[package]]
 name = "h2"
 version = "0.3.13"
@@ -554,12 +703,33 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "handlebars"
+version = "4.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "360d9740069b2f6cbb63ce2dbaa71a20d3185350cbb990d7bebeb9318415eb17"
+dependencies = [
+ "log",
+ "pest",
+ "pest_derive",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "walkdir",
+]
+
 [[package]]
 name = "hashbrown"
 version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
 
+[[package]]
+name = "heck"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+
 [[package]]
 name = "hermit-abi"
 version = "0.1.19"
@@ -609,6 +779,24 @@ dependencies = [
  "unicode-normalization",
 ]
 
+[[package]]
+name = "ignore"
+version = "0.4.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d"
+dependencies = [
+ "crossbeam-utils",
+ "globset",
+ "lazy_static",
+ "log",
+ "memchr",
+ "regex",
+ "same-file",
+ "thread_local",
+ "walkdir",
+ "winapi-util",
+]
+
 [[package]]
 name = "indexmap"
 version = "1.9.1"
@@ -619,6 +807,26 @@ dependencies = [
  "hashbrown",
 ]
 
+[[package]]
+name = "intl-memoizer"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c310433e4a310918d6ed9243542a6b83ec1183df95dff8f23f87bb88a264a66f"
+dependencies = [
+ "type-map",
+ "unic-langid",
+]
+
+[[package]]
+name = "intl_pluralrules"
+version = "7.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b18f988384267d7066cc2be425e6faf352900652c046b6971d2e228d3b1c5ecf"
+dependencies = [
+ "tinystr",
+ "unic-langid",
+]
+
 [[package]]
 name = "itoa"
 version = "1.0.2"
@@ -634,12 +842,27 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "js-sys"
+version = "0.3.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2"
+dependencies = [
+ "wasm-bindgen",
+]
+
 [[package]]
 name = "language-tags"
 version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
 
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
 [[package]]
 name = "libc"
 version = "0.2.126"
@@ -772,6 +995,15 @@ dependencies = [
  "windows-sys",
 ]
 
+[[package]]
+name = "nanorand"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
+dependencies = [
+ "getrandom",
+]
+
 [[package]]
 name = "num-integer"
 version = "0.1.45"
@@ -870,6 +1102,70 @@ version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
 
+[[package]]
+name = "pest"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69486e2b8c2d2aeb9762db7b4e00b0331156393555cff467f4163ff06821eef8"
+dependencies = [
+ "thiserror",
+ "ucd-trie",
+]
+
+[[package]]
+name = "pest_derive"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b13570633aff33c6d22ce47dd566b10a3b9122c2fe9d8e7501895905be532b91"
+dependencies = [
+ "pest",
+ "pest_generator",
+]
+
+[[package]]
+name = "pest_generator"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3c567e5702efdc79fb18859ea74c3eb36e14c43da7b8c1f098a4ed6514ec7a0"
+dependencies = [
+ "pest",
+ "pest_meta",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pest_meta"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5eb32be5ee3bbdafa8c7a18b0a8a8d962b66cfa2ceee4037f49267a50ee821fe"
+dependencies = [
+ "once_cell",
+ "pest",
+ "sha-1",
+]
+
+[[package]]
+name = "pin-project"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78203e83c48cffbe01e4a2d35d566ca4de445d79a85372fc64e378bfc812a260"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "pin-project-lite"
 version = "0.2.9"
@@ -894,6 +1190,12 @@ version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
 
+[[package]]
+name = "proc-macro-hack"
+version = "0.5.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
+
 [[package]]
 name = "proc-macro2"
 version = "1.0.40"
@@ -980,6 +1282,12 @@ version = "0.6.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
 
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
 [[package]]
 name = "rustc_version"
 version = "0.4.0"
@@ -1010,6 +1318,12 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
+[[package]]
+name = "self_cell"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af"
+
 [[package]]
 name = "semver"
 version = "1.0.12"
@@ -1021,6 +1335,20 @@ name = "serde"
 version = "1.0.138"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.138"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
 
 [[package]]
 name = "serde_json"
@@ -1045,6 +1373,17 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "sha-1"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
 [[package]]
 name = "sha1"
 version = "0.10.1"
@@ -1077,6 +1416,28 @@ version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
 
+[[package]]
+name = "snafu"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5177903bf45656592d9eb5c0e22f408fc023aae51dbe2088889b71633ba451f2"
+dependencies = [
+ "doc-comment",
+ "snafu-derive",
+]
+
+[[package]]
+name = "snafu-derive"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "410b26ed97440d90ced3e2488c868d56a86e2064f5d7d6f417909b286afe25e5"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "socket2"
 version = "0.4.4"
@@ -1087,6 +1448,15 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "spin"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09"
+dependencies = [
+ "lock_api",
+]
+
 [[package]]
 name = "syn"
 version = "1.0.98"
@@ -1098,6 +1468,35 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "thiserror"
+version = "1.0.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
+dependencies = [
+ "once_cell",
+]
+
 [[package]]
 name = "time"
 version = "0.1.44"
@@ -1127,6 +1526,12 @@ version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
 
+[[package]]
+name = "tinystr"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29738eedb4388d9ea620eeab9384884fc3f06f586a2eddb56bedc5885126c7c1"
+
 [[package]]
 name = "tinyvec"
 version = "1.6.0"
@@ -1195,12 +1600,70 @@ dependencies = [
  "once_cell",
 ]
 
+[[package]]
+name = "type-map"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6d3364c5e96cb2ad1603037ab253ddd34d7fb72a58bdddf4b7350760fc69a46"
+dependencies = [
+ "rustc-hash",
+]
+
 [[package]]
 name = "typenum"
 version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
 
+[[package]]
+name = "ucd-trie"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c"
+
+[[package]]
+name = "unic-langid"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73328fcd730a030bdb19ddf23e192187a6b01cd98be6d3140622a89129459ce5"
+dependencies = [
+ "unic-langid-impl",
+ "unic-langid-macros",
+]
+
+[[package]]
+name = "unic-langid-impl"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a4a8eeaf0494862c1404c95ec2f4c33a2acff5076f64314b465e3ddae1b934d"
+dependencies = [
+ "tinystr",
+]
+
+[[package]]
+name = "unic-langid-macros"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18f980d6d87e8805f2836d64b4138cc95aa7986fa63b1f51f67d5fbff64dd6e5"
+dependencies = [
+ "proc-macro-hack",
+ "tinystr",
+ "unic-langid-impl",
+ "unic-langid-macros-impl",
+]
+
+[[package]]
+name = "unic-langid-macros-impl"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29396ffd97e27574c3e01368b1a64267d3064969e4848e2e130ff668be9daa9f"
+dependencies = [
+ "proc-macro-hack",
+ "quote",
+ "syn",
+ "unic-langid-impl",
+]
+
 [[package]]
 name = "unicase"
 version = "2.6.0"
@@ -1263,9 +1726,13 @@ dependencies = [
  "actix-web",
  "chrono",
  "fern",
+ "fluent-templates",
  "git2",
+ "handlebars",
  "log",
  "pulldown-cmark",
+ "serde",
+ "serde_json",
  "walkdir",
 ]
 
@@ -1298,6 +1765,60 @@ version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a"
+
 [[package]]
 name = "winapi"
 version = "0.3.9"

+ 6 - 1
Cargo.toml

@@ -3,14 +3,19 @@ name = "vcs-blog"
 version = "0.1.0"
 edition = "2021"
 description = "Blog engine running on version control system to store articles"
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+license = "Apache-2.0"
+license-file = "LICENSE"
 
 [dependencies]
 actix-web = "4"
 actix-files = "0.6.1"
 chrono = "0.4"
 fern = "0.6"
+fluent-templates = { version = "0.7", features = ["handlebars"] }
 git2 = "0.14.4"
+handlebars = { version = "4.3", features = ["dir_source"] }
 log = "0.4"
 pulldown-cmark = "0.9"
+serde = { version = "1", features = ["derive"] }
+serde_json = "1"
 walkdir = "2.3.2"

+ 61 - 19
src/main.rs

@@ -7,44 +7,71 @@ mod theme;
 use actix_web::middleware::Logger;
 use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};
 use fern;
+use fluent_templates::{static_loader, FluentLoader, Loader as _};
+use handlebars::Handlebars;
+use serde_json;
 use std::{env, io};
 
 use crate::config::Configuration;
-use crate::repository::{ArticlesRepository, FsRepository};
+use crate::repository::{ArticlesRepository, GitRepository};
 use crate::theme::Theme;
 
+const ARTICLE_BASE_PATH: &'static str = "./articles-repo";
+const DEFAULT_LANGUAGE: &'static str = "fr";
+
+static_loader! {
+    static LOCALES = {
+        locales: "./www/themes/default/locales",
+        fallback_language: "en",
+
+        // removes unicode isolating marks around arguments
+        // you typically should only set to false when testing.
+        customise: |bundle| bundle.set_use_isolating(false),
+    };
+}
+
 #[get("")]
-async fn show_articles_list() -> impl Responder {
-    let repo: FsRepository = FsRepository {};
+async fn show_articles_list(hb: web::Data<Handlebars<'_>>) -> impl Responder {
+    let repo = GitRepository {};
 
-    const CONTENT_FOLDER: &str = "./content";
     let exe_dir = env::current_dir()
         .and_then(|dir| Ok(dir.to_str().unwrap_or("").to_string()))
         .unwrap();
-    log::trace!("Estimated content repository: {exe_dir}/{CONTENT_FOLDER}");
+    log::trace!("Serving article from repository: {exe_dir}/{ARTICLE_BASE_PATH}");
 
-    let paths = repo.get_directory_listing(&CONTENT_FOLDER).join("\n");
+    let paths = repo.get_directory_listing(ARTICLE_BASE_PATH);
 
-    HttpResponse::Ok().body(format!("List of articles:\r\n{paths}"))
-}
+    let data = serde_json::json!({
+        "page-title": "Article",
+        "entries": &paths
+    });
+    let body = hb.render("articles_list", &data).unwrap();
 
-#[get("/{path:.+}")]
-async fn show_article(path: web::Path<(String,)>) -> impl Responder {
-    let p = format!("./content/{}", &path.0);
+    HttpResponse::Ok().body(body)
+}
 
+#[get("/{path:.+}.html")]
+async fn show_article(hb: web::Data<Handlebars<'_>>, path: web::Path<(String,)>) -> impl Responder {
+    let p = format!("{ARTICLE_BASE_PATH}/{}", &path.0);
     let exe_dir = env::current_dir()
         .and_then(|dir| Ok(dir.to_str().unwrap_or("").to_string()))
         .unwrap();
+    log::trace!("Estimated content repository: {p}");
 
-    let theme_path = format!("{}/www/themes/default", exe_dir);
-    log::info!("Theme path: {}", theme_path);
-
-    let theme = Theme::new(theme_path);
+    let theme: Theme = Theme::new(format!("{}/{}", exe_dir, ARTICLE_BASE_PATH));
 
     let rendered = theme.markdown_as_html(&p);
 
     match rendered {
-        Ok(content) => HttpResponse::Ok().body(content),
+        Ok(content) => {
+            let data = serde_json::json!({
+                "page-title": "Article",
+                "article": &content
+            });
+            let body = hb.render("article", &data).unwrap();
+
+            HttpResponse::Ok().body(body)
+        }
         Err(e) => {
             log::error!("Couldn't find article located at '{}'. Error: {}", p, e);
             HttpResponse::NotFound().finish()
@@ -70,13 +97,24 @@ async fn main() -> io::Result<()> {
                 message
             ))
         })
-        .level(log::LevelFilter::Info)
+        .level(log::LevelFilter::Trace)
         .chain(io::stdout())
         .apply();
 
-    HttpServer::new(|| {
+    let mut handlebars = Handlebars::new();
+
+    // register template dir with Handlebars registry
+    handlebars
+        .register_templates_directory(".html", "./www/themes/default/")
+        .unwrap();
+    // FLUENT:
+    //handlebars.register_helper("fluent", Box::new(FluentLoader::new(&*LOCALES)));
+    let handlebars = web::Data::new(handlebars);
+
+    HttpServer::new(move || {
         App::new()
             .wrap(Logger::default())
+            .app_data(web::Data::clone(&handlebars))
             .service(actix_files::Files::new("/css", "./www/themes/default/css"))
             .service(actix_files::Files::new(
                 "/images",
@@ -86,7 +124,11 @@ async fn main() -> io::Result<()> {
             .service(
                 web::scope("/a")
                     .service(show_article)
-                    .service(show_articles_list),
+                    .service(show_articles_list)
+                    .service(actix_files::Files::new("/", "./articles-repo")),
+            )
+            .default_service(
+                actix_files::Files::new("/", "./www/themes/default").index_file("index.html"),
             )
     })
     .bind((config.listen_addr, config.listen_port))?

+ 2 - 7
src/repository/articles_repository.rs

@@ -1,13 +1,8 @@
-pub struct ArticleAuthor {
-    pub name: String,
-    pub email: String,
-}
-
-pub struct ArticleEntry {
+pub struct ArticlesListEntry {
     pub version_id: String,
     pub title: String,
     pub date: String,
-    pub author: ArticleAuthor,
+    pub author: String,
     pub path: String,
 }
 

+ 4 - 3
src/repository/fs_repository.rs

@@ -14,6 +14,7 @@ impl ArticlesRepository for FsRepository {
                 entry.file_name().to_string_lossy().ends_with(".md") && entry.file_type().is_file()
             })
             .map(|entry| entry.path().to_str().unwrap().to_string())
+            .map(|entry| entry[0..entry.len() - ".md".len()].to_string())
             .collect::<Vec<String>>()
     }
 }
@@ -34,10 +35,10 @@ mod tests_fs_repository {
     #[test]
     fn when_path_is_valid_should_return_proper_listing() {
         let repo = FsRepository {};
-        let values = repo.get_directory_listing("./content");
+        let values = repo.get_directory_listing("./articles-repo");
         let expected = vec![
-            "./content\\some-category\\article.md".to_owned(),
-            "./content\\test.md".to_owned(),
+            "./articles-repo\\some-category\\article.md".to_owned(),
+            "./articles-repo\\test.md".to_owned(),
         ];
 
         assert_eq!(expected, values);

+ 20 - 27
src/repository/git_repository.rs

@@ -2,27 +2,21 @@ use git2;
 
 use crate::ArticlesRepository;
 
-pub struct GitRepository {
-    repo: git2::Repository,
-}
-
-impl GitRepository {
-    fn from_path(path: &str) -> Result<Self, git2::Error> {
-        let repo = git2::Repository::open(path)?;
-
-        Ok(GitRepository { repo })
-    }
-}
+pub struct GitRepository {}
 
 //Modification times: https://github.com/Shnatsel/rustsec/blob/f043c75731df311292a7e4f4dbb05c6728d055f5/rustsec/src/repository/git/modification_time.rs#L18
 impl ArticlesRepository for GitRepository {
     fn get_directory_listing(&self, path: &str) -> Vec<String> {
-        match self.repo.index() {
-            Ok(index) => index
-                .iter()
-                .map(|entry| String::from_utf8(entry.path).unwrap())
-                .filter(|entry| entry.ends_with(".md"))
-                .collect(),
+        match git2::Repository::open(path) {
+            Ok(repo) => match repo.index() {
+                Ok(index) => index
+                    .iter()
+                    .map(|entry| String::from_utf8(entry.path).unwrap())
+                    .filter(|entry| entry.ends_with(".md"))
+                    .map(|entry| entry[0..entry.len() - ".md".len()].to_string())
+                    .collect(),
+                Err(_) => Vec::new(),
+            },
             Err(_) => Vec::new(),
         }
     }
@@ -31,22 +25,21 @@ impl ArticlesRepository for GitRepository {
 #[cfg(test)]
 mod tests_git_repository {
     use super::*;
-    use git2::IntoCString;
 
     #[test]
     fn when_path_is_empty_should_return_nothing() {
-        let repo = GitRepository::from_path(".").expect("Should have stuff.");
+        let repo = GitRepository {};
+        let result = repo.get_directory_listing("./not-git");
 
-        for path in repo.get_directory_listing(".") {
-            println!("{path}");
-        }
+        assert_eq!(result.len(), 0);
     }
 
-    // TODO: Change directory to something where there is no folders.
     #[test]
-    fn when_path_is_valid_should_return_proper_listing() {}
+    fn when_path_is_valid_should_return_proper_listing() {
+        let repo = GitRepository {};
+        let values = repo.get_directory_listing("./articles-repo");
+        let expected = vec!["some-category/article.md".to_owned(), "test.md".to_owned()];
 
-    // Blahblahblah
-    #[test]
-    fn when_fetching_file_informations_return_git_info() {}
+        assert_eq!(expected, values);
+    }
 }

+ 1 - 13
src/theme.rs

@@ -31,12 +31,7 @@ impl Theme {
                 let mut html_output = String::new();
                 html::push_html(&mut html_output, parser);
 
-                let mut buf = String::new();
-                buf.push_str(self.get_fragment("header").as_str());
-                buf.push_str(html_output.as_str());
-                buf.push_str(self.get_fragment("footer").as_str());
-
-                Ok(buf)
+                Ok(html_output)
             }
             Err(error) => {
                 log::error!("File located at path {} was not found.", path);
@@ -44,11 +39,4 @@ impl Theme {
             }
         }
     }
-
-    // TODO: Cache
-    fn get_fragment(&self, fragment_name: &str) -> String {
-        let path = format!("{}/{}.html.tpl", self.root_path, fragment_name);
-        log::trace!("Loading fragment file '{}'", path);
-        std::fs::read_to_string(path).unwrap_or("".to_owned())
-    }
 }

+ 0 - 34
www/themes/default/footer.html.tpl

@@ -1,34 +0,0 @@
-<hr />
-<footer>
-    <div class="row">
-        <div class="six columns">
-            <h5>Profils</h5>
-            <p>
-                <span class="external">
-                    <a href="https://www.linkedin.com/in/mrtryhard/" class="lnk linked-in" target="_blank">LinkedIn</a>
-                </span>
-                <br />
-                <span class="external">
-                        <a href="https://www.github.com/mrtryhard" class="lnk github" target="_blank">GitHub</a>
-                </span>
-            </p>
-        </div>
-        <div class="six columns centered">
-            <h5>Contact</h5>
-            <a class="lnk no-style mail">a.leblanc@&lt;ce domaine&gt;</a>
-            <br />
-            Trouvez-moi parfois à <span class="external"><a href="http://meetup.com/cppmtl" class="lnk canada" target="_blank">C++ Meetups Montréal</a></span> !
-        </div>
-    </div>
-    <div>
-        <p style="text-align:center;">
-            <small><em>
-                &copy;2019 - Alexandre Leblanc, droits réservés. Les droits sur les logos sont réservés par leurs propriétaires respectifs (Université de Sherbrooke, ReaQtor, GitHub, LinkedIn).
-            </em></small>
-        </p>
-    </div>
-</footer>
-</div>
-<script>hljs.highlightAll();</script>
-</body>
-<html>

+ 0 - 17
www/themes/default/header.html.tpl

@@ -1,17 +0,0 @@
-<!DOCTYPE html>
-<html lang="fr">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <title>{TITLE}</title>
-    <link href="https://fonts.googleapis.com/css?family=Raleway&display=swap" rel="stylesheet">
-    <link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/default.min.css" rel="stylesheet">
-    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script>
-    <link rel="stylesheet" href="/css/normalize.css" />
-    <link rel="stylesheet" href="/css/skeleton.css" />
-    <link rel="stylesheet" href="/css/style.css" />
-</head>
-<body>
-<div class="container">
-
-

BIN
www/themes/default/images/reaqtor-q.png