aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/frontend/pages/home.rs4
-rw-r--r--src/frontend/pages/layout.rs10
-rw-r--r--src/frontend/pages/mod.rs61
-rw-r--r--src/frontend/pages/node.rs46
-rw-r--r--src/frontend/style/layout.css35
-rw-r--r--src/frontend/style/master.css0
-rw-r--r--src/frontend/style/mod.rs2
-rw-r--r--src/library.rs91
-rw-r--r--src/main.rs28
9 files changed, 250 insertions, 27 deletions
diff --git a/src/frontend/pages/home.rs b/src/frontend/pages/home.rs
index a694c3c..bb0eb59 100644
--- a/src/frontend/pages/home.rs
+++ b/src/frontend/pages/home.rs
@@ -5,9 +5,9 @@ use crate::{
use actix_web::{get, web::Data, Responder};
#[get("/")]
-async fn page_home(state: Data<AppState>) -> impl Responder {
+pub async fn page_home(state: Data<AppState>) -> impl Responder {
HtmlTemplate(Layout {
- title: "Home - Jellything",
+ title: String::from("Home"),
main: markup::new! {
h1 { "It works!" }
},
diff --git a/src/frontend/pages/layout.rs b/src/frontend/pages/layout.rs
index e02336d..5654d3b 100644
--- a/src/frontend/pages/layout.rs
+++ b/src/frontend/pages/layout.rs
@@ -1,17 +1,19 @@
use markup::Render;
markup::define! {
- Layout<'a, Main: Render>(title: &'a str, main: Main) {
+ Layout<Main: Render>(title: String, main: Main) {
@markup::doctype()
html {
head {
- title { @title }
+ title { @title " - Jellything" }
link[rel="stylesheet", href="/assets/style.css"];
}
body {
- header { "Grain" }
+ nav {
+ h1 { "Jellything" }
+
+ }
#main { @main }
- footer { span { "jellything" } }
}
}
}
diff --git a/src/frontend/pages/mod.rs b/src/frontend/pages/mod.rs
index 08e0e77..6e7bd87 100644
--- a/src/frontend/pages/mod.rs
+++ b/src/frontend/pages/mod.rs
@@ -1,7 +1,17 @@
-use actix_web::{body::BoxBody, http::StatusCode, HttpResponseBuilder, Responder};
+use std::{error::Error, fmt::Display};
+
+use actix_web::{
+ body::BoxBody,
+ http::{
+ header::{HeaderName, HeaderValue},
+ StatusCode,
+ },
+ HttpResponseBuilder, Responder,
+};
pub mod home;
pub mod layout;
+pub mod node;
struct HtmlTemplate<T>(pub T);
@@ -17,3 +27,52 @@ impl<T: markup::Render> Responder for HtmlTemplate<T> {
.respond_to(req)
}
}
+
+pub struct ContentType<T>(pub &'static str, pub T);
+
+impl<T: Responder> Responder for ContentType<T> {
+ type Body = T::Body;
+
+ fn respond_to(self, req: &actix_web::HttpRequest) -> actix_web::HttpResponse<Self::Body> {
+ let mut r = self.1.respond_to(req);
+ r.headers_mut().insert(
+ HeaderName::from_static("content-type"),
+ HeaderValue::from_static(self.0),
+ );
+ r
+ }
+}
+
+pub type MyResult<T> = actix_web::Result<T, MyError>;
+
+#[derive(Debug)]
+pub struct MyError(anyhow::Error);
+
+impl Responder for MyError {
+ type Body = BoxBody;
+ fn respond_to(self, req: &actix_web::HttpRequest) -> actix_web::HttpResponse<Self::Body> {
+ HttpResponseBuilder::new(StatusCode::BAD_REQUEST)
+ .body(format!("error: {}", self.0))
+ .respond_to(req)
+ }
+}
+impl actix_web::error::ResponseError for MyError {
+ fn status_code(&self) -> StatusCode {
+ StatusCode::BAD_REQUEST
+ }
+}
+impl Display for MyError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.0.fmt(f)
+ }
+}
+impl From<anyhow::Error> for MyError {
+ fn from(err: anyhow::Error) -> MyError {
+ MyError(err)
+ }
+}
+impl From<std::fmt::Error> for MyError {
+ fn from(err: std::fmt::Error) -> MyError {
+ MyError(anyhow::anyhow!("{err}"))
+ }
+}
diff --git a/src/frontend/pages/node.rs b/src/frontend/pages/node.rs
new file mode 100644
index 0000000..c3f7791
--- /dev/null
+++ b/src/frontend/pages/node.rs
@@ -0,0 +1,46 @@
+use super::layout::Layout;
+use crate::{
+ frontend::pages::{HtmlTemplate, MyError, MyResult},
+ library::{LibDirectory, LibItem, LibNode},
+ AppState,
+};
+use actix_web::{get, web, Responder};
+use actix_web_lab::respond::Html;
+use log::debug;
+use markup::Render;
+use std::{ops::Deref, sync::Arc};
+
+#[get("/library/{path:.*}")]
+pub async fn page_library_node(
+ state: web::Data<AppState>,
+ params: web::Path<(String,)>,
+) -> MyResult<impl Responder> {
+ debug!("request: {:?}", params.0);
+ let node = state.library.nested(&params.0)?;
+ let mut out = String::new();
+ match node.deref() {
+ LibNode::Directory(dir) => Layout {
+ title: format!(
+ "{} - Library",
+ dir.path.file_name().unwrap().to_str().unwrap()
+ ),
+ main: Directory { dir: dir.clone() },
+ }
+ .render(&mut out)?,
+ LibNode::Item(item) => Layout {
+ title: "".to_string(),
+ main: Item { item: item.clone() },
+ }
+ .render(&mut out)?,
+ };
+ Ok(Html(out))
+}
+
+markup::define! {
+ Directory(dir: Arc<LibDirectory>) {
+ h1 { @dir.data.name }
+ }
+ Item(item: Arc<LibItem>) {
+ h1 { "thats an item" }
+ }
+}
diff --git a/src/frontend/style/layout.css b/src/frontend/style/layout.css
new file mode 100644
index 0000000..d6196b0
--- /dev/null
+++ b/src/frontend/style/layout.css
@@ -0,0 +1,35 @@
+@import url("https://s.metamuffin.org/static/font-ubuntu/include.css");
+
+* {
+ color: white;
+ font-family: "Ubuntu", sans-serif;
+ font-weight: 300;
+ margin: 0px;
+ padding: 0px;
+}
+
+body {
+ background-color: #1a1a1a;
+}
+
+nav {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ padding: 1em;
+ width: 100vw;
+ height: 2em;
+ background-color: #41414144;
+}
+
+nav h1 {
+ margin: 0px;
+ font-size: 1.5em;
+}
+
+#main {
+ margin-top: 5em;
+ padding: 1em;
+ padding-left: 3em;
+ padding-right: 3em;
+}
diff --git a/src/frontend/style/master.css b/src/frontend/style/master.css
deleted file mode 100644
index e69de29..0000000
--- a/src/frontend/style/master.css
+++ /dev/null
diff --git a/src/frontend/style/mod.rs b/src/frontend/style/mod.rs
index 180fe0e..9d0729e 100644
--- a/src/frontend/style/mod.rs
+++ b/src/frontend/style/mod.rs
@@ -1,2 +1,2 @@
-pub const CSS_BUNDLE: &'static str = include_str!("master.css");
+pub const CSS_BUNDLE: &'static str = include_str!("layout.css");
diff --git a/src/library.rs b/src/library.rs
index 576ed77..e4c7fe6 100644
--- a/src/library.rs
+++ b/src/library.rs
@@ -1,23 +1,96 @@
-use anyhow::Ok;
+use std::{fs::File, path::PathBuf, str::FromStr, sync::Arc};
+
+use anyhow::{bail, Context, Ok};
+use chashmap::CHashMap;
+use serde::{Deserialize, Serialize};
pub struct Library {
- path: String,
- tree: LibNode,
+ path: PathBuf,
+ cache: CHashMap<String, LibNode>, // TODO
}
+#[derive(Debug, Clone)]
pub enum LibNode {
- Directory(LibDirectory),
- Item(LibItem),
+ Directory(Arc<LibDirectory>),
+ Item(Arc<LibItem>),
+}
+
+#[derive(Debug, Clone)]
+pub struct LibDirectory {
+ pub path: PathBuf,
+ pub child_nodes: Vec<PathBuf>,
+ pub data: LibDirectoryData,
+}
+
+#[derive(Debug, Clone)]
+pub struct LibItem {
+ pub data: LibItemData,
+}
+
+#[derive(Debug, Clone, Deserialize, Serialize)]
+pub struct LibDirectoryData {
+ pub name: String,
}
-pub struct LibDirectory {}
-pub struct LibItem {}
+#[derive(Debug, Clone, Deserialize, Serialize)]
+pub struct LibItemData {}
impl Library {
pub fn open(path: &str) -> anyhow::Result<Self> {
Ok(Self {
- path: path.to_string(),
- tree: ,
+ path: PathBuf::from_str(path).unwrap(),
+ cache: CHashMap::new(),
})
}
+ pub fn root(&self) -> anyhow::Result<Arc<LibNode>> {
+ LibNode::from_path(self.path.clone())
+ }
+ pub fn nested(&self, path: &str) -> anyhow::Result<Arc<LibNode>> {
+ let mut n = self.root()?;
+ if path == "" {
+ return Ok(n);
+ }
+ for seg in path.split("/") {
+ n = n.get_directory()?.get_child(seg)?
+ }
+ Ok(n)
+ }
+}
+impl LibDirectory {
+ pub fn get_child(&self, p: &str) -> anyhow::Result<Arc<LibNode>> {
+ if p.contains("..") || p.starts_with("/") {
+ bail!("no! dont do that.")
+ }
+ let path = self.path.join(p);
+ // if !path.exists() {bail!("does not exist");}
+ LibNode::from_path(path)
+ }
+}
+impl LibNode {
+ pub fn get_directory(&self) -> anyhow::Result<&LibDirectory> {
+ match self {
+ LibNode::Directory(d) => Ok(d),
+ LibNode::Item(_) => bail!("not a directory"),
+ }
+ }
+ pub fn from_path(path: PathBuf) -> anyhow::Result<Arc<LibNode>> {
+ if path.is_dir() {
+ let mpath = path.join("directory.json");
+ let data: LibDirectoryData =
+ serde_json::from_reader(File::open(mpath).context("metadata missing")?)?;
+ let child_nodes = path.read_dir()?.map(|e| e.unwrap().path()).collect();
+ Ok(LibNode::Directory(Arc::new(LibDirectory {
+ path,
+ child_nodes,
+ data,
+ }))
+ .into())
+ } else if path.is_file() {
+ let mpath = path.clone().with_extension(".metadata.json");
+ let data: LibItemData = serde_json::from_reader(File::open(mpath)?)?;
+ Ok(LibNode::Item(Arc::new(LibItem { data })).into())
+ } else {
+ bail!("did somebody really put a fifo or socket in the library?!")
+ }
+ }
}
diff --git a/src/main.rs b/src/main.rs
index f400920..39a4a5a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,7 +1,12 @@
-use crate::frontend::style::CSS_BUNDLE;
+#![feature(box_syntax)]
+
+use std::fs::read_to_string;
+
+use crate::frontend::{pages::ContentType, style::CSS_BUNDLE};
use actix_web::{get, web, App, HttpServer, Responder};
use database::Database;
-use frontend::pages::home::page_home;
+use frontend::pages::{home::page_home, node::page_library_node};
+use library::Library;
pub mod database;
pub mod frontend;
@@ -9,30 +14,33 @@ pub mod library;
#[get("/assets/style.css")]
async fn assets_style() -> impl Responder {
- CSS_BUNDLE
-}
-
-#[get("/{name}")]
-async fn hello(name: web::Path<String>) -> impl Responder {
- format!("Hello {}!", &name)
+ // ContentType("text/css", CSS_BUNDLE)
+ ContentType(
+ "text/css",
+ read_to_string("src/frontend/style/layout.css").unwrap(),
+ )
}
pub struct AppState {
pub database: Database,
+ pub library: Library,
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
env_logger::init_from_env("LOG");
let db_path = std::env::var("DB_PATH").unwrap_or("data/db".to_string());
+ let lib_path = std::env::var("LIB_PATH").unwrap_or("data/library".to_string());
let state = web::Data::new(AppState {
- database: Database::open(&db_path).unwrap().into(),
+ library: Library::open(&lib_path).unwrap(),
+ database: Database::open(&db_path).unwrap(),
});
HttpServer::new(move || {
App::new()
.app_data(state.clone())
.service(page_home)
- .service(hello)
+ .service(assets_style)
+ .service(page_library_node)
})
.bind(("127.0.0.1", 8080))?
.run()