Skip to content

Latest commit

 

History

History
150 lines (124 loc) · 5.34 KB

File metadata and controls

150 lines (124 loc) · 5.34 KB

Sql File Comment and Statement Parsing for Documentation

License Codecov CI Documentation

This crate extracts documentation from SQL files by parsing:

  • SQL statements (via sqlparser)
  • Line and block comments
  • Table and column locations

Then comments are attached to the SQL structures they describe, producing a structured, queryable documentation model.

Example

With a directory structured like this:

sql_dir/
└── users.sql

and the content of users.sql being:

/* Table storing user accounts */
CREATE TABLE users (
    /* Primary key for each user */
    id INTEGER PRIMARY KEY,
    -- The user's login name
    username VARCHAR(255) NOT NULL,
    /* User's email address */
    email VARCHAR(255) UNIQUE NOT NULL
);

A rudimentary implementation can be generated with:

# #[cfg(not(feature = "std"))]
# fn main() {}
# #[cfg(feature = "std")]
fn main() -> Result<(), sql_docs::error::DocError> {
    use sql_docs::{GenericDialect, SqlDoc};
    use std::{env, fs};

    let base = env::temp_dir().join("tmp_sql_docs_example");
    let _ = fs::remove_dir_all(&base);
    fs::create_dir_all(&base)?;
    let example = base.join("example.sql");

    fs::write(
        &example,
        r#"-- Table storing user accounts
-- Contains all user values
/* Rows generated at registration */
CREATE TABLE users (
    /* Primary key for each user */
    id INTEGER PRIMARY KEY,
    -- The user's login name
    username VARCHAR(255) NOT NULL,
    /* User's email address */
    email VARCHAR(255) UNIQUE NOT NULL
);"#,
    )?;

    let docs = SqlDoc::from_path(&example)
        .collect_all_leading()
        .flatten_multiline_with(". ")
        .build::<GenericDialect>()?;

    let users = docs.table("users", None)?;
    assert_eq!(users.name(), "users");
    assert_eq!(
        users.doc(),
        Some("Table storing user accounts. Contains all user values. Rows generated at registration")
    );
    assert_eq!(users.path(), Some(example.as_path()));

    let _ = fs::remove_dir_all(&base);
    Ok(())
}

Or with no std and from a String:

# #[cfg(feature = "std")]
# fn main() {}
# #[cfg(not(feature = "std"))]
fn main() -> Result<(), sql_docs::error::DocError> {
    use sql_docs::{GenericDialect, SqlDoc};

    let example = r#"-- Table storing user accounts
-- Contains all user values
/* Rows generated at registration */
CREATE TABLE users (
    /* Primary key for each user */
    id INTEGER PRIMARY KEY,
    -- The user's login name
    username VARCHAR(255) NOT NULL,
    /* User's email address */
    email VARCHAR(255) UNIQUE NOT NULL
);"#;

    let docs = SqlDoc::builder_from_str(example)
        .collect_all_leading()
        .flatten_multiline_with(". ")
        .build::<GenericDialect>()?;

    let users = docs.table("users", None)?;
    assert_eq!(users.name(), "users");
    assert_eq!(
        users.doc(),
        Some("Table storing user accounts. Contains all user values. Rows generated at registration")
    );

    Ok(())
}

Primary Interface (Main API)

These are the primary entry points most users will interact with:

Use Cases

This crate is designed for generating documentation from SQL schemas by attaching comments to:

  • Tables
  • Columns

using only comments that immediately precede the items they describe, with the ability to differentiate between multiple leading comments per statement or collect only the nearest comment preceding a statement.

This makes it well-suited for:

  • Auto-generating Markdown or HTML documentation
  • Building database schema explorers
  • Enforcing documentation rules in CI
  • Static analysis of SQL schemas

Design Notes

  • Inline and interstitial comments are intentionally ignored.
  • Comment attachment is line-based and deterministic.
  • One SQL file may define multiple tables.
  • No database connection is required.
  • sql_doc items are sorted by table name and column name, supporting binary searching