Skip to content

Commit 80a63b7

Browse files
committed
final polish for 1.0 release
1 parent e16a8d3 commit 80a63b7

10 files changed

Lines changed: 130 additions & 70 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "sql_docs"
3-
version = "0.1.0"
3+
version = "1.0.0"
44
edition = "2024"
55
description = "A crate for parsing comments from sql files and using them for documentation generation"
66
license = "GNU General Public License v3.0"

README.md

Lines changed: 62 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
[![License](https://img.shields.io/badge/GPL-3%20-blue.svg)](https://opensource.org/license/gpl-3-0)
44
[![Codecov](https://codecov.io/gh/rpg-alex/sql-docs/branch/main/graph/badge.svg)](https://codecov.io/gh/rpg-alex)
55

6-
This crate extracts *documentation* from SQL files by parsing:
6+
This crate extracts **documentation** from SQL files by parsing:
77

8-
- SQL statements (via [DataFusion’s SQL Parser](https://github.com/apache/datafusion-sqlparser-rs/))
9-
- Inline and multiline comments
8+
- SQL statements (via [`sqlparser`](https://github.com/sqlparser-rs/sqlparser-rs))
9+
- Line and block comments
1010
- Table and column locations
1111

12-
It then attaches comments to the SQL structures they describe.
12+
It then attaches comments to the SQL structures they describe, producing a structured, queryable documentation model.
1313

1414
## Example
1515

@@ -34,46 +34,76 @@ A rudimentary implementation can be generated with:
3434
```rust
3535
use sql_docs::SqlDoc;
3636
use sql_docs::error::DocError;
37-
use std::{env, fs, path::Path};
37+
use std::{env, fs};
3838

3939
fn main() -> Result<(), DocError> {
40-
// tmp dir and file created for demonstration purposes
40+
// Temporary directory and file created for demonstration purposes
4141
let base = env::temp_dir().join("tmp_dir");
4242
let _ = fs::remove_dir_all(&base);
4343
fs::create_dir_all(&base)?;
4444
let example = base.join("example.sql");
45-
fs::write(&example, r#"/* Table storing user accounts */
46-
CREATE TABLE users (
47-
/* Primary key for each user */
48-
id INTEGER PRIMARY KEY,
49-
-- The user's login name
50-
username VARCHAR(255) NOT NULL,
51-
/* User's email address */
52-
email VARCHAR(255) UNIQUE NOT NULL
53-
);"#)?;
54-
55-
// Extract table & column documentation
45+
46+
fs::write(
47+
&example,
48+
r#"/* Table storing user accounts */
49+
CREATE TABLE users (
50+
/* Primary key for each user */
51+
id INTEGER PRIMARY KEY,
52+
-- The user's login name
53+
username VARCHAR(255) NOT NULL,
54+
/* User's email address */
55+
email VARCHAR(255) UNIQUE NOT NULL
56+
);"#,
57+
)?;
58+
59+
// Extract documentation from a single file
5660
let docs = SqlDoc::from_path(&example).build()?;
57-
// Verify that the table name is correct
58-
assert_eq!(docs.tables().first().expect("hardcoded value").name(), "users");
59-
// Verify extraction of table comment
60-
assert_eq!(docs.tables().first().expect("hardcoded value").doc(), Some("Table storing user accounts"));
61-
// Verify First Column name and comment
62-
assert_eq!(docs.tables().first().expect("hardcoded value").columns().first().expect("hardcoded value").name(), "id");
63-
assert_eq!(docs.tables().first().expect("hardcoded value").columns().first().expect("hardcoded value").doc(), Some("Primary key for each user"));
61+
// Or extract recursively from a directory
62+
// let docs = SqlDoc::from_dir(&base).build()?;
63+
64+
// Retrieve a specific table
65+
let users = docs.table("users")?;
66+
67+
// Table name
68+
assert_eq!(users.name(), "users");
69+
// Optional table-level documentation
70+
assert_eq!(users.doc(), Some("Table storing user accounts"));
71+
// Path to the source file
72+
assert_eq!(users.path(), &Some(example));
73+
6474
let _ = fs::remove_dir_all(&base);
6575
Ok(())
6676
}
6777
```
68-
## Use cases
78+
## Primary Interface (Main API)
79+
80+
These are the primary entry points most users will interact with:
81+
82+
* [`SqlDoc::from_path`](https://docs.rs/sql-docs/latest/sql_docs/sql_doc/struct.SqlDoc.html#method.from_path) Build documentation from a single `.sql` file.
83+
* [`SqlDoc::from_dir`](https://docs.rs/sql-docs/latest/sql_docs/sql_doc/struct.SqlDoc.html#method.from_dir) Recursively scan a directory for `.sql` files and build documentation.
84+
* [`SqlDocBuilder::build`](https://docs.rs/sql-docs/latest/sql_docs/sql_doc/struct.SqlDocBuilder.html#method.build) Finalize the builder and produce a [`SqlDoc`].
85+
* [`SqlDocBuilder::deny`](https://docs.rs/sql-docs/latest/sql_docs/sql_doc/struct.SqlDocBuilder.html#method.deny) Exclude specific files by full path.
86+
* [`SqlDocBuilder::flatten_multiline`](https://docs.rs/sql-docs/latest/sql_docs/sql_doc/struct.SqlDocBuilder.html#method.flatten_multiline) Flatten multiline comments into a single line.
87+
88+
## Use Cases
6989

7090
This crate is designed for generating documentation from SQL schemas by attaching comments to:
71-
- Tables
72-
- Columns
7391

74-
using only comments that immediately precede the items they describe.
92+
* Tables
93+
* Columns
94+
95+
using **only comments that immediately precede** the items they describe.
96+
97+
This makes it well-suited for:
98+
99+
* Auto-generating Markdown or HTML documentation
100+
* Building database schema explorers
101+
* Enforcing documentation rules in CI
102+
* Static analysis of SQL schemas
103+
104+
## Design Notes
75105

76-
This makes it ideal for:
77-
- Auto-generating Markdown or HTML documentation
78-
- Building database schema explorers
79-
- Enforcing documentation rules in CI
106+
* Inline and interstitial comments are intentionally ignored.
107+
* Comment attachment is line-based and deterministic.
108+
* One SQL file may define multiple tables.
109+
* No database connection is required.

src/ast.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
//! Module for parsing the SQL and then using the resulting AST that will later
2-
//! be used in `comments` module
1+
//! Parse SQL text into an AST (`sqlparser`) for downstream comment attachment.
2+
//!
3+
//! This module does not interpret semantics; it only produces an AST + file metadata.
4+
35
use std::path::{Path, PathBuf};
46

57
use sqlparser::{
@@ -17,17 +19,16 @@ pub struct ParsedSqlFile {
1719
}
1820

1921
impl ParsedSqlFile {
20-
/// A parsed SQL file containing `DataFusion` AST nodes paired with their
21-
/// spans.
22+
/// Parses a [`SqlFile`] into `sqlparser` [`Statement`] nodes.
2223
///
23-
/// This struct wraps DataFusion’s `Statement` parsing so we can attach
24-
/// leading comments later in the pipeline.
24+
/// This is the AST layer used by the `comments` module to attach leading
25+
/// comment spans to statements/columns.
2526
///
2627
/// # Parameters
27-
/// - `file` is the [`SqlFile`] that will be parsed
28+
/// - `file`: the [`SqlFile`] to parse
2829
///
2930
/// # Errors
30-
/// - [`ParserError`] is returned for any errors parsing
31+
/// - Returns [`ParserError`] if parsing fails
3132
pub fn parse(file: SqlFile) -> Result<Self, ParserError> {
3233
let dialect = GenericDialect {};
3334
let statements = Parser::parse_sql(&dialect, file.content())?;

src/comments.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
//! Module for crawling the SQL documents based on the parser and
2-
//! parsing/extracting the leading comments.
1+
//! Extract comment spans from parsed SQL files.
32
//!
4-
//! *leading comment* a comment that
5-
//! precedes an SQL Statement.
3+
//! Definitions used throughout this crate:
4+
//! - **leading**: a comment that appears on lines immediately preceding a statement/column
5+
//! - **inline**: a comment that appears after code on the same line (ignored)
6+
//! - **interstitial**: a comment inside a statement (ignored)
7+
68
use std::fmt;
79

810
use crate::ast::ParsedSqlFile;
911

10-
/// Structure for holding a location in the file. Assumes file is first split by
11-
/// lines and then split by characters (column)
12+
/// Represents a line/column location within a source file.
1213
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd)]
1314
pub struct Location {
1415
line: u64,
@@ -45,7 +46,7 @@ impl Default for Location {
4546
}
4647
}
4748

48-
/// A structure for holding the span of comments found
49+
/// Represents a start/end span (inclusive/exclusive as used by this crate) for a comment in a file.
4950
#[derive(Clone, Debug, Eq, PartialEq)]
5051
pub struct Span {
5152
start: Location,

src/docs.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
//! Module for parsing sql and comments and returning `table` and `column`
2-
//! information, including comments
1+
//! Convert parsed SQL + extracted comments into structured documentation types.
2+
33
use core::fmt;
44
use std::path::PathBuf;
55

@@ -124,6 +124,12 @@ impl TableDoc {
124124
pub fn columns_mut(&mut self) -> &mut [ColumnDoc] {
125125
&mut self.columns
126126
}
127+
128+
/// Getter method for retrieving the table's [`PathBuf`]
129+
#[must_use]
130+
pub const fn path(&self) -> &Option<PathBuf> {
131+
&self.path
132+
}
127133
}
128134

129135
impl fmt::Display for TableDoc {
@@ -237,6 +243,7 @@ impl SqlFileDoc {
237243
}
238244
}
239245

246+
/// Converts a file doc into its table docs (consumes the [`SqlFileDoc`]).
240247
impl From<SqlFileDoc> for Vec<TableDoc> {
241248
fn from(value: SqlFileDoc) -> Self {
242249
value.tables
@@ -286,7 +293,7 @@ fn schema_and_table(name: &ObjectName) -> Result<(Option<String>, String), DocEr
286293
#[cfg(test)]
287294
mod tests {
288295
use core::fmt;
289-
use std::{env, fs};
296+
use std::{env, fs, path::PathBuf};
290297

291298
use sqlparser::{
292299
ast::{Ident, ObjectName, ObjectNamePart, ObjectNamePartFunction},
@@ -849,4 +856,15 @@ CREATE TABLE posts (
849856
let expected = vec![t1, t2];
850857
assert_eq!(got, expected);
851858
}
859+
#[test]
860+
fn table_doc_path_getter_returns_expected_value() {
861+
let mut table = TableDoc::new(None, "users".to_string(), None, Vec::new(), None);
862+
assert_eq!(table.path(), &None);
863+
let pb = PathBuf::from("some/dir/file.sql");
864+
table.set_path(Some(pb.clone()));
865+
assert_eq!(table.path(), &Some(pb));
866+
let no_path: Option<PathBuf> = None;
867+
table.set_path(no_path);
868+
assert_eq!(table.path(), &None);
869+
}
852870
}

src/error.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
//! Module for managing Document Errors as [`DocError`]
1+
//! Error types returned by this crate’s public APIs.
2+
23
use crate::{comments::CommentError, docs::TableDoc};
34
use core::fmt;
45
use sqlparser::parser::ParserError;
56
use std::{error, fmt::Debug};
6-
/// Error enum for returning relevant error based on error type
7+
8+
/// Errors that can occur while discovering, parsing, or documenting SQL files.
9+
#[non_exhaustive]
710
#[derive(Debug)]
811
pub enum DocError {
912
/// Wrapper for standard [`std::io::Error`]

src/files.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
//! Module that offers utilities for discovering, filtering, and loading `.sql`
2-
//! files from the filesystem.
1+
//! Discover and filter `.sql` files on disk.
2+
//!
3+
//! This module finds file paths; it does not parse SQL or extract comments.
34
45
use std::{
56
fs, io,
@@ -37,9 +38,8 @@ impl SqlFilesList {
3738
/// Creates a list of `.sql` files under `path`, optionally excluding files
3839
/// whose full paths appear in `deny_list`.
3940
///
40-
/// Paths in `deny_list` must match exactly the `PathBuf` returned by
41-
/// recursion. For convenience, the top-level API accepts `&[&str]`
42-
/// instead.
41+
/// Deny entries must match the discovered full path exactly (string equality on
42+
/// the resulting [`PathBuf`]).
4343
///
4444
/// # Parameters
4545
///
@@ -60,7 +60,7 @@ impl SqlFilesList {
6060
Ok(Self { sql_files: allow_list })
6161
}
6262

63-
/// Returns the discovered `.sql` files in their original order.
63+
/// Returns discovered `.sql` files in discovery order (filesystem-dependent).
6464
#[must_use]
6565
pub fn sql_files(&self) -> &[PathBuf] {
6666
&self.sql_files

src/lib.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
#![doc = include_str!("../README.md")]
2-
//! Module layout:
3-
//! - [`files`] : discover and load `.sql` files from disk
4-
//! - [`ast`] : parse SQL into an AST using [`sqlparser`]
5-
//! - [`comments`] : extract and model comments and spans
6-
//! - [`docs`] : Generates a struct to hold [`docs::TableDoc`]
7-
//! - [`sql_doc`] : Creates the [`SqlDoc`] structure
2+
//!
3+
//! ## Module layout
4+
//!
5+
//! - [`files`] — Discover and load `.sql` files from disk
6+
//! - [`ast`] — Parse SQL into an AST using [`sqlparser`]
7+
//! - [`comments`] — Extract and model SQL comments and spans
8+
//! - [`docs`] — Generate structured documentation (`TableDoc`, `ColumnDoc`)
9+
//! - [`sql_doc`] — Build the top-level [`SqlDoc`] and primary entry point
10+
//!
11+
//! **Start here:** [`SqlDoc::from_dir`] or [`SqlDoc::from_path`]
812
913
pub mod ast;
1014
pub mod comments;

src/sql_doc.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Module for the top level `SqlDoc` structure.
1+
//! Public entry point for building [`SqlDoc`] from a directory, file, or string.
22
33
use std::path::{Path, PathBuf};
44

@@ -10,7 +10,7 @@ use crate::{
1010
files::{SqlFile, SqlFilesList},
1111
};
1212

13-
/// Structure for Sql Documentation, built from [`TableDoc`] and
13+
/// Top-level documentation object containing all discovered [`TableDoc`] entries.
1414
#[derive(Clone, Debug, Eq, PartialEq)]
1515
pub struct SqlDoc {
1616
/// Holds the [`Vec`] of all tables found in all specified files.
@@ -70,7 +70,7 @@ impl SqlDoc {
7070
}
7171
}
7272

73-
/// Method for generating builder from a [`str`]
73+
/// Creates a builder from SQL text (no filesystem path is associated) from a [`str`]
7474
#[must_use]
7575
pub const fn from_str(content: &str) -> SqlDocBuilder<'_> {
7676
SqlDocBuilder {
@@ -168,14 +168,17 @@ impl SqlDocBuilder<'_> {
168168
self.multiline_flat = MultiFlatten::Flatten(suffix.to_string());
169169
self
170170
}
171-
/// Method to set multiline comments to preserver multiple lines
171+
/// Method to set multiline comments to preserve multiple lines
172172
#[must_use]
173173
pub fn preserve_multiline(mut self) -> Self {
174174
self.multiline_flat = MultiFlatten::NoFlat;
175175
self
176176
}
177177
/// Builds the [`SqlDoc`]
178178
///
179+
///
180+
/// Comment flattening (if enabled) is applied as a post-processing step after docs are generated.
181+
///
179182
/// # Errors
180183
/// - Will return `DocError` bubbled up
181184
pub fn build(self) -> Result<SqlDoc, DocError> {

0 commit comments

Comments
 (0)