100줄의 Rust로 정적 사이트 생성기 만들기
26024 단어 webdevrustprogrammingtutorial
원래 내 블로그에 게시됨: https://kerkour.com/rust-static-site-generator
개념적으로 정적 사이트 생성기는 간단합니다.
일부 파일을 입력으로 사용하고, 종종 마크다운하고, 렌더링하고, 미리 정의된 템플릿과 병합하고, 모든 것을 원시 HTML 파일로 출력합니다. 간단하고 기본입니다.
우리는 파일이 변경될 때 웹사이트를 미리 볼 수 있도록 웹 서버를 내장할 것입니다.
다음은 사용할
Cargo.toml
파일입니다.Cargo.toml
[package]
name = "rust_static_site_generator"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
pulldown-cmark = "0.8.0"
hotwatch = "0.4"
tokio = { version = "1", features = ["full"] }
anyhow = "1"
walkdir = "2"
axum = "0.2"
tower-http = { version = "0.1", features = ["fs"] }
템플릿
템플릿의 경우
format!
매크로와 함께 일반 Rust 문자열을 사용합니다.템플릿.rs
pub const HEADER: &str = r#"<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
"#;
pub fn render_body(body: &str) -> String {
format!(
r#" <body>
<nav>
<a href="/">Home</a>
</nav>
<br />
{}
</body>"#,
body
)
}
pub const FOOTER: &str = r#"
</html>
"#;
변경 시 사이트 재구축
파일 변경 사항을 감지하기 위해 hotwatch , notify 위에 간단한 래퍼를 사용하여 몇 줄을 절약할 수 있습니다.
시작 시 웹사이트를 먼저 빌드한 다음
content
폴더에서 변경 사항이 감지될 때마다.main.rs
use axum::{http::StatusCode, service, Router};
use std::{convert::Infallible, fs, net::SocketAddr, path::Path, thread, time::Duration};
use tower_http::services::ServeDir;
mod templates;
const CONTENT_DIR: &str = "content";
const PUBLIC_DIR: &str = "public";
#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
rebuild_site(CONTENT_DIR, PUBLIC_DIR).expect("Rebuilding site");
tokio::task::spawn_blocking(move || {
println!("listenning for changes: {}", CONTENT_DIR);
let mut hotwatch = hotwatch::Hotwatch::new().expect("hotwatch failed to initialize!");
hotwatch
.watch(CONTENT_DIR, |_| {
println!("Rebuilding site");
rebuild_site(CONTENT_DIR, PUBLIC_DIR).expect("Rebuilding site");
})
.expect("failed to watch content folder!");
loop {
thread::sleep(Duration::from_secs(1));
}
});
// ...
}
우리는 잔인한 방법으로 웹 사이트를 구축합니다.
public
폴더.md
폴더의 모든 content
파일을 반복합니다public
에서 HTML 파일로 렌더링합니다content/blog/hello.md
-> public/blog/hello.html
에서 blog
하위 폴더가 보존됨)또한 정적 사이트의 인덱스에 추가하기 위해 생성된 모든 HTML 파일 목록을 유지합니다.
main.rs
fn rebuild_site(content_dir: &str, output_dir: &str) -> Result<(), anyhow::Error> {
let _ = fs::remove_dir_all(output_dir);
let markdown_files: Vec<String> = walkdir::WalkDir::new(content_dir)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.path().display().to_string().ends_with(".md"))
.map(|e| e.path().display().to_string())
.collect();
let mut html_files = Vec::with_capacity(markdown_files.len());
for file in &markdown_files {
let mut html = templates::HEADER.to_owned();
let markdown = fs::read_to_string(&file)?;
let parser = pulldown_cmark::Parser::new_ext(&markdown, pulldown_cmark::Options::all());
let mut body = String::new();
pulldown_cmark::html::push_html(&mut body, parser);
html.push_str(templates::render_body(&body).as_str());
html.push_str(templates::FOOTER);
let html_file = file
.replace(content_dir, output_dir)
.replace(".md", ".html");
let folder = Path::new(&html_file).parent().unwrap();
let _ = fs::create_dir_all(folder);
fs::write(&html_file, html)?;
html_files.push(html_file);
}
write_index(html_files, output_dir)?;
Ok(())
}
인덱스 생성
사이트의 모든 페이지를 구축한 후 방문자가 페이지를 탐색할 수 있도록 색인을 렌더링해야 합니다. 이를 위해 페이지 목록을 HTML 링크로 렌더링합니다.
main.rs
fn write_index(files: Vec<String>, output_dir: &str) -> Result<(), anyhow::Error> {
let mut html = templates::HEADER.to_owned();
let body = files
.into_iter()
.map(|file| {
let file = file.trim_start_matches(output_dir);
let title = file.trim_start_matches("/").trim_end_matches(".html");
format!(r#"<a href="{}">{}</a>"#, file, title)
})
.collect::<Vec<String>>()
.join("<br />\n");
html.push_str(templates::render_body(&body).as_str());
html.push_str(templates::FOOTER);
let index_path = Path::new(&output_dir).join("index.html");
fs::write(index_path, html)?;
Ok(())
}
웹 서버
마지막으로 콘텐츠를 작성하고 편집할 때 페이지를 미리 보려면 웹 서버가 필요합니다. axum의 새로운 tokio's team 프레임워크를 선택한 이유는 API가 매우 훌륭하고 직관적이며 hyper 위에 구축되어 매우 안정적이기 때문입니다.
main.rs
#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
// ...
let app = Router::new().nest(
"/",
service::get(ServeDir::new(PUBLIC_DIR)).handle_error(|error: std::io::Error| {
Ok::<_, Infallible>((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Unhandled internal error: {}", error),
))
}),
);
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
println!("serving site on {}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await?;
Ok(())
}
그리고 마지막 누락된 부분: Markdown 페이지!
내용/hello-world.md
# Hellow world
Cool
다음을 실행하여 정적 사이트를 로컬로 제공할 수 있습니다.
$ cargo run
그런 다음 방문하십시오 http://localhost:8080
귀엽죠?
더 나아가
물론, 우리의 작은 아기는 완벽하지 않으며 신뢰할 수 있는 정적 사이트 생성기로 바꾸려면 더 많은 작업이 필요하지만 후렴구를 알고 있습니다: 독자를 위한 연습으로 남겨둡니다 😉
sitemap.xml
(index.html
처럼 쉽게) 코드는 GitHub에 있습니다.
평소와 같이 GitHub에서 코드를 찾을 수 있습니다: github.com/skerkour/kerkour.com (저장소에 별표를 표시하는 것을 잊지 마세요 🙏).
Reference
이 문제에 관하여(100줄의 Rust로 정적 사이트 생성기 만들기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/sylvainkerkour/building-a-static-site-generator-in-100-lines-of-rust-10l4텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)