feat: Forgot to commit lol
Now it's too much stuff and it's impossible to discern what I did
This commit is contained in:
parent
8ee77d392a
commit
6983603a77
|
@ -175,6 +175,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"askama",
|
"askama",
|
||||||
"axum",
|
"axum",
|
||||||
|
"glob",
|
||||||
"serde",
|
"serde",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
|
@ -240,6 +241,12 @@ version = "0.31.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64"
|
checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.5"
|
version = "0.14.5"
|
||||||
|
|
|
@ -6,6 +6,7 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
askama = "0.12.1"
|
askama = "0.12.1"
|
||||||
axum = "0.7.5"
|
axum = "0.7.5"
|
||||||
|
glob = "0.3.1"
|
||||||
serde = { version = "1.0.210", features = ['derive'] }
|
serde = { version = "1.0.210", features = ['derive'] }
|
||||||
tokio = "1.40.0"
|
tokio = "1.40.0"
|
||||||
toml = "0.8.19"
|
toml = "0.8.19"
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Config {
|
||||||
|
pub settings: Settings,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Settings {
|
||||||
|
pub base_directory: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_config() -> Result<Config, Box<dyn std::error::Error>> {
|
||||||
|
let mut file = File::open("config.toml")?;
|
||||||
|
let mut contents = String::new();
|
||||||
|
file.read_to_string(&mut contents)?;
|
||||||
|
let config: Config = toml::from_str(&contents)?;
|
||||||
|
Ok(config)
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
use crate::templates::HtmlTemplate;
|
||||||
|
use crate::templates::ListTemplate;
|
||||||
|
use crate::utils::find_files;
|
||||||
|
use axum::response::IntoResponse;
|
||||||
|
|
||||||
|
pub async fn list_files(base_path: String) -> impl IntoResponse {
|
||||||
|
let template = ListTemplate {
|
||||||
|
files: find_files(&base_path).unwrap(),
|
||||||
|
};
|
||||||
|
HtmlTemplate(template)
|
||||||
|
}
|
103
src/main.rs
103
src/main.rs
|
@ -1,29 +1,13 @@
|
||||||
use std::{
|
mod config;
|
||||||
fs::{self, File},
|
mod handlers;
|
||||||
io::Read,
|
mod templates;
|
||||||
path::Path,
|
mod utils;
|
||||||
};
|
|
||||||
|
|
||||||
use axum::{
|
use crate::config::read_config;
|
||||||
http::StatusCode,
|
use crate::handlers::list_files;
|
||||||
response::{Html, IntoResponse},
|
use axum::routing::get;
|
||||||
routing::get,
|
use axum::Router;
|
||||||
Router,
|
use tokio::net::TcpListener;
|
||||||
};
|
|
||||||
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
use askama::Template;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct Config {
|
|
||||||
settings: Settings,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct Settings {
|
|
||||||
base_directory: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
@ -34,73 +18,8 @@ async fn main() {
|
||||||
get(move || list_files(config.settings.base_directory.clone())),
|
get(move || list_files(config.settings.base_directory.clone())),
|
||||||
);
|
);
|
||||||
|
|
||||||
let listener = tokio::net::TcpListener::bind("127.0.0.1:3004")
|
let listener = TcpListener::bind("127.0.0.1:3004").await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
println!("Listenening on {}", listener.local_addr().unwrap());
|
println!("Listening on {}", listener.local_addr().unwrap());
|
||||||
axum::serve(listener, app).await.unwrap();
|
axum::serve(listener, app).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_config() -> Result<Config, Box<dyn std::error::Error>> {
|
|
||||||
let mut file = File::open("config.toml")?;
|
|
||||||
let mut contents = String::new();
|
|
||||||
file.read_to_string(&mut contents)?;
|
|
||||||
let config: Config = toml::from_str(&contents)?;
|
|
||||||
Ok(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Template)]
|
|
||||||
#[template(path = "list.html")]
|
|
||||||
struct ListTemplate {
|
|
||||||
paths: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct HtmlTemplate<T>(T);
|
|
||||||
|
|
||||||
impl<T> IntoResponse for HtmlTemplate<T>
|
|
||||||
where
|
|
||||||
T: Template,
|
|
||||||
{
|
|
||||||
fn into_response(self) -> axum::response::Response {
|
|
||||||
match self.0.render() {
|
|
||||||
Ok(html) => Html(html).into_response(),
|
|
||||||
Err(err) => (
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
format!("Failed to render template. Error: {err}"),
|
|
||||||
)
|
|
||||||
.into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn list_files(base_path: String) -> impl IntoResponse {
|
|
||||||
let template = ListTemplate {
|
|
||||||
paths: find_ora_files(&base_path).unwrap(),
|
|
||||||
};
|
|
||||||
HtmlTemplate(template)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_ora_files(dir: &str) -> Result<Vec<String>, std::io::Error> {
|
|
||||||
let mut ora_files = Vec::new();
|
|
||||||
let base_path = Path::new(dir);
|
|
||||||
|
|
||||||
if base_path.is_dir() {
|
|
||||||
for entry in fs::read_dir(base_path)? {
|
|
||||||
let entry = entry?;
|
|
||||||
let path = entry.path();
|
|
||||||
|
|
||||||
if path.is_dir() {
|
|
||||||
if let Ok(mut subdir_files) = find_ora_files(path.to_str().unwrap()) {
|
|
||||||
ora_files.append(&mut subdir_files);
|
|
||||||
}
|
|
||||||
} else if let Some(extension) = path.extension() {
|
|
||||||
if extension == "ora" {
|
|
||||||
ora_files.push(path.to_string_lossy().to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(ora_files)
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
use askama::Template;
|
||||||
|
use axum::http::StatusCode;
|
||||||
|
use axum::response::{Html, IntoResponse};
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "list.html")]
|
||||||
|
pub struct ListTemplate {
|
||||||
|
pub files: Vec<crate::utils::FileEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HtmlTemplate<T>(pub T);
|
||||||
|
|
||||||
|
impl<T> IntoResponse for HtmlTemplate<T>
|
||||||
|
where
|
||||||
|
T: Template,
|
||||||
|
{
|
||||||
|
fn into_response(self) -> axum::response::Response {
|
||||||
|
match self.0.render() {
|
||||||
|
Ok(html) => Html(html).into_response(),
|
||||||
|
Err(err) => (
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
format!("Failed to render template. Error: {err}"),
|
||||||
|
)
|
||||||
|
.into_response(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
use std::fs::{self, File};
|
||||||
|
use std::io::Read;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use glob::glob;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct TagFile {
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
basedir: String,
|
||||||
|
|
||||||
|
#[serde(flatten)]
|
||||||
|
entries: HashMap<String, FileTags>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct FileTags {
|
||||||
|
tags: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct FileEntry {
|
||||||
|
pub path: String,
|
||||||
|
pub tags: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_tag_file(path: &PathBuf) -> Result<TagFile, Box<dyn std::error::Error>> {
|
||||||
|
let mut file = File::open(path)?;
|
||||||
|
let mut contents = String::new();
|
||||||
|
file.read_to_string(&mut contents)?;
|
||||||
|
|
||||||
|
Ok(TagFile {
|
||||||
|
basedir: path.parent().unwrap().to_str().unwrap().to_string(),
|
||||||
|
entries: toml::from_str(&contents)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_files(pattern: &str, base_path: &Path) -> Vec<String> {
|
||||||
|
let pattern = base_path.join(pattern).to_str().unwrap().to_string();
|
||||||
|
let mut matched_files = Vec::new();
|
||||||
|
for entry in glob(&pattern).expect("Failed to read glob pattern") {
|
||||||
|
if let Ok(path) = entry {
|
||||||
|
if path.is_file() {
|
||||||
|
matched_files.push(path.to_string_lossy().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
matched_files
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_tag_files(dir: &str) -> Result<Vec<TagFile>, Box<dyn std::error::Error>> {
|
||||||
|
let base_path = Path::new(dir);
|
||||||
|
let mut tag_files = Vec::new();
|
||||||
|
|
||||||
|
if base_path.is_dir() {
|
||||||
|
for entry in fs::read_dir(base_path)? {
|
||||||
|
let entry = entry?;
|
||||||
|
let path = entry.path();
|
||||||
|
|
||||||
|
if path.is_dir() {
|
||||||
|
if let Ok(mut subdir_files) = find_tag_files(path.to_str().unwrap()) {
|
||||||
|
tag_files.append(&mut subdir_files);
|
||||||
|
}
|
||||||
|
} else if let Some(filename) = path.file_name() {
|
||||||
|
if filename != ".tags.toml" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tag_file = load_tag_file(&path).unwrap();
|
||||||
|
|
||||||
|
tag_files.push(tag_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(tag_files)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_files(dir: &str) -> Result<Vec<FileEntry>, Box<dyn std::error::Error>> {
|
||||||
|
let mut files = HashMap::new();
|
||||||
|
|
||||||
|
for tag_file in find_tag_files(dir)? {
|
||||||
|
for (key, value) in tag_file.entries.into_iter() {
|
||||||
|
// Create a copy of the list (append needs it to be mutable) and
|
||||||
|
// ignore .tags.toml files
|
||||||
|
let matched_files: Vec<String> = match_files(&key, Path::new(&tag_file.basedir))
|
||||||
|
.iter_mut()
|
||||||
|
.filter(|x| !x.ends_with(".tags.toml"))
|
||||||
|
.map(|x| x.to_owned())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for matched_file in matched_files {
|
||||||
|
files
|
||||||
|
.entry(matched_file.clone())
|
||||||
|
.and_modify(|file_entry: &mut FileEntry| {
|
||||||
|
file_entry.tags.extend(value.tags.to_owned());
|
||||||
|
})
|
||||||
|
.or_insert_with(|| FileEntry {
|
||||||
|
path: matched_file,
|
||||||
|
tags: value.tags.to_owned(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(files.values().cloned().collect())
|
||||||
|
}
|
|
@ -1,12 +1,58 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
<style>
|
||||||
|
ul, p, h1, h2, h3, h4 {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
background-color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
font-family: mono;
|
||||||
|
margin: auto;
|
||||||
|
width: 80vw;
|
||||||
|
padding: 40px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Drawing list</h1>
|
<h1 style="margin-bottom: 40px;">Drawing list</h1>
|
||||||
<ul>
|
<ul style="display: flex; flex-direction: column; gap: 20px;">
|
||||||
{% for path in paths -%}
|
{% for file in files -%}
|
||||||
<li>{{ path }}</li>
|
<li>
|
||||||
|
<p>{{ file.path }}<p>
|
||||||
|
<ul
|
||||||
|
style="
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 5px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{% for tag in file.tags -%}
|
||||||
|
<li
|
||||||
|
style="
|
||||||
|
list-style: none;
|
||||||
|
background-color: #555;
|
||||||
|
padding: 2px 7px;
|
||||||
|
margin: 0px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ tag }}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</body>
|
</body>
|
||||||
|
|
Loading…
Reference in New Issue