M .gitignore => .gitignore +14 -1
@@ 1,1 1,14 @@
-/target
+# Generated by Cargo
+# will have compiled files and executables
+debug/
+target/
+
+# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
+# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
+Cargo.lock
+
+# These are backup files generated by rustfmt
+**/*.rs.bk
+
+# MSVC Windows builds of rustc generate these, which store debugging information
+*.pdb
M Cargo.toml => Cargo.toml +7 -1
@@ 6,6 6,12 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+dirs = "4.0.0"
git2 = "0.15.0"
+lazy_static = "1.4.0"
markdown = "0.3.0"
-rocket = "0.4.11"
+rocket = "0.5.0-rc"
+rocket_dyn_templates = {version = "0.1.0-rc.2", features=["handlebars"]}
+serde = "1.0.145"
+serde_derive = "1.0.145"
+toml = "0.5.9"
A Rocket.toml +2 -0
@@ 0,0 1,2 @@
+[default]
+template_dir = "templates"
A src/config.rs +32 -0
@@ 0,0 1,32 @@
+use lazy_static::lazy_static;
+use serde_derive::{Serialize, Deserialize};
+use std::fs;
+
+lazy_static! {
+ pub static ref CONFIG: Config = {
+ let mut config = dirs::config_dir().expect("Could not get the config directory");
+ config.push("git-server");
+ config.push("git-server.toml");
+ if config.exists() {
+ let config:Config = toml::from_str(&String::from_utf8_lossy(&fs::read(config.as_path()).expect("Failed to read the config file"))).expect("Could not parse the toml in the config file");
+ config
+ }
+ else {
+ let config_struct = Config {
+ name: "Example User".to_string(),
+ description: "This is billy and he loves his [website](https://example.com)!!!".to_string()
+ };
+ config.pop();
+ let _ = fs::create_dir_all(config.clone());
+ config.push("git-server.toml");
+ fs::write(config, toml::to_string(&config_struct).expect("Failed to stringify config")).expect("Failed to set the config content");
+ config_struct
+ }
+ };
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct Config {
+ pub name: String,
+ pub description: String,
+}
A src/git/mod.rs +1 -0
@@ 0,0 1,1 @@
+pub mod repos;
A src/git/repos.rs +38 -0
@@ 0,0 1,38 @@
+use std::fs;
+use serde_derive::Serialize;
+
+pub fn get_repos() -> Option<Vec<Repo>> {
+ let home = dirs::home_dir()?;
+ Some(fs::read_dir(home.clone()).ok()?
+ .filter(|path| path.is_ok())
+ .map(|path| path.unwrap())
+ .filter(|path| path.file_type().is_ok())
+ .filter(|path| path.file_type().unwrap().is_dir())
+ .map(|path| path.file_name().into_string())
+ .filter(|path| path.is_ok())
+ .map(|path| path.unwrap())
+ .filter(|path| !path.starts_with("."))
+ .filter(|path| path.ends_with(".git"))
+ .map(|path| {
+ let mut description_path = home.clone();
+ description_path.push(path.clone());
+ description_path.push("DESCRIPTION");
+ let description = match fs::read(description_path.clone()) {
+ Ok(content) => {
+ String::from_utf8_lossy(&content).parse().unwrap_or("".to_string())
+ },
+ Err(_) => "".to_string()
+ };
+ Repo {
+ name: path[0..path.len() - 4].to_string(),
+ description
+ }
+ })
+ .collect::<Vec<Repo>>())
+}
+
+#[derive(Serialize, Clone)]
+pub struct Repo {
+ name: String,
+ description: String,
+}
A src/index.rs +22 -0
@@ 0,0 1,22 @@
+use crate::git::repos::get_repos;
+use crate::config::CONFIG;
+
+use rocket_dyn_templates::{Template, context};
+
+#[get("/?<page>")]
+pub fn index(page: Option<usize>) -> Option<Template> {
+ let repos = get_repos()?;
+ let page = page.unwrap_or(0);
+ let last_item = if repos.len() > (page+1)*25 {
+ (page+1)*25
+ }
+ else {
+ repos.len()
+ };
+ Some(Template::render("index", context! {
+ title: "Me",
+ user: CONFIG.name.clone(),
+ description: markdown::to_html(&CONFIG.description.clone()),
+ repos: repos[page*25..last_item].to_vec(),
+ }))
+}
M src/main.rs => src/main.rs +12 -2
@@ 1,3 1,13 @@
-fn main() {
- println!("Hello, world!");
+#[macro_use] extern crate rocket;
+mod git;
+mod index;
+mod config;
+
+use rocket_dyn_templates::Template;
+
+#[launch]
+fn rocket() -> _ {
+ rocket::build()
+ .mount("/", routes![index::index])
+ .attach(Template::fairing())
}
A templates/index.html.hbs +18 -0
@@ 0,0 1,18 @@
+{{#*inline "page"}}
+
+<section id="hello">
+ <h1>{{ user }}!</h1>
+ <h2>{{{ description }}}</h2>
+ <h3>Here are your repos:</h3>
+ <ul>
+ {{#each repos}}
+ <div>
+ <a href="/{{this.name}}">{{ this.name }}</a>
+ <p>{{ this.description }}</p>
+ </div>
+ {{/each}}
+ </ul>
+</section>
+
+{{/inline}}
+{{> layout}}
A templates/layout.html.hbs +9 -0
@@ 0,0 1,9 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{{ title }} :: Arthur Melton Git</title>
+ </head>
+ <body>
+ {{~> page}}
+ </body>
+</html>