chore(refactor): add pre-commit rules and refactor with clippy
This commit is contained in:
parent
b277c95ba8
commit
fe9d32ec19
@ -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
22
CONTRIBUTING.md
Normal 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.
|
||||
72
src/main.rs
72
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<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()));
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user