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
|
- id: clippy
|
||||||
name: clippy
|
name: clippy
|
||||||
language: system
|
language: system
|
||||||
entry: cargo clippy
|
entry: cargo clippy -- -W clippy::all -W clippy::pedantic
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
always_run: true
|
always_run: true
|
||||||
|
|
||||||
- id: cargo-fmt
|
- id: cargo-fmt
|
||||||
name: cargo fmt
|
name: cargo fmt
|
||||||
language: system
|
language: system
|
||||||
entry: cargo fmt
|
entry: cargo fmt --check
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
always_run: true
|
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 notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
|
||||||
use pulldown_cmark::{CodeBlockKind, Event, Options, Tag, html};
|
use pulldown_cmark::{CodeBlockKind, Event, Options, Tag, html};
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
|
use std::fmt::Write;
|
||||||
use std::net::{SocketAddr, ToSocketAddrs};
|
use std::net::{SocketAddr, ToSocketAddrs};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -97,14 +98,14 @@ async fn main() {
|
|||||||
|
|
||||||
let addr = resolve_addr(&args.host, args.port).unwrap();
|
let addr = resolve_addr(&args.host, args.port).unwrap();
|
||||||
|
|
||||||
println!("Сервер запущен на http://{}", addr);
|
println!("Сервер запущен на http://{addr}");
|
||||||
|
|
||||||
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
||||||
axum::serve(listener, app).await.unwrap();
|
axum::serve(listener, app).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_addr(host: &str, port: u16) -> io::Result<SocketAddr> {
|
fn resolve_addr(host: &str, port: u16) -> io::Result<SocketAddr> {
|
||||||
let addr_str = format!("{}:{}", host, port);
|
let addr_str = format!("{host}:{port}");
|
||||||
|
|
||||||
addr_str
|
addr_str
|
||||||
.to_socket_addrs()?
|
.to_socket_addrs()?
|
||||||
@ -178,25 +179,26 @@ async fn serve_file(
|
|||||||
requested_path.push(&full_path);
|
requested_path.push(&full_path);
|
||||||
|
|
||||||
// Безопасность путей
|
// Безопасность путей
|
||||||
let safe_path = match fs::canonicalize(&requested_path).await {
|
let Ok(safe_path) = fs::canonicalize(&requested_path).await else {
|
||||||
Ok(p) => p,
|
return Err(StatusCode::NOT_FOUND);
|
||||||
Err(_) => return Err(StatusCode::NOT_FOUND),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let base_dir = match fs::canonicalize(&state.root).await {
|
let Ok(base_dir) = fs::canonicalize(&state.root).await else {
|
||||||
Ok(p) => p,
|
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
||||||
Err(_) => return Err(StatusCode::INTERNAL_SERVER_ERROR),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if !safe_path.starts_with(&base_dir) {
|
if !safe_path.starts_with(&base_dir) {
|
||||||
eprintln!("Попытка выхода за пределы директории: {:?}", safe_path);
|
eprintln!(
|
||||||
|
"Попытка выхода за пределы директории: {}",
|
||||||
|
safe_path.display()
|
||||||
|
);
|
||||||
return Err(StatusCode::FORBIDDEN);
|
return Err(StatusCode::FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
let metadata = match fs::metadata(&safe_path).await {
|
let metadata = match fs::metadata(&safe_path).await {
|
||||||
Ok(m) => m,
|
Ok(m) => m,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Ошибка получения метаданных: {}", e);
|
eprintln!("Ошибка получения метаданных: {e}");
|
||||||
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -208,7 +210,7 @@ async fn serve_file(
|
|||||||
let content = match fs::read_to_string(&safe_path).await {
|
let content = match fs::read_to_string(&safe_path).await {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Ошибка чтения: {}", e);
|
eprintln!("Ошибка чтения: {e}");
|
||||||
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -219,7 +221,7 @@ async fn serve_file(
|
|||||||
if parent.is_empty() {
|
if parent.is_empty() {
|
||||||
"/".to_string()
|
"/".to_string()
|
||||||
} else {
|
} else {
|
||||||
format!("/{}", parent)
|
format!("/{parent}")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
"/".to_string()
|
"/".to_string()
|
||||||
@ -227,12 +229,11 @@ async fn serve_file(
|
|||||||
|
|
||||||
let back_button_html = format!(
|
let back_button_html = format!(
|
||||||
r#"<div style="margin-bottom: 20px;">
|
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'">
|
onmouseover="this.style.color='#ffffff'" onmouseout="this.style.color='#90a4ae'">
|
||||||
<span style="margin-right: 8px; font-size: 1.2em;">←</span> Назад
|
<span style="margin-right: 8px; font-size: 1.2em;">←</span> Назад
|
||||||
</a>
|
</a>
|
||||||
</div>"#,
|
</div>"#
|
||||||
back_link
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let html_content = markdown_to_html(&content, &state.syntax_set, &state.theme_set, &full_path);
|
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
|
let final_html = TEMPLATE_FILE
|
||||||
.replace("{{CONTENT}}", &html_content)
|
.replace("{{CONTENT}}", &html_content)
|
||||||
.replace("{{SSE_URL}}", &format!("/events/{}", full_path))
|
.replace("{{SSE_URL}}", &format!("/events/{full_path}"))
|
||||||
.replace("{{BACK_BUTTON}}", &back_button_html);
|
.replace("{{BACK_BUTTON}}", &back_button_html);
|
||||||
|
|
||||||
Ok(Html(final_html))
|
Ok(Html(final_html))
|
||||||
@ -253,7 +254,7 @@ async fn render_directory_index(
|
|||||||
let mut entries = match fs::read_dir(dir_path).await {
|
let mut entries = match fs::read_dir(dir_path).await {
|
||||||
Ok(list) => list,
|
Ok(list) => list,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Ошибка чтения директории: {}", e);
|
eprintln!("Ошибка чтения директории: {e}");
|
||||||
return Err(StatusCode::FORBIDDEN);
|
return Err(StatusCode::FORBIDDEN);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -291,24 +292,25 @@ async fn render_directory_index(
|
|||||||
let mut list_html = String::from("<ul>");
|
let mut list_html = String::from("<ul>");
|
||||||
|
|
||||||
if !request_path.is_empty() && request_path != "files" {
|
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() {
|
let parent_link = if parent_path.is_empty() {
|
||||||
"/".to_string()
|
"/".to_string()
|
||||||
} else {
|
} else {
|
||||||
format!("/{}", parent_path)
|
format!("/{parent_path}")
|
||||||
};
|
};
|
||||||
|
|
||||||
list_html.push_str(&format!(
|
let _ = write!(
|
||||||
|
list_html,
|
||||||
r#"<li>
|
r#"<li>
|
||||||
<a href="{}" class="back-link">📁 ..</a>
|
<a href="{parent_link}" class="back-link">📁 ..</a>
|
||||||
</li>"#,
|
</li>"#
|
||||||
parent_link
|
);
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (name, link, is_dir) in files {
|
for (name, link, is_dir) in files {
|
||||||
let icon = if is_dir { "📁" } else { "📄" };
|
let icon = if is_dir { "📁" } else { "📄" };
|
||||||
list_html.push_str(&format!(
|
let _ = write!(
|
||||||
|
list_html,
|
||||||
r#"<li>
|
r#"<li>
|
||||||
<a href="/{}" class="file-link">
|
<a href="/{}" class="file-link">
|
||||||
<span class="icon">{}</span>
|
<span class="icon">{}</span>
|
||||||
@ -318,7 +320,7 @@ async fn render_directory_index(
|
|||||||
link.trim_start_matches('/'),
|
link.trim_start_matches('/'),
|
||||||
icon,
|
icon,
|
||||||
name
|
name
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
list_html.push_str("</ul>");
|
list_html.push_str("</ul>");
|
||||||
|
|
||||||
@ -355,7 +357,7 @@ async fn run_file_watcher(state: AppState) {
|
|||||||
|
|
||||||
let watch_path = PathBuf::from("./notes");
|
let watch_path = PathBuf::from("./notes");
|
||||||
if let Err(e) = watcher.watch(&watch_path, RecursiveMode::Recursive) {
|
if let Err(e) = watcher.watch(&watch_path, RecursiveMode::Recursive) {
|
||||||
eprintln!("Ошибка настройки watcher: {}", e);
|
eprintln!("Ошибка настройки watcher: {e}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,9 +408,8 @@ fn markdown_to_html(markdown: &str, ss: &SyntaxSet, ts: &ThemeSet, _file_path: &
|
|||||||
<span class="code-lang">Mermaid Diagram</span>
|
<span class="code-lang">Mermaid Diagram</span>
|
||||||
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="mermaid" style="background: transparent; padding: 20px; text-align: center;">{}</div>
|
<div class="mermaid" style="background: transparent; padding: 20px; text-align: center;">{escaped_code}</div>
|
||||||
</div>"#,
|
</div>"#
|
||||||
escaped_code
|
|
||||||
);
|
);
|
||||||
processed_events.push(Event::Html(mermaid_html.into()));
|
processed_events.push(Event::Html(mermaid_html.into()));
|
||||||
} else {
|
} 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 h = HighlightLines::new(syntax, theme);
|
||||||
let mut result_html = String::new();
|
let mut result_html = String::new();
|
||||||
for line in current_code.lines() {
|
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) {
|
match h.highlight_line(&line_with_newline, ss) {
|
||||||
Ok(regions) => {
|
Ok(regions) => {
|
||||||
let html_line = styled_line_to_highlighted_html(
|
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);
|
result_html.push_str(&html_line);
|
||||||
}
|
}
|
||||||
Err(_) => {
|
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!(
|
let code_container = format!(
|
||||||
r#"<div class="code-block-wrapper">
|
r#"<div class="code-block-wrapper">
|
||||||
<div class="code-header">
|
<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>
|
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
||||||
</div>
|
</div>
|
||||||
<pre style="margin: 0; border-radius: 0 0 6px 6px;"><code>{}</code></pre>
|
<pre style="margin: 0; border-radius: 0 0 6px 6px;"><code>{highlighted_html}</code></pre>
|
||||||
</div>"#,
|
</div>"#
|
||||||
lang_escaped, highlighted_html
|
|
||||||
);
|
);
|
||||||
processed_events.push(Event::Html(code_container.into()));
|
processed_events.push(Event::Html(code_container.into()));
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user