最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

利用rust編一個(gè)靜態(tài)博客工具

 更新時(shí)間:2023年12月07日 14:05:27   作者:tommy_1  
這篇文章主要為大家詳細(xì)介紹了如何利用rust編一個(gè)靜態(tài)博客工具,這個(gè)靜態(tài)博客的工具主要是把md文檔轉(zhuǎn)為html靜態(tài)網(wǎng)站/博客,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

這個(gè)靜態(tài)博客的工具主要是把md文檔轉(zhuǎn)為html靜態(tài)網(wǎng)站/博客。github鏈接github.com/maochunguang/rust-md-site-tool

首先說明md文檔轉(zhuǎn)為靜態(tài)網(wǎng)站/博客的原理,就是先做一個(gè)目錄文檔,叫做summary.md,然后其他文檔都會(huì)鏈接到這個(gè)目錄文檔里。當(dāng)把md文檔轉(zhuǎn)為html時(shí),需要對(duì)鏈接進(jìn)行處理,保證鏈接可以正常跳轉(zhuǎn),這樣就完成了一個(gè)簡(jiǎn)單的md轉(zhuǎn)靜態(tài)博客工具。

第一步,設(shè)計(jì)博客工具

目錄和樣式設(shè)計(jì)

這個(gè)簡(jiǎn)易的博客/站點(diǎn)工具主要是模仿gitbook,可以認(rèn)為是gitbook的簡(jiǎn)易版。頁面布局也是gitbook一樣,左邊是目錄,右邊是內(nèi)容。

首先需要定義一個(gè)博客的靜態(tài)目錄結(jié)構(gòu),如下圖所示:

  • docs目錄是所有md文檔源文件;
  • static目錄是所有靜態(tài)文件的存放目錄,比如js,css,image文件;
  • md_config.toml是全局配置文件,
  • summary.md是全局站點(diǎn)的目錄;
  • index.md是全局首頁內(nèi)容

├── docs
│   ├── chapter1
│   │   ├── chapter1.html
│   │   ├── chapter1.md
│   ├── chapter2
│   │   ├── chapter2.html
│   │   ├── chapter2.md
│   ├── index.md
│   └── summary.md
├── md_config.toml
└── static
    ├── css
    │   └── style.css
    ├── images
    └── js

summary.md格式如下:

* [章節(jié)1](chapter1/chapter1.md)
  * [小節(jié)1.1](chapter1/section1.1.md)
  * [小節(jié)1.2](chapter1/section1.2.md)
* [章節(jié)2](chapter2/chapter2.md)
  * [小節(jié)2.1](chapter2/section2.1.md)
  * [小節(jié)2.2](chapter2/section2.2.md)

配置文件結(jié)構(gòu)如下:

title = "My Blog"
author = "Your Name"
description = "This is my blog."
port = 9900
static_dir = "static"
md_source_dir = "docs"
output_dir = ".site"
default_css_header = "<link rel=\"stylesheet\" href=\"./css/style.css\">"
default_code_header = "<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/styles/default.min.css\"><script src=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/highlight.min.js\"></script>"
default_code_plugin = "<script>hljs.highlightAll();</script>"

功能實(shí)現(xiàn)和設(shè)計(jì)

本質(zhì)上是一個(gè)命令行工具,依賴clap=4.4.0來創(chuàng)建,第一版支持三個(gè)命令:

  • init,初始化一些目錄和配置文件
  • build,構(gòu)建所有的文件,轉(zhuǎn)為html到指定的輸出目錄
  • run,本地運(yùn)行,在本地預(yù)覽

由于要實(shí)現(xiàn)md文件轉(zhuǎn)為html,需要借助依賴pulldown-cmark = "0.9"。

第二步,創(chuàng)建三個(gè)命令

創(chuàng)建main.js,把三個(gè)命令寫出來:

fn main() {
    let matches = Command::new("rust-md-blog-tool")
        .version("1.0")
        .author("Your Name")
        .about("A simple static site generator")
        .subcommand(Command::new("init")
            .about("Initializes the blog with default configuration"))
        .subcommand(Command::new("build")
            .about("Builds the static site from markdown files"))
        .subcommand(Command::new("run")
            .about("Runs a local server to view the blog"))
        .get_matches();

    match matches.subcommand() {
        Some(("init", _)) => init_lib::init_command(),
        Some(("build", _)) => build_lib::build_command(),
        Some(("run", _)) => server_lib::run_command(),
        _ => println!("Invalid command or no command provided"),
    }
}

第三步,實(shí)現(xiàn)init命令

init命令就是創(chuàng)建一個(gè)默認(rèn)的配置文件,以及六個(gè)目錄和一個(gè)css文件。 核心代碼如下:

  • config_content是默認(rèn)的配置文件內(nèi)容,可以自定義字符串或者使用模板文件;
  • get_style_content是默認(rèn)的css文件,可以自定義字符串或者使用模板文件;
    let _ = File::create("md_config.toml").and_then(|mut file| file.write_all(config_content.as_bytes()));

    // 創(chuàng)建所需的目錄
    let _ = fs::create_dir_all("docs").map(|_| println!("created 'docs' Success"));
    let _ = fs::create_dir_all(".site").map(|_| println!("created '.site' Success"));
    let _ = fs::create_dir_all("static").map(|_| println!("created 'static' Success"));
    let _ = fs::create_dir_all("static/js").map(|_| println!("created 'static/js' Success"));
    let _ = fs::create_dir_all("static/css").map(|_| println!("created 'static/css' Success"));
    let _ = fs::create_dir_all("static/images").map(|_| println!("created 'static/images' Success"));
    let _ = File::create("static/css/style.css").and_then(|mut file| file.write_all(get_style_content().as_bytes())); 
    println!("Blog initialized with default configuration.");

第四步,實(shí)現(xiàn)build命令

構(gòu)建所有的md文件流程也很簡(jiǎn)單:

核心代碼如下:

pub fn build_command() {
    // ... 讀取配置文件和設(shè)置目錄 ...
    let config = fs::read_to_string("md_config.toml").expect("Unable to read config file");
    let parsed_config = config.parse::<Value>().expect("Unable to parse config");
    let source_dir =  parsed_config.get("md_source_dir").and_then(Value::as_str).unwrap_or("docs");
    // .....
    println!("load config source_dir :{}, output_dir:{}", source_dir, output_dir);
    let md_source_dir = Path::new(source_dir);
    let summary_path =format!("{}{}", source_dir, "/summary.md");

 
    // 解析 summary.md 并構(gòu)建目錄HTML
    let toc_html = build_toc_content(summary_path.clone());
    
    // 解析每個(gè).md文件 轉(zhuǎn)為HTML
    let md_files = read_dir_recursive(md_source_dir).expect("read md dir failed");

    // 為每個(gè)Markdown文件生成HTML頁面
    for entry in md_files {
        let path = entry;
        if path.ends_with("summary.md"){
            continue;
        }
        let output_file_path = transform_path(&path, source_dir, output_dir);
        if path.extension().and_then(|s| s.to_str()) == Some("md") {
            let mut md_content = String::new();
            File::open(&path).and_then(|mut file| file.read_to_string(&mut md_content)).expect("Failed to read Markdown file");
            let parser = Parser::new(&md_content);
            let mut html_content = String::new();
            html::push_html(&mut html_content, parser);
            let _ = html_content.replace(".md", ".html");
            let output_file = output_file_path.with_extension("html");
            println!("output file:{}", output_file.as_path().display());
            let mut full_html;
            // 處理index頁面邏輯
            full_html = format!("<html><head></head><body><div class=\"toc\">{}</div><div class=\"content\">{}</div></body></html>", toc_html, html_content);
            full_html = append_html(&full_html, "</head>", &[default_css_header, default_code_header]);
            // 增加highlight.js處理代碼塊
            full_html = append_html(&full_html, "</body>", &[default_code_plugin]);
            // 處理相對(duì)路徑
            if contains_sub_dir(&output_file, output_dir){
                full_html = full_html.replace("./", "../").replace("<a href=\"", "<a href=\"../")
            }

            if let Some(parent) = output_file.parent() {
                // 創(chuàng)建所有必要的父文件夾
                fs::create_dir_all(parent).expect("create html parent dir failed");
            }
            fs::write(output_file, full_html).expect("Failed to write HTML file");
        }
    }

    // ... 復(fù)制靜態(tài)資源 ...
    // 定義靜態(tài)資源目錄和目標(biāo)目錄
    let static_dir = Path::new(static_dir);
    let output_dir = Path::new(output_dir);

    // 復(fù)制靜態(tài)資源
    if static_dir.exists() {
        copy_dir_all(static_dir, output_dir).expect("Failed to copy static files");
    }

    println!("Site built successfully.");
}

生成html文件這里需要注意幾個(gè)點(diǎn):

  • 第一個(gè)是文件路徑,需要把html生成到配置的輸出目錄,需要一個(gè)path轉(zhuǎn)換邏輯;
  • summary里面的鏈接需要處理,默認(rèn)是.md,需要轉(zhuǎn)為.html;
  • 所有鏈接的相對(duì)路徑需要處理,根據(jù)目錄都是用相對(duì)路徑;
  • 復(fù)制靜態(tài)資源時(shí),也需要注意文件路徑,以及文件夾是否存在。

第五步,實(shí)現(xiàn)run命令

這里由于只是本地運(yùn)行查看html,所以選擇tiny_http,啟動(dòng)一個(gè)簡(jiǎn)單的http服務(wù)。核心代碼如下:

pub fn run_command() {
    // 讀取配置文件
    let config = fs::read_to_string("md_config.toml").expect("Unable to read config file");
    let parsed_config = config.parse::<Value>().expect("Unable to parse config");
    let port = parsed_config.get("port").and_then(Value::as_str).unwrap_or("9900");
    let output_dir = parsed_config.get("output_dir").and_then(Value::as_str).unwrap_or(".site");
    let address = format!("0.0.0.0:{}", port);
    let server = Server::http(address).unwrap();
    println!("Running local server on port {}", port);

    for request in server.incoming_requests() {
        let url = if request.url() == "/" {
            "/index.html" // 使用 index.html 作為根路徑的默認(rèn)頁面
        } else {
            request.url()
        };

        let file_path = PathBuf::from(output_dir).join(&url[1..]); // 移除 URL 的首個(gè)斜杠
        match fs::read(&file_path) {
            Ok(contents) => {
                let response = Response::from_data(contents).with_header(
                    Header::from_bytes("Content-Type", "text/html; charset=utf-8").unwrap(),
                );
                request.respond(response).unwrap();
            }
            Err(_) => {
                let response = Response::from_string("Not Found").with_status_code(404);
                request.respond(response).unwrap();
            }
        }
    }
}

這里需要注意的是,直接返回html會(huì)中文亂碼,所以統(tǒng)一都加了Content-Type", "text/html; charset=utf-8。

第六步,展示成果

新建一個(gè)用于測(cè)試的博客目錄:

  • 在博客工具目錄執(zhí)行cargo run生成執(zhí)行的命令文件;
  • 復(fù)制博客工具命令,cp target/debug/rust_md_site_tool ~/.cargo/bin;
  • 執(zhí)行 rust_md_site_tool init初始化博客;
  • 在docs目錄新建summary.mdindex.md,并在summary里創(chuàng)建好測(cè)試的md文檔和鏈接;
  • rust_md_site_tool build構(gòu)建目錄;
  • 打開瀏覽器 http://localhost:9900/ ;可以訪問index.html;

到此這篇關(guān)于利用rust編一個(gè)靜態(tài)博客工具的文章就介紹到這了,更多相關(guān)rust靜態(tài)博客內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 深入理解Rust中Cargo的使用

    深入理解Rust中Cargo的使用

    本文主要介紹了深入理解Rust中Cargo的使用,Cargo簡(jiǎn)化了項(xiàng)目的構(gòu)建過程,提供了依賴項(xiàng)管理,以及一系列方便的工作流程工具,下面就來具體的介紹一下如何使用,感興趣的可以了解一下
    2024-04-04
  • Rust中HashMap類型的使用詳解

    Rust中HashMap類型的使用詳解

    Rust中一種常見的集合類型是哈希映射,本文主要介紹了Rust中HashMap類型的使用詳解,包含創(chuàng)建訪問修改遍歷等,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03
  • 基于Rust構(gòu)建一個(gè)Git提交歷史可視化工具

    基于Rust構(gòu)建一個(gè)Git提交歷史可視化工具

    在軟件開發(fā)中,版本控制系統(tǒng)的歷史記錄往往承載著項(xiàng)目的演進(jìn)脈絡(luò),本文主要介紹了如何基于Rust構(gòu)建一個(gè)Git提交歷史可視化工具,感興趣的小伙伴可以了解下
    2025-11-11
  • 淺析Rust多線程中如何安全的使用變量

    淺析Rust多線程中如何安全的使用變量

    這篇文章主要為大家詳細(xì)介紹了Rust如何在線程的閉包中安全的使用變量,包括共享變量和修改變量,文中的示例代碼講解詳細(xì),有需要的小伙伴可以參考下
    2025-01-01
  • rust引用和借用的使用小結(jié)

    rust引用和借用的使用小結(jié)

    在rust中,引用的語法非常簡(jiǎn)單。通過&來取引用,通過*來解引用,這篇文章主要介紹了rust引用和借用的使用小結(jié),總的來說,借用規(guī)則,同一時(shí)刻,你只能擁有要么一個(gè)可變引用,?要么任意多個(gè)不可變引用,具體內(nèi)容詳情跟隨小編一起看看吧
    2023-01-01
  • 如何使用bindgen將C語言頭文件轉(zhuǎn)換為Rust接口代碼

    如何使用bindgen將C語言頭文件轉(zhuǎn)換為Rust接口代碼

    這篇文章主要介紹了使用bindgen將C語言頭文件轉(zhuǎn)換為Rust接口代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-01-01
  • Rust中print和println的區(qū)別實(shí)例解析

    Rust中print和println的區(qū)別實(shí)例解析

    Rust中print!和println!宏用于輸出,區(qū)別在于println!自動(dòng)換行,print!不換行,前者適用于同一行內(nèi)組合輸出,后者用于獨(dú)立日志或需換行場(chǎng)景,均支持格式化參數(shù),如{},本文給大家介紹Rust中print和println的區(qū)別,感興趣的朋友一起看看吧
    2025-06-06
  • Rust生命周期常見誤區(qū)(中英對(duì)照)全面指南

    Rust生命周期常見誤區(qū)(中英對(duì)照)全面指南

    這篇文章主要WEIDJAI?介紹了Rust生命周期常見誤區(qū)(中英對(duì)照)的全面指南,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • rust udp編程方法的具體使用

    rust udp編程方法的具體使用

    Rust標(biāo)準(zhǔn)庫通過std::net模塊提供了UDP網(wǎng)絡(luò)編程支持,下面就來介紹一下rust udp編程方法的具體使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-06-06
  • Rust整合Elasticsearch的詳細(xì)過程(收藏)

    Rust整合Elasticsearch的詳細(xì)過程(收藏)

    Elasticsearch是基于Lucene構(gòu)建的開源分布式搜索和分析引擎,支持水平擴(kuò)展和多語言調(diào)用,ELK(Elastic Stack)組合包括Elasticsearch、Kibana、Logstash和Beats,專注于日志數(shù)據(jù)分析和實(shí)時(shí)監(jiān)控,本文介紹Rust整合Elasticsearch的過程,一起看看吧
    2024-11-11

最新評(píng)論

淳安县| 广宁县| 沿河| 宿州市| 铁岭县| 郎溪县| 天柱县| 营山县| 封开县| 赣州市| 铁岭县| 保定市| 和政县| 应用必备| 阜城县| 峨山| 徐闻县| 来安县| 拉萨市| 肇州县| 宣汉县| 廊坊市| 桓仁| 大余县| 冀州市| 万州区| 尼勒克县| 梁山县| 沅江市| 海兴县| 建阳市| 嫩江县| 彭山县| 锦屏县| 南投市| 南皮县| 奈曼旗| 农安县| 太原市| 北川| 三河市|