ルースは、マークダウンファイルの変更を検出し、HTMLを生成しましょう.


私は最近錆を学んでいます.
私は最近、いくつかの簡単なHTMLファイルを作成するために、ほとんどのテキストが必要です.
もちろん、HTMLを直接作成することは痛みですので、私はMarkdownを書いて、HTMLに変換したと考えました.
私はとにかく私は錆でその変換ツールを作成できるかどうか疑問に思った.それは私に良い学習プログラミングのようだった.
これはどのように私は錆のHTML変換ツールにマークダウンを行ったの記録です.

マークダウンをHTMLに変換する


プルダウンCmarkと呼ばれるHTMLにMarkdownを変換するための箱があります.
https://crates.io/crates/pulldown-cmark
私は変換のためのコードを書くためにそれを使用することを決めた.
プルダウンCmarkを使用するために、私は貨物をセットアップしました.このようなtoml.
デフォルトでは、バイナリも構築されましたが、今回はプルダウンCmarkをライブラリとして使いたかったので、これを説明しました.
pulldown-cmark = { version = "0.9.1", default-features = false }
私は変換のためのサンプルコードを書いている.
(プルダウン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>

Markdownファイルの読み込みとHTMLファイルの生成例


上記のコードに基づいて、入力と呼ばれるファイルから読み込んだMarkdownテキストを変換するサンプルを書いた.mdを出力に書き込みます.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();
}

さびのファイル変化の検出


私は今、MarkdownファイルからHTMLファイルを生成するタスクを達成しました.
しかし、どのように我々はMarkdownファイルの変更を検出できますか?
今回はファイルの変更を検出するために別のクレートを使うことにしました.それは通知と呼ばれる箱です.
https://github.com/notify-rs/notify
なお、4.0と5.0.0 - preで開発された2つのバージョンの通知があるようです.14 .私は安定版と思われる4.0を選びました.
( READMEでドキュメントを見ただけですが、それぞれのバージョンは非常に異なった方法で書かれているようです).
私は次の貨物に追加しました.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),
        };
    }
}

指定したディレクトリのMarkdownファイルへの変更を検出し、ターゲットファイルをHTMLに書き込む


我々は最終的に我々の目標に近づいている.
さて、指定したディレクトリ内の変更を検出できるように、上記のプロセスをさらに拡張したいと思います.
しかし、私がNotifyでディレクトリを指定するならば、それはディレクトリの中で変化を見つけるでしょう.クール.

に関する通知について


なお、通知中のイベント検出に関しては、複数の種類のイベントが発生するようです.
   loop {
        match rx.recv() {
            Ok(event) => println!("event: {:?}", event),
            Err(err) => println!("watch error: {:?}", err),
        };
    }
このイベントで通過されるものはDeadounceDeventと呼ばれています.
https://docs.rs/notify/latest/notify/enum.DebouncedEvent.html
このような場合には、ターゲットパスの書き込みイベント直後にNoteeWriteが発行され、ファイルが書き込まれたときに書き込みが発行され、指定された時間内にパスのイベントが検出されない.
今回はHTMLでの変換を行うためにこれらのイベントを使用することにしましたが、ファイルの保存直後に処理のタイミングで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),
         };
     }

私はGM 2 Hと呼ばれるツールを作成しました。


これは、私がしたかったすべてを成し遂げました.
それから、私はCLIツールの形で利用できるコードのこのセットを作りました.
私はGM 2 Hという名前を付けて、Githubでそれを発表しました.

https://github.com/shinshin86/gm2h
それはまだ箱にリリースされていません.IOがインストールされ、直接Githubから使用することができます.
cargo install --git https://github.com/shinshin86/gm2h.git
現在のディレクトリにMarkdownファイルを作成し、次のコマンドを実行します
次に、マークダウンファイルを編集し、HTMLファイルが生成されます.
ところで、gm 2 hを実行した後に、Markdownファイルを作成した場合、そのファイルへの変更を検出していないようです.私はまだこれが私のコードのためであるか、またはNotifyの仕様であるかどうか見つけることができませんでした.
これは実際にどのように動作します.

私は実際に私はHTMLファイルを作成する状況でこれを使用し、それは非常に便利です.
まだ改善の余地がありますので、少しずつ改善する予定です.
任意の改善を見つける場合は、私にプルrequstを送ってください.歓迎します.
最後まで読んでくれてありがとう!