@@ -28,6 +28,19 @@ pub struct SqlDocBuilder {
2828 deny : Vec < String > ,
2929 /// Used to indicate maintaining the `[(PathBuf, SqlFileDoc)]`
3030 retain_files : bool ,
31+ /// Struct for tracking the settings for flattening multiline comments
32+ multiline_flat : MultiFlatten ,
33+ }
34+
35+ /// Enum for multiline comment flattening.
36+ #[ derive( Debug , Eq , PartialEq ) ]
37+ pub enum MultiFlatten {
38+ /// Default option, retains multiline structure with `\n`
39+ NoFlat ,
40+ /// Sets multiline comments to be flattened and combined without adding formatting
41+ FlattenWithNone ,
42+ /// Will flatten comments and amend the content of [`String`] to the end of the former leading lines
43+ Flatten ( String ) ,
3144}
3245
3346/// Enum for specifying a file doc source as a `directory` or a specific `file`
@@ -49,6 +62,7 @@ impl SqlDoc {
4962 source : SqlFileDocSource :: Dir ( root. as_ref ( ) . to_path_buf ( ) ) ,
5063 deny : Vec :: new ( ) ,
5164 retain_files : false ,
65+ multiline_flat : MultiFlatten :: NoFlat ,
5266 }
5367 }
5468 /// Method for generating builder from a [`Path`] of a single file
@@ -57,6 +71,7 @@ impl SqlDoc {
5771 source : SqlFileDocSource :: File ( path. as_ref ( ) . to_path_buf ( ) ) ,
5872 deny : Vec :: new ( ) ,
5973 retain_files : false ,
74+ multiline_flat : MultiFlatten :: NoFlat ,
6075 }
6176 }
6277
@@ -112,6 +127,11 @@ impl SqlDoc {
112127 pub fn tables ( & self ) -> & [ TableDoc ] {
113128 & self . tables
114129 }
130+ /// Getter that returns a mutable reference to the [`TableDoc`]
131+ #[ must_use]
132+ pub fn tables_mut ( & mut self ) -> & mut [ TableDoc ] {
133+ & mut self . tables
134+ }
115135 /// Method to move tables out of Structure if needed
116136 #[ must_use]
117137 pub fn into_tables ( self ) -> Vec < TableDoc > {
@@ -142,6 +162,26 @@ impl SqlDocBuilder {
142162 self
143163 }
144164
165+ /// Method for flattening the multiline comments without additional formatting
166+ #[ must_use]
167+ pub fn flatten_multiline ( mut self ) -> Self {
168+ self . multiline_flat = MultiFlatten :: FlattenWithNone ;
169+ self
170+ }
171+
172+ /// Method for flattening the multiline comments with [`String`] containing additional leading line formatting to add, such as punctuation.
173+ #[ must_use]
174+ pub fn flatten_multiline_with ( mut self , suffix : & str ) -> Self {
175+ self . multiline_flat = MultiFlatten :: Flatten ( suffix. to_string ( ) ) ;
176+ self
177+ }
178+ /// Method to set multiline comments to preserver multiple lines
179+ #[ must_use]
180+ pub fn preserve_multiline ( mut self ) -> Self {
181+ self . multiline_flat = MultiFlatten :: NoFlat ;
182+ self
183+ }
184+
145185 /// Builds the [`SqlDoc`]
146186 ///
147187 /// # Errors
@@ -156,21 +196,60 @@ impl SqlDocBuilder {
156196 }
157197 } ;
158198 let mut tables = Vec :: new ( ) ;
159- if self . retain_files {
199+ let mut sql_doc = if self . retain_files {
160200 let files = docs;
161201 for ( _, sql_doc) in & files {
162202 tables. extend ( sql_doc. tables ( ) . iter ( ) . cloned ( ) ) ;
163203 }
164- Ok ( SqlDoc { tables, files : Some ( files) } )
204+ SqlDoc { tables, files : Some ( files) }
165205 } else {
166206 for ( _, sql_doc) in & docs {
167207 tables. extend ( sql_doc. tables ( ) . iter ( ) . cloned ( ) ) ;
168208 }
169- Ok ( SqlDoc { tables, files : None } )
209+ SqlDoc { tables, files : None }
210+ } ;
211+ match self . multiline_flat {
212+ MultiFlatten :: FlattenWithNone => {
213+ sql_doc = flatten_docs ( sql_doc, None ) ;
214+ }
215+ MultiFlatten :: Flatten ( flat_format) => {
216+ sql_doc = flatten_docs ( sql_doc, Some ( & flat_format) ) ;
217+ }
218+ MultiFlatten :: NoFlat => { }
170219 }
220+ Ok ( sql_doc)
171221 }
172222}
173223
224+ fn flatten_docs ( mut sql_doc : SqlDoc , flat_format : Option < & str > ) -> SqlDoc {
225+ let format = flat_format. map_or ( "" , |c| c) ;
226+ for table in sql_doc. tables_mut ( ) {
227+ if let Some ( doc) = table. doc ( ) {
228+ let new_doc = flatten_lines ( doc. lines ( ) , format) ;
229+ table. set_doc ( new_doc) ;
230+ }
231+ for column in table. columns_mut ( ) {
232+ if let Some ( doc) = column. doc ( ) {
233+ let new_doc = flatten_lines ( doc. lines ( ) , format) ;
234+ column. set_doc ( new_doc) ;
235+ }
236+ }
237+ }
238+
239+ sql_doc
240+ }
241+
242+ fn flatten_lines < ' a > ( lines : impl Iterator < Item = & ' a str > , sep : & str ) -> String {
243+ let mut out = String :: new ( ) ;
244+ for ( i, line) in lines. enumerate ( ) {
245+ if i > 0 {
246+ out. push_str ( sep) ;
247+ }
248+ out. push_str ( line) ;
249+ }
250+ out
251+ }
252+
174253fn generate_docs_from_dir < P : AsRef < Path > , S : AsRef < str > > (
175254 source : P ,
176255 deny : & [ S ] ,
@@ -206,7 +285,7 @@ mod tests {
206285 SqlDoc ,
207286 docs:: { ColumnDoc , SqlFileDoc , TableDoc } ,
208287 error:: DocError ,
209- sql_doc:: SqlDocBuilder ,
288+ sql_doc:: { MultiFlatten , SqlDocBuilder } ,
210289 } ;
211290
212291 #[ test]
@@ -446,6 +525,7 @@ mod tests {
446525 source : crate :: sql_doc:: SqlFileDocSource :: File ( PathBuf :: from ( "path" ) ) ,
447526 deny : vec ! [ "path1" . to_string( ) , "path2" . to_string( ) ] ,
448527 retain_files : true ,
528+ multiline_flat : MultiFlatten :: NoFlat ,
449529 } ;
450530 assert_eq ! ( actual_builder, expected_builder) ;
451531 }
@@ -476,4 +556,138 @@ mod tests {
476556 let _ = fs:: remove_dir_all ( & base) ;
477557 Ok ( ( ) )
478558 }
559+
560+ #[ test]
561+ fn test_builder_multiflatten_variants ( ) {
562+ let b1 = SqlDoc :: from_path ( "dummy.sql" ) ;
563+ let b2 = SqlDoc :: from_path ( "dummy.sql" ) . flatten_multiline ( ) ;
564+ let b3 = SqlDoc :: from_path ( "dummy.sql" ) . flatten_multiline_with ( " . " ) ;
565+ let b4 = SqlDoc :: from_path ( "dummy.sql" ) . flatten_multiline_with ( "--" ) . preserve_multiline ( ) ;
566+ assert ! ( matches!( b1, SqlDocBuilder { multiline_flat: MultiFlatten :: NoFlat , .. } ) ) ;
567+ assert ! ( matches!( b2, SqlDocBuilder { multiline_flat: MultiFlatten :: FlattenWithNone , .. } ) ) ;
568+ assert ! (
569+ matches!( b3, SqlDocBuilder { multiline_flat: MultiFlatten :: Flatten ( s) , .. } if s == " . " )
570+ ) ;
571+ assert ! ( matches!( b4, SqlDocBuilder { multiline_flat: MultiFlatten :: NoFlat , .. } ) ) ;
572+ }
573+
574+ #[ test]
575+ fn test_flatten_lines_behavior ( ) {
576+ use crate :: sql_doc:: flatten_lines;
577+ let input = vec ! [ "a" , "b" , "c" ] ;
578+ let no_sep = flatten_lines ( input. clone ( ) . into_iter ( ) , "" ) ;
579+ assert_eq ! ( no_sep, "abc" ) ;
580+ let dash_sep = flatten_lines ( input. clone ( ) . into_iter ( ) , " - " ) ;
581+ assert_eq ! ( dash_sep, "a - b - c" ) ;
582+ let single = flatten_lines ( [ "solo" ] . into_iter ( ) , "XXX" ) ;
583+ assert_eq ! ( single, "solo" ) ;
584+ }
585+
586+ #[ test]
587+ fn test_flatten_docs_no_flatter ( ) {
588+ let table = TableDoc :: new (
589+ None ,
590+ "t" . into ( ) ,
591+ Some ( "line1\n line2" . into ( ) ) ,
592+ vec ! [
593+ ColumnDoc :: new( "c1" . into( ) , Some ( "a\n b" . into( ) ) ) ,
594+ ColumnDoc :: new( "c2" . into( ) , None ) ,
595+ ] ,
596+ ) ;
597+ let flat_table = TableDoc :: new (
598+ None ,
599+ "t" . into ( ) ,
600+ Some ( "line1line2" . into ( ) ) ,
601+ vec ! [ ColumnDoc :: new( "c1" . into( ) , Some ( "ab" . into( ) ) ) , ColumnDoc :: new( "c2" . into( ) , None ) ] ,
602+ ) ;
603+
604+ let output = {
605+ use crate :: sql_doc:: flatten_docs;
606+ let original = SqlDoc :: new ( vec ! [ table] , None ) ;
607+ flatten_docs ( original, None )
608+ } ;
609+ assert_eq ! ( output. tables( ) , vec![ flat_table] , "NoFlat should not alter docs" ) ;
610+ }
611+
612+ #[ test]
613+ fn test_flatten_docs_flatten_no_separator ( ) {
614+ let table = TableDoc :: new (
615+ None ,
616+ "t" . into ( ) ,
617+ Some ( "A\n B\n C" . into ( ) ) ,
618+ vec ! [ ColumnDoc :: new( "c" . into( ) , Some ( "x\n y" . into( ) ) ) ] ,
619+ ) ;
620+ let doc = SqlDoc :: new ( vec ! [ table] , None ) ;
621+
622+ let out = {
623+ use crate :: sql_doc:: flatten_docs;
624+ flatten_docs ( doc, Some ( "" ) )
625+ } ;
626+
627+ let t = & out. tables ( ) [ 0 ] ;
628+
629+ assert_eq ! ( t. doc( ) , Some ( "ABC" ) ) ;
630+ assert_eq ! ( t. columns( ) [ 0 ] . doc( ) , Some ( "xy" ) ) ;
631+ }
632+
633+ #[ test]
634+ fn test_flatten_docs_flatten_with_separator ( ) {
635+ let table = TableDoc :: new (
636+ None ,
637+ "t" . into ( ) ,
638+ Some ( "hello\n world" . into ( ) ) ,
639+ vec ! [ ColumnDoc :: new( "c" . into( ) , Some ( "x\n y\n z" . into( ) ) ) ] ,
640+ ) ;
641+ let doc = SqlDoc :: new ( vec ! [ table] , None ) ;
642+
643+ let out = {
644+ use crate :: sql_doc:: flatten_docs;
645+ flatten_docs ( doc, Some ( " | " ) )
646+ } ;
647+
648+ let t = & out. tables ( ) [ 0 ] ;
649+
650+ assert_eq ! ( t. doc( ) , Some ( "hello | world" ) ) ;
651+ assert_eq ! ( t. columns( ) [ 0 ] . doc( ) , Some ( "x | y | z" ) ) ;
652+ }
653+
654+ #[ test]
655+ fn test_tables_mut_allows_modification ( ) {
656+ let mut sql_doc =
657+ SqlDoc :: new ( vec ! [ TableDoc :: new( None , "t" . into( ) , Some ( "old" . into( ) ) , vec![ ] ) ] , None ) ;
658+ for t in sql_doc. tables_mut ( ) {
659+ t. set_doc ( "new" ) ;
660+ }
661+ assert_eq ! ( sql_doc. tables( ) [ 0 ] . doc( ) , Some ( "new" ) ) ;
662+ }
663+
664+ #[ test]
665+ fn test_builder_build_with_flattening ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
666+ let base = env:: temp_dir ( ) . join ( "builder_flatten_test" ) ;
667+ let _ = fs:: remove_dir_all ( & base) ;
668+ fs:: create_dir_all ( & base) ?;
669+ let file = base. join ( "t.sql" ) ;
670+
671+ let sql = r"
672+ /* Table Doc line1
673+ line2 */
674+ CREATE TABLE things (
675+ /* col1
676+ doc */
677+ id INTEGER
678+ );
679+ " ;
680+
681+ fs:: write ( & file, sql) ?;
682+
683+ let built = SqlDoc :: from_path ( & file) . flatten_multiline_with ( " • " ) . build ( ) ?;
684+
685+ let t = & built. tables ( ) [ 0 ] ;
686+
687+ assert_eq ! ( t. doc( ) , Some ( "Table Doc line1 • line2" ) ) ;
688+ assert_eq ! ( t. columns( ) [ 0 ] . doc( ) , Some ( "col1 • doc" ) ) ;
689+
690+ let _ = fs:: remove_dir_all ( & base) ;
691+ Ok ( ( ) )
692+ }
479693}
0 commit comments