risedev_docslt/
risedev-docslt.rs1use std::io::Write;
16use std::path::{Path, PathBuf};
17
18use anyhow::Result;
19use itertools::Itertools;
20use thiserror_ext::AsReport;
21use tracing::*;
22
23#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
24struct Position {
25    filepath: PathBuf,
26    line_no: usize,
27}
28
29struct SltBlock {
30    position: Position,
31    content: String,
32}
33
34fn extract_slt(filepath: &Path) -> Vec<SltBlock> {
36    let content = std::fs::read_to_string(filepath).unwrap();
37
38    let mut blocks = vec![];
39    let mut iter = content.lines().enumerate();
40    while let Some((i, line)) = iter.next() {
41        if !line.trim_end().ends_with("```slt") {
42            continue;
43        }
44        let mut content = String::new();
45        loop {
46            let Some((i, mut line)) = iter.next() else {
47                panic!("unexpected end of file at {}", filepath.display());
48            };
49            line = line.trim();
50            if line.is_empty() {
52                continue;
53            }
54            if !(line.starts_with("///") || line.starts_with("//!")) {
55                panic!("expect /// or //! at {}:{}", filepath.display(), i + 1);
56            }
57            line = &line[3..];
58            if line.trim() == "```" {
59                break;
60            }
61            content += line.strip_prefix(' ').unwrap_or(line);
63            content += "\n";
64        }
65        blocks.push(SltBlock {
66            position: Position {
67                filepath: filepath.into(),
68                line_no: i + 1,
69            },
70            content,
71        });
72    }
73    blocks
74}
75
76fn main() -> Result<()> {
77    tracing_subscriber::fmt()
78        .with_max_level(tracing::Level::INFO)
79        .without_time()
80        .with_target(false)
81        .with_level(true)
82        .init();
83
84    let slt_dir = PathBuf::from("e2e_test/generated/docslt");
86    fs_err::remove_dir_all(&slt_dir).ok();
87    fs_err::create_dir_all(&slt_dir)?;
88
89    for entry in glob::glob("src/**/*.rs")? {
90        let path = match entry {
91            Ok(path) => path,
92            Err(e) => {
93                error!("{}", e.as_report());
94                continue;
95            }
96        };
97        let blocks = extract_slt(&path);
98        if blocks.is_empty() {
99            continue;
100        }
101        info!("found {} blocks at {}", blocks.len(), path.display());
102
103        let slt_filename = path
104            .components()
105            .map(|comp| comp.as_os_str().to_str().unwrap())
106            .filter(|name| *name != "src")
107            .join("__")
108            .replace(".rs", ".slt");
109        let mut slt_file = fs_err::File::create(slt_dir.join(slt_filename))?;
110        write!(
111            slt_file,
112            "\
113            # DO NOT MODIFY THIS FILE\n\
114            # This file is generated from `{}` at {}.\n\
115            \n\
116            statement ok\n\
117            set RW_IMPLICIT_FLUSH to true;\n",
118            path.display(),
119            chrono::Utc::now()
120        )?;
121        for block in blocks {
122            write!(
123                slt_file,
124                "\n\
125                # ==== `slt` @ L{} ====\n\
126                \n\
127                {}\n",
128                block.position.line_no, block.content
129            )?;
130        }
131    }
132    Ok(())
133}