chore(refactor): add pre-commit rules and refactor with clippy

This commit is contained in:
thek4n 2026-02-21 12:46:32 +03:00
parent b277c95ba8
commit fe9d32ec19
3 changed files with 60 additions and 38 deletions

View File

@ -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

22
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,22 @@
# Contributing Guide
## <a name="commit-message-format"></a> Commit message format
We use [this](https://www.conventionalcommits.org/en/v1.0.0/) standard
```gitcommit
<type>(<scope>): <subject>
<BLANK LINE>
[optional body]
<BLANK LINE>
[optional footer]
```
## <a name="pre-commit"></a> 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.

View File

@ -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<SocketAddr> {
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#"<div style="margin-bottom: 20px;">
<a href="{}" style="display: inline-flex; align-items: center; color: #90a4ae; text-decoration: none; font-size: 0.95em; transition: color 0.2s;"
<a href="{back_link}" style="display: inline-flex; align-items: center; color: #90a4ae; text-decoration: none; font-size: 0.95em; transition: color 0.2s;"
onmouseover="this.style.color='#ffffff'" onmouseout="this.style.color='#90a4ae'">
<span style="margin-right: 8px; font-size: 1.2em;"></span> Назад
</a>
</div>"#,
back_link
</div>"#
);
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("<ul>");
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#"<li>
<a href="{}" class="back-link">📁 ..</a>
</li>"#,
parent_link
));
<a href="{parent_link}" class="back-link">📁 ..</a>
</li>"#
);
}
for (name, link, is_dir) in files {
let icon = if is_dir { "📁" } else { "📄" };
list_html.push_str(&format!(
let _ = write!(
list_html,
r#"<li>
<a href="/{}" class="file-link">
<span class="icon">{}</span>
@ -318,7 +320,7 @@ async fn render_directory_index(
link.trim_start_matches('/'),
icon,
name
));
);
}
list_html.push_str("</ul>");
@ -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: &
<span class="code-lang">Mermaid Diagram</span>
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
</div>
<div class="mermaid" style="background: transparent; padding: 20px; text-align: center;">{}</div>
</div>"#,
escaped_code
<div class="mermaid" style="background: transparent; padding: 20px; text-align: center;">{escaped_code}</div>
</div>"#
);
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#"<div class="code-block-wrapper">
<div class="code-header">
<span class="code-lang">{}</span>
<span class="code-lang">{lang_escaped}</span>
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
</div>
<pre style="margin: 0; border-radius: 0 0 6px 6px;"><code>{}</code></pre>
</div>"#,
lang_escaped, highlighted_html
<pre style="margin: 0; border-radius: 0 0 6px 6px;"><code>{highlighted_html}</code></pre>
</div>"#
);
processed_events.push(Event::Html(code_container.into()));
}