diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 696987a..779a10b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,14 +27,14 @@ repos: - id: clippy name: clippy language: system - entry: cargo clippy + entry: cargo clippy -- -W clippy::all -W clippy::pedantic pass_filenames: false always_run: true - id: cargo-fmt name: cargo fmt language: system - entry: cargo fmt + entry: cargo fmt --check pass_filenames: false always_run: true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..1566389 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,22 @@ +# Contributing Guide + +## Commit message format +We use [this](https://www.conventionalcommits.org/en/v1.0.0/) standard +```gitcommit +(): + +[optional body] + +[optional footer] +``` + +## Setting Up Pre-Commit Hooks +We use pre-commit to automatically enforce code quality checks before commits: +```sh +python3 -m venv venv +. ./venv/bin/activate +pip install pre-commit +pre-commit install +pre-commit install --hook-type commit-msg +``` +Hooks will now run automatically on every commit. diff --git a/src/main.rs b/src/main.rs index 67f0482..545bd05 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ use futures::StreamExt; use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; use pulldown_cmark::{CodeBlockKind, Event, Options, Tag, html}; use std::convert::Infallible; +use std::fmt::Write; use std::net::{SocketAddr, ToSocketAddrs}; use std::path::PathBuf; use std::sync::Arc; @@ -97,14 +98,14 @@ async fn main() { let addr = resolve_addr(&args.host, args.port).unwrap(); - println!("Сервер запущен на http://{}", addr); + println!("Сервер запущен на http://{addr}"); let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); axum::serve(listener, app).await.unwrap(); } fn resolve_addr(host: &str, port: u16) -> io::Result { - let addr_str = format!("{}:{}", host, port); + let addr_str = format!("{host}:{port}"); addr_str .to_socket_addrs()? @@ -178,25 +179,26 @@ async fn serve_file( requested_path.push(&full_path); // Безопасность путей - let safe_path = match fs::canonicalize(&requested_path).await { - Ok(p) => p, - Err(_) => return Err(StatusCode::NOT_FOUND), + let Ok(safe_path) = fs::canonicalize(&requested_path).await else { + return Err(StatusCode::NOT_FOUND); }; - let base_dir = match fs::canonicalize(&state.root).await { - Ok(p) => p, - Err(_) => return Err(StatusCode::INTERNAL_SERVER_ERROR), + let Ok(base_dir) = fs::canonicalize(&state.root).await else { + return Err(StatusCode::INTERNAL_SERVER_ERROR); }; if !safe_path.starts_with(&base_dir) { - eprintln!("Попытка выхода за пределы директории: {:?}", safe_path); + eprintln!( + "Попытка выхода за пределы директории: {}", + safe_path.display() + ); return Err(StatusCode::FORBIDDEN); } let metadata = match fs::metadata(&safe_path).await { Ok(m) => m, Err(e) => { - eprintln!("Ошибка получения метаданных: {}", e); + eprintln!("Ошибка получения метаданных: {e}"); return Err(StatusCode::INTERNAL_SERVER_ERROR); } }; @@ -208,7 +210,7 @@ async fn serve_file( let content = match fs::read_to_string(&safe_path).await { Ok(c) => c, Err(e) => { - eprintln!("Ошибка чтения: {}", e); + eprintln!("Ошибка чтения: {e}"); return Err(StatusCode::INTERNAL_SERVER_ERROR); } }; @@ -219,7 +221,7 @@ async fn serve_file( if parent.is_empty() { "/".to_string() } else { - format!("/{}", parent) + format!("/{parent}") } } else { "/".to_string() @@ -227,12 +229,11 @@ async fn serve_file( let back_button_html = format!( r#""#, - back_link + "# ); let html_content = markdown_to_html(&content, &state.syntax_set, &state.theme_set, &full_path); @@ -240,7 +241,7 @@ async fn serve_file( // Заполнение шаблона let final_html = TEMPLATE_FILE .replace("{{CONTENT}}", &html_content) - .replace("{{SSE_URL}}", &format!("/events/{}", full_path)) + .replace("{{SSE_URL}}", &format!("/events/{full_path}")) .replace("{{BACK_BUTTON}}", &back_button_html); Ok(Html(final_html)) @@ -253,7 +254,7 @@ async fn render_directory_index( let mut entries = match fs::read_dir(dir_path).await { Ok(list) => list, Err(e) => { - eprintln!("Ошибка чтения директории: {}", e); + eprintln!("Ошибка чтения директории: {e}"); return Err(StatusCode::FORBIDDEN); } }; @@ -291,24 +292,25 @@ async fn render_directory_index( let mut list_html = String::from("
    "); if !request_path.is_empty() && request_path != "files" { - let parent_path = request_path.rsplit_once('/').map(|(p, _)| p).unwrap_or(""); + let parent_path = request_path.rsplit_once('/').map_or("", |(p, _)| p); let parent_link = if parent_path.is_empty() { "/".to_string() } else { - format!("/{}", parent_path) + format!("/{parent_path}") }; - list_html.push_str(&format!( + let _ = write!( + list_html, r#"
  • - 📁 .. -
  • "#, - parent_link - )); + 📁 .. + "# + ); } for (name, link, is_dir) in files { let icon = if is_dir { "📁" } else { "📄" }; - list_html.push_str(&format!( + let _ = write!( + list_html, r#"
  • {} @@ -318,7 +320,7 @@ async fn render_directory_index( link.trim_start_matches('/'), icon, name - )); + ); } list_html.push_str("
"); @@ -355,7 +357,7 @@ async fn run_file_watcher(state: AppState) { let watch_path = PathBuf::from("./notes"); if let Err(e) = watcher.watch(&watch_path, RecursiveMode::Recursive) { - eprintln!("Ошибка настройки watcher: {}", e); + eprintln!("Ошибка настройки watcher: {e}"); return; } @@ -406,9 +408,8 @@ fn markdown_to_html(markdown: &str, ss: &SyntaxSet, ts: &ThemeSet, _file_path: & Mermaid Diagram -
{}
- "#, - escaped_code +
{escaped_code}
+ "# ); processed_events.push(Event::Html(mermaid_html.into())); } else { @@ -420,7 +421,7 @@ fn markdown_to_html(markdown: &str, ss: &SyntaxSet, ts: &ThemeSet, _file_path: & let mut h = HighlightLines::new(syntax, theme); let mut result_html = String::new(); for line in current_code.lines() { - let line_with_newline = format!("{}\n", line); + let line_with_newline = format!("{line}\n"); match h.highlight_line(&line_with_newline, ss) { Ok(regions) => { let html_line = styled_line_to_highlighted_html( @@ -431,7 +432,7 @@ fn markdown_to_html(markdown: &str, ss: &SyntaxSet, ts: &ThemeSet, _file_path: & result_html.push_str(&html_line); } Err(_) => { - result_html.push_str(&escape_html(&line_with_newline)) + result_html.push_str(&escape_html(&line_with_newline)); } } } @@ -446,12 +447,11 @@ fn markdown_to_html(markdown: &str, ss: &SyntaxSet, ts: &ThemeSet, _file_path: & let code_container = format!( r#"
- {} + {lang_escaped}
-
{}
-
"#, - lang_escaped, highlighted_html +
{highlighted_html}
+ "# ); processed_events.push(Event::Html(code_container.into())); }