Skip to content

Commit 9abcce9

Browse files
committed
enforced sorting at creation for ColumnDoc and TableDoc
1 parent 2bf380a commit 9abcce9

4 files changed

Lines changed: 47 additions & 36 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 = "1.0.5"
3+
version = "1.0.6"
44
edition = "2024"
55
description = "A crate for parsing comments from sql files and using them for documentation generation"
66
documentation = "https://docs.rs/sql_docs"

src/docs.rs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ pub struct TableDoc {
6868
}
6969

7070
impl TableDoc {
71-
/// Creates a new [`TableDoc`] after sorting [`ColumnDoc`] by name
71+
/// Creates a new [`TableDoc`] after sorting [`ColumnDoc`] by `name`
7272
///
7373
/// # Parameters
7474
/// - name: `String` - the name of the table
@@ -83,7 +83,7 @@ impl TableDoc {
8383
mut columns: Vec<ColumnDoc>,
8484
path: Option<PathBuf>,
8585
) -> Self {
86-
columns.sort_by(|a,b| a.name().cmp(b.name()));
86+
columns.sort_by(|a, b| a.name().cmp(b.name()));
8787
Self { schema, name, doc, columns, path }
8888
}
8989

@@ -649,35 +649,41 @@ CREATE TABLE posts (
649649
fn test_doc() {
650650
let col_doc = ColumnDoc::new("test".to_owned(), Some("comment".to_owned()));
651651
assert_eq!(&col_doc.to_string(), &"Column Name: test\nColumn Doc: comment\n".to_owned());
652-
let col_doc_no_doc = ColumnDoc::new("test".to_owned(), None);
652+
let col_doc_no_doc = ColumnDoc::new("id".to_owned(), None);
653653
assert_eq!(
654654
&col_doc_no_doc.to_string(),
655-
&"Column Name: test\nNo Column Doc Found\n".to_owned()
655+
&"Column Name: id\nNo Column Doc Found\n".to_owned()
656656
);
657657
assert_eq!(col_doc.doc(), Some("comment"));
658658
assert_eq!(col_doc.name(), "test");
659659
assert_eq!(col_doc_no_doc.doc(), None);
660-
assert_eq!(col_doc_no_doc.name(), "test");
660+
assert_eq!(col_doc_no_doc.name(), "id");
661661
let table_doc = TableDoc::new(
662662
Some("schema".to_owned()),
663663
"table".to_owned(),
664664
Some("table doc".to_owned()),
665-
vec![col_doc],
665+
vec![col_doc.clone(), col_doc_no_doc.clone()],
666+
None,
667+
);
668+
let last_col = ColumnDoc::new("zed".to_owned(), Some("the last column".to_owned()));
669+
let table_doc_no_doc = TableDoc::new(
670+
None,
671+
"table".to_owned(),
672+
None,
673+
vec![last_col, col_doc, col_doc_no_doc],
666674
None,
667675
);
668-
let table_doc_no_doc =
669-
TableDoc::new(None, "table".to_owned(), None, vec![col_doc_no_doc], None);
670676
assert_eq!(table_doc.name(), "table");
671677
assert_eq!(table_doc.schema(), Some("schema"));
672678
assert_eq!(
673679
table_doc.to_string(),
674-
"Table Schema: schema\nTable Name: table\nTable Doc: table doc\nTable Column Docs: \n Column Name: test\nColumn Doc: comment\n"
680+
"Table Schema: schema\nTable Name: table\nTable Doc: table doc\nTable Column Docs: \n Column Name: id\nNo Column Doc Found\n Column Name: test\nColumn Doc: comment\n"
675681
);
676682
assert_eq!(table_doc_no_doc.schema(), None);
677683
assert_eq!(table_doc_no_doc.name(), "table");
678684
assert_eq!(
679685
table_doc_no_doc.to_string(),
680-
"No Table Schema\nTable Name: table\nNo Table Doc\nTable Column Docs: \n Column Name: test\nNo Column Doc Found\n"
686+
"No Table Schema\nTable Name: table\nNo Table Doc\nTable Column Docs: \n Column Name: id\nNo Column Doc Found\n Column Name: test\nColumn Doc: comment\n Column Name: zed\nColumn Doc: the last column\n"
681687
);
682688
}
683689

@@ -770,7 +776,7 @@ CREATE TABLE posts (
770776
}
771777

772778
#[test]
773-
fn display_propagates_every_question_mark_path_for_column_and_table() {
779+
fn test_display_propagates_every_question_mark_path_for_column_and_table() {
774780
let col_with_doc = ColumnDoc::new("col_a".into(), Some("doc".into()));
775781
let col_without_doc = ColumnDoc::new("col_b".into(), None);
776782

src/sql_doc.rs

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,11 @@ enum SqlFileDocSource<'a> {
5454
impl SqlDoc {
5555
/// Method for creating a new [`SqlDoc`]
5656
#[must_use]
57-
pub const fn new(tables: Vec<TableDoc>) -> Self {
57+
pub fn new(mut tables: Vec<TableDoc>) -> Self {
58+
tables.sort_by(|a, b| a.name().cmp(b.name()));
5859
Self { tables }
5960
}
61+
6062
/// Method for generating builder from a directory.
6163
pub fn from_dir<P: AsRef<Path>>(root: &P) -> SqlDocBuilder<'_> {
6264
SqlDocBuilder {
@@ -102,20 +104,11 @@ impl SqlDoc {
102104
///
103105
/// # Errors
104106
/// - Will return [`DocError::TableNotFound`] if the expected table is not found
105-
/// - Will return [`DocError::DuplicateTablesFound`] if more than one table is found
106-
pub fn table(&self, table: &str) -> Result<&TableDoc, DocError> {
107-
let matches = self
108-
.tables
109-
.iter()
110-
.filter(|table_doc| table_doc.name() == table)
111-
.collect::<Vec<&TableDoc>>();
112-
match matches.as_slice() {
113-
[] => Err(DocError::TableNotFound { name: table.to_owned() }),
114-
[only] => Ok(*only),
115-
_ => Err(DocError::DuplicateTablesFound {
116-
tables: matches.into_iter().cloned().collect(),
117-
}),
118-
}
107+
pub fn table(&self, name: &str) -> Result<&TableDoc, DocError> {
108+
self.tables().binary_search_by(|t| t.name().cmp(name)).map_or_else(
109+
|_| Err(DocError::TableNotFound { name: name.to_owned() }),
110+
|id| Ok(&self.tables()[id]),
111+
)
119112
}
120113

121114
/// Method for finding a specific [`TableDoc`] from `schema` and table `name`
@@ -222,7 +215,7 @@ impl SqlDocBuilder<'_> {
222215
for sql_doc in docs {
223216
tables.extend(sql_doc);
224217
}
225-
let mut sql_doc = SqlDoc { tables };
218+
let mut sql_doc = SqlDoc::new(tables);
226219
match self.multiline_flat {
227220
MultiFlatten::FlattenWithNone => {
228221
sql_doc = flatten_docs(sql_doc, None);
@@ -334,6 +327,11 @@ mod tests {
334327
stamp_table_paths(&mut expected_tables, &file);
335328
let expected_doc = SqlDoc::new(expected_tables);
336329
assert_eq!(sql_doc, expected_doc);
330+
let names: Vec<&str> =
331+
sql_doc.tables().iter().map(super::super::docs::TableDoc::name).collect();
332+
let mut sorted = names.clone();
333+
sorted.sort_unstable();
334+
assert_eq!(names, sorted, "tables should be in alphabetical order");
337335
let _ = fs::remove_dir_all(&base);
338336
Ok(())
339337
}
@@ -396,12 +394,6 @@ mod tests {
396394
empty_table_err,
397395
Err(DocError::TableNotFound { name }) if name == "name"
398396
));
399-
let duplicate_set = SqlDoc::new(vec![
400-
TableDoc::new(None, "duplicate".to_owned(), None, vec![], None),
401-
TableDoc::new(None, "duplicate".to_owned(), None, vec![], None),
402-
]);
403-
let duplicate_tables_err = duplicate_set.table("duplicate");
404-
assert!(matches!(duplicate_tables_err, Err(DocError::DuplicateTablesFound { .. })));
405397
}
406398

407399
#[test]
@@ -720,7 +712,7 @@ mod tests {
720712
}
721713

722714
#[test]
723-
fn test_fromstr_parse_sql_doc() -> Result<(), Box<dyn std::error::Error>> {
715+
fn test_from_str_parse_sql_doc() -> Result<(), Box<dyn std::error::Error>> {
724716
let doc: SqlDoc = "CREATE TABLE t(id INTEGER);".parse()?;
725717
assert_eq!(doc.tables().len(), 1);
726718
Ok(())
@@ -764,4 +756,17 @@ mod tests {
764756
let _ = fs::remove_dir_all(&base);
765757
Ok(())
766758
}
759+
760+
#[test]
761+
fn test_tables_binary_searchable_by_name() {
762+
let sample = sample_sql();
763+
let tables: Vec<TableDoc> =
764+
sample.into_iter().flat_map(|(_, doc)| doc.into_tables()).collect();
765+
let sql_doc = SqlDoc::new(tables);
766+
let id = sql_doc
767+
.tables()
768+
.binary_search_by(|t| t.name().cmp("users"))
769+
.unwrap_or_else(|_| panic!("expected to find table `users` via binary search"));
770+
assert_eq!(sql_doc.tables()[id].name(), "users");
771+
}
767772
}

0 commit comments

Comments
 (0)