Rust가 Markdown 파일의 변경 사항을 감지하고 HTML을 생성하도록 합니다.
나는 최근에 주로 텍스트인 간단한 HTML 파일을 만들어야 했습니다.
물론 HTML을 직접 작성하는 것은 번거롭기 때문에 Markdown을 작성하여 HTML로 변환해야겠다고 생각했습니다.
어쨌든 Rust에서 그 변환 도구를 만들 수 있을지 궁금했습니다. 그것은 나에게 좋은 학습 프로그래밍처럼 보였습니다.
Rust에서 Markdown to HTML 변환 도구를 만든 기록입니다.
pulldown-cmark를 사용하여 Markdown을 HTML로 변환
pulldown-cmark라는 Markdown을 HTML로 변환하기 위한 크레이트가 있습니다.
https://crates.io/crates/pulldown-cmark
변환 코드를 작성하는 데 사용하기로 결정했습니다.
pulldown-cmark를 사용하기 위해 Cargo.toml을 이렇게 설정했습니다.
기본적으로 바이너리도 빌드되는데 이번에는 pulldown-cmark를 라이브러리로 사용하고 싶어서 이렇게 설명했습니다.
pulldown-cmark = { version = "0.9.1", default-features = false }
변환을 위한 샘플 코드를 작성했습니다.
(pulldown-cmark 문서와 거의 동일한 내용입니다.)
use pulldown_cmark::{html, Options, Parser};
fn main() {
let markdown_input = "# Hello world
* 111
* 222
* 333
~~Strikethroughs~~ *bold*.
";
let mut options = Options::empty();
// Strikeouts are not part of the CommonMark standard and must be explicitly enabled.
options.insert(Options::ENABLE_STRIKETHROUGH);
let parser = Parser::new_ext(markdown_input, options);
let mut html_output = String::new();
html::push_html(&mut html_output, parser);
println!("{}", &html_output);
}
다음은 변환 결과입니다.
<h1>Hello world</h1>
<ul>
<li>111</li>
<li>222</li>
<li>333</li>
</ul>
<p><del>Strikethroughs</del> <em>bold</em>.</p>
마크다운 파일 읽기 및 HTML 파일 생성 샘플
위의 코드를 기반으로 input.md라는 파일에서 읽은 Markdown 텍스트를 변환하여 output.html 파일에 쓰는 샘플을 작성했습니다.
(코드에 미숙함의 힌트가 있는 것 같지만 이 부분은 앞으로 작업할 것입니다.)
use pulldown_cmark::{html, Options, Parser};
use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::Path;
fn read_md_file(file_path: &std::path::Path) -> Result<String, Box<dyn std::error::Error>> {
let md = fs::read_to_string(file_path.to_str().unwrap())?;
Ok(md)
}
fn write_html_file(
file_path: &std::path::Path,
html: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let mut file = File::create(file_path)?;
write!(file, "{}", html)?;
Ok(())
}
fn main() {
let input_file_path = Path::new("./input.md");
let markdown_input = read_md_file(input_file_path).unwrap();
let mut options = Options::empty();
options.insert(Options::ENABLE_STRIKETHROUGH);
let parser = Parser::new_ext(&markdown_input, options);
let mut html_output = String::new();
html::push_html(&mut html_output, parser);
let html_file_path = Path::new("./output.html");
write_html_file(html_file_path, &html_output).unwrap();
}
Rust에서 파일 변경 감지
이제 마크다운 파일에서 HTML 파일을 생성하는 작업을 완료했습니다.
하지만 마크다운 파일의 변경 사항을 어떻게 감지할 수 있을까요?
이번에는 다른 상자를 사용하여 파일 변경 사항을 감지하기로 결정했습니다. 그것은 notify라는 크레이트입니다.
https://github.com/notify-rs/notify
부수적으로, 노티파이에는 4.0과 5.0.0-pre.14의 두 가지 버전이 개발되고 있는 것 같습니다. 안정적인 버전인 것 같은 4.0을 선택했습니다.
(README에 있는 문서만 봤는데 버전마다 굉장히 다른 방식으로 쓰여진 것 같습니다.)
Cargo.toml에 다음을 추가하고 마크다운 파일의 변경 사항을 감지하고 HTML 파일을 생성하도록 코드를 다시 작성했습니다.
notify = "4.0.16"
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
use pulldown_cmark::{html, Options, Parser};
use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::sync::mpsc::channel;
use std::time::Duration;
fn read_md_file(file_path: &std::path::Path) -> Result<String, Box<dyn std::error::Error>> {
let md = fs::read_to_string(file_path.to_str().unwrap())?;
Ok(md)
}
fn write_html_file(
file_path: &std::path::Path,
html: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let mut file = File::create(file_path)?;
write!(file, "{}", html)?;
Ok(())
}
fn markdown_to_html(input_path: &std::path::Path, output_path: &std::path::Path) {
let markdown_input = read_md_file(input_path).unwrap();
let mut options = Options::empty();
options.insert(Options::ENABLE_STRIKETHROUGH);
let parser = Parser::new_ext(&markdown_input, options);
let mut html_output = String::new();
html::push_html(&mut html_output, parser);
write_html_file(output_path, &html_output).unwrap();
}
fn main() -> notify::Result<()> {
let input_file_path = Path::new("./input.md");
let output_file_path = Path::new("./output.html");
let (tx, rx) = channel();
let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(1))?;
watcher.watch(input_file_path, RecursiveMode::Recursive)?;
loop {
match rx.recv() {
Ok(_) => markdown_to_html(input_file_path, output_file_path),
Err(err) => println!("watch error: {:?}", err),
};
}
}
지정된 디렉토리의 마크다운 파일에 대한 변경 사항을 감지하고 대상 파일을 HTML에 씁니다.
드디어 목표에 가까워졌습니다.
이제 지정된 디렉토리 내에서 변경 사항을 감지할 수 있도록 위의 프로세스를 추가로 확장하고자 했습니다.
그러나 알림에서 디렉토리를 지정하면 디렉토리 내의 변경 사항을 감지하는 것 같았습니다. 시원한.
알림의 DebouncedEvent 정보
덧붙여서, notify에서의 이벤트 검출에 관해서는, 여러 종류의 이벤트가 접하는 것 같습니다.
loop {
match rx.recv() {
Ok(event) => println!("event: {:?}", event),
Err(err) => println!("watch error: {:?}", err),
};
}
이 이벤트에 전달된 이벤트는 DebouncedEvent라고 하며 이 문서에 자세히 설명되어 있습니다.
https://docs.rs/notify/latest/notify/enum.DebouncedEvent.html
이와 같은 경우 대상 경로에 대한 쓰기 이벤트 직후에 NoticeWrite가 발생하고, 쓰기는 파일이 작성되고 지정된 시간 내에 경로에 대한 이벤트가 감지되지 않으면 발생합니다.
이번에는 이러한 이벤트를 이용하여 HTML 변환을 하기로 하였으나, 파일을 저장한 직후에 처리하는 것이 아니라 Write 시점에 HTML 변환 처리를 하기로 했습니다.
따라서 다음과 같이 코드를 다시 작성했습니다.
fn main() -> notify::Result<()> {
- let input_file_path = Path::new("./input.md");
- let output_file_path = Path::new("./output.html");
-
let (tx, rx) = channel();
let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(1))?;
- watcher.watch(input_file_path, RecursiveMode::Recursive)?;
+
+ let input_dir_path = Path::new("./input");
+ watcher.watch(input_dir_path, RecursiveMode::Recursive)?;
loop {
match rx.recv() {
- Ok(_) => markdown_to_html(input_file_path, output_file_path),
+ Ok(event) => match event {
+ notify::DebouncedEvent::Write(path) => {
+ let input_file_path = Path::new(&path);
+ let md_file_name = input_file_path.file_name().unwrap();
+ match Path::new(md_file_name).extension() {
+ Some(md_exntension) => {
+ if md_exntension != "md" {
+ eprintln!("ERROR: Only markdown files can be converted.");
+ std::process::exit(1);
+ }
+ }
+ None => {
+ eprintln!("ERROR: Not found extension.");
+ std::process::exit(1);
+ }
+ };
+
+ let html_file_name = md_file_name.to_str().unwrap().replace(".md", ".html");
+ let output_file_path = input_dir_path
+ .parent()
+ .unwrap()
+ .join("output")
+ .join(html_file_name);
+
+ markdown_to_html(input_file_path, output_file_path.as_path());
+
+ println!("=== Generated HTML ===");
+ println!("Input file path: {:?}", input_file_path);
+ println!(
+ "Output file path: {:?}",
+ output_file_path.canonicalize().unwrap()
+ );
+ }
+ _ => (),
+ },
Err(err) => println!("watch error: {:?}", err),
};
}
gm2h라는 도구를 만들었습니다.
이것은 내가 원하는 모든 것을 성취했습니다.
그런 다음 이 코드 세트를 CLI 도구의 형태로 사용할 수 있도록 만들었습니다.
이름을 gm2h로 지정하고 GitHub에 게시했습니다.
https://github.com/shinshin86/gm2h
아직 crates.io에는 출시되지 않았지만 GitHub에서 직접 설치하여 사용할 수 있습니다.
cargo install --git https://github.com/shinshin86/gm2h.git
현재 디렉터리에 Markdown 파일을 만들고 다음 명령을 실행합니다.
그런 다음 Markdown 파일을 편집하면 HTML 파일이 생성됩니다.
덧붙여서, gm2h 실행 후 Markdown 파일을 생성하면 해당 파일에 대한 변경 사항을 감지하지 못하는 것 같습니다. 이것이 내 코드 때문인지 알림 사양인지 아직 확인하지 못했습니다.
이것이 실제로 작동하는 방식입니다.
나는 실제로 HTML 파일을 만드는 상황에서 이것을 사용하며 매우 유용합니다.
아직 개선의 여지가 있으니 조금씩 개선해 나갈 생각입니다.
개선 사항이 있으면 Pull Request를 보내주세요. 환영합니다.
끝까지 읽어주셔서 감사합니다!
Reference
이 문제에 관하여(Rust가 Markdown 파일의 변경 사항을 감지하고 HTML을 생성하도록 합니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/shinshin86/let-rust-detect-changes-in-the-markdown-file-and-generate-html-2a8e텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)