✔
プロジェクトと一体でコンテンツを提供するCLIツールを作った
xxxxx.md
---
title: タイトル
emoji: 🐒
type: tech
topics: []
published: false
---
コード↓
```rust:src/main.rs
1
```
例えばxxxxx.md
のファイル内容が上記のような場合で、projects/xxxxx/src/main.rs
が以下の場合には...
main.rs
// 1
fn main() {
println!("Hello, world!");
}
// -1
xxxxx.md
ファイルが以下のように更新されます。
xxxxx.md
---
title: タイトル
emoji: 🐒
type: tech
topics: []
published: false
---
コード↓
```rust:src/main.rs
fn main() {
println!("Hello, world!");
}
```
zenn-content
フォルダの構成
├─images
├─books
├─articles
│ └─xxxxx.md
└─projects
└─xxxxx
├─src
│ └─main.rs
└─Cargo.toml
新しくprojects
というフォルダを作成し、この下に記事に使うソースコードを管理するプロジェクトを作成します。
この時作成するプロジェクトフォルダの名称は、記事と同じ名前にします。
コード
main.rust
use std::env;
use regex::Regex;
mod modules;
fn main() {
let args: Vec<String> = env::args().collect();
let path: &str = &args[1];
let article_path: &String = &format!("./articles/{}.md", path);
let project_path: String = format!("./projects/{}", path);
let re_code_snippet: Regex = Regex::new(r"```(?P<lang>\w+):(?P<path>.*)").unwrap();
let re_number: Regex = Regex::new(r"^\d+$").unwrap();
let mut article_line_count: usize = 0;
let mut result: Vec<String> = Vec::new();
let article_data: Vec<String> = modules::file::read_lines(&article_path);
for line in &article_data {
if re_number.is_match(line) {
article_line_count += 1;
continue;
}
result.push(line.clone());
if let Some(captures) = re_code_snippet.captures(&line) {
let path: &str = captures.name("path").unwrap().as_str();
// let lang: &str = captures.name("lang").unwrap().as_str();
let source_code_number: &String = &article_data[article_line_count + 1];
let source_code_path: String = format!("{}/{}", project_path, path);
let source_data: Vec<String> = modules::file::read_lines(&source_code_path);
let mut source_code_count: usize = 0;
for line in &source_data {
if line == &format!("// {}", source_code_number) {
source_code_count += 1;
for mached_line in &source_data[source_code_count..] {
if mached_line == &format!("// -{}", source_code_number) {
break
}
result.push(mached_line.clone());
}
}
source_code_count += 1;
}
}
article_line_count += 1;
}
modules::file::write(&article_path, result.join("\n"));
}
modules/file.rs
use std::fs::File;
use std::io::{prelude::*, self};
use std::path::{Path, Display};
pub fn write(path: &str, content: String) {
let path: &Path = Path::new(&path);
let display: Display = path.display();
let mut file: File = match File::create(&path) {
Err(why) => panic!("couldn't create {}: {}", display, why),
Ok(file) => file,
};
match file.write_all(content.as_bytes()) {
Err(why) => panic!("couldn't write to {}: {}", display, why),
Ok(_) => (),
}
}
pub fn read_lines(path: &str) -> Vec<String> {
let mut result: Vec<String> = Vec::new();
let file: File = File::open(path).unwrap();
let tmp = io::BufReader::new(file).lines();
for line in tmp {
result.push(line.unwrap());
}
return result;
}
pub mod file;
Cargo.toml
[package]
name = "aci"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+ regex = "1"
使い方
$ cargo build
target/debug/aci.exe
をzenn-content
直下に配置し、以下コマンドを実行。
$ aci <path>
Discussion