1+ //
2+ // main entrypoint
13// Owns process flow, input detection, loading, and top-level CLI behavior.
4+ //
5+
26pub fn main () ! void {
37 var gpa : std .heap .DebugAllocator (.{}) = .init ;
48 defer {
@@ -7,28 +11,27 @@ pub fn main() !void {
711 }
812 const alloc = gpa .allocator ();
913
10- if (main0 (alloc ) catch | err | switch (err ) {
11- error .BrokenPipe , error .WriteFailed = > {
12- std .process .exit (0 );
13- },
14- else = > return err ,
15- }) | fatal | {
16- defer fatal .deinit (alloc );
17- try fatal .print ();
18- flushPipe () catch | err | switch (err ) {
19- error .BrokenPipe , error .WriteFailed = > std .process .exit (0 ),
20- else = > return err ,
21- };
22- std .process .exit (1 );
23- }
24- flushPipe () catch | err | switch (err ) {
25- error .BrokenPipe , error .WriteFailed = > std .process .exit (0 ),
14+ var exit : u8 = 0 ;
15+ const fatal = main0 (alloc ) catch | err | switch (err ) {
16+ error .BrokenPipe , error .WriteFailed = > null ,
2617 else = > return err ,
2718 };
28- std .process .exit (0 );
19+ if (fatal ) | value | {
20+ defer value .deinit (alloc );
21+ try value .print ();
22+ exit = 1 ;
23+ }
24+
25+ util .stdout .flush () catch {};
26+ util .stderr .flush () catch {};
27+ std .process .exit (exit );
2928}
3029
30+ //
31+ // main0
3132// Run the CLI and return a printable failure when the command should fail.
33+ //
34+
3235fn main0 (alloc : std.mem.Allocator ) ! ? failure.Failure {
3336 // timer
3437 var total = try std .time .Timer .start ();
@@ -102,27 +105,27 @@ fn main0(alloc: std.mem.Allocator) !?failure.Failure {
102105 // input => data rows
103106 var data = load (alloc , config , input ) catch | err | {
104107 if (err == error .SqliteInvalidTable ) {
105- const path = config .filename orelse return err ;
106- var db = try sqlite .Sqlite .init (alloc , path );
108+ var db = try sqlite .Sqlite .init (alloc , config .filename .? );
107109 defer db .deinit ();
108110 return try failure .Failure .fromSqliteTableError (alloc , config .table , db .tables );
109111 }
110112 return failure .Failure .fromError (err ) orelse return err ;
111113 };
112- errdefer data .deinit (alloc );
113114
114115 // plug data headers into config, for validation
115116 config .bind (alloc , data .headers ()) catch | err | {
117+ const fatal = try failure .Failure .fromTableError (alloc , err , data .headers ());
118+ data .deinit (alloc );
116119 config .deinit (alloc );
117- return try failure . Failure . fromTableError ( alloc , err , data . headers ()) ;
120+ return fatal ;
118121 };
119122
120123 //
121124 // data => table
122125 //
123126
127+ // Hand off both config and data here; Table.init owns cleanup from this point on.
124128 const table = try Table .init (alloc , config , data );
125- data = .{ .rows = &.{} };
126129 defer table .deinit ();
127130 util .benchmark ("table.init" , timer .read ());
128131
@@ -141,21 +144,23 @@ fn main0(alloc: std.mem.Allocator) !?failure.Failure {
141144 return null ;
142145}
143146
147+ //
148+ // loading input data
149+ //
150+
144151// Load the configured input into table data, dispatching by detected format.
145152fn load (alloc : std.mem.Allocator , config : types.Config , input : std.fs.File ) ! Data {
146153 // typically we read the whole file into memory for processing. That won't
147- // work if we are using `sqlite3`, though
148- if (config .filename ) | path | {
149- if (detect .formatFromFilename (path ) == .sqlite ) {
150- var db = try sqlite .Sqlite .init (alloc , path );
151- defer db .deinit ();
152- return try db .load (config .table );
153- }
154+ // work if we are using `sqlite3`, though.
155+ if (try detect .isSqliteFile (alloc , config .filename , input )) {
156+ var db = try sqlite .Sqlite .init (alloc , config .filename .? );
157+ defer db .deinit ();
158+ return try db .load (config .table );
154159 }
155160
156- const input_bytes = try input .readToEndAlloc (alloc , std .math .maxInt (usize ));
157- defer alloc .free (input_bytes );
158- return try loadBytes (alloc , config , input_bytes );
161+ const bytes = try input .readToEndAlloc (alloc , std .math .maxInt (usize ));
162+ defer alloc .free (bytes );
163+ return try loadBytes (alloc , config , bytes );
159164}
160165
161166// Load in-memory bytes into table data using the existing text format loaders.
@@ -166,22 +171,23 @@ fn loadBytes(alloc: std.mem.Allocator, config: types.Config, bytes_in: []const u
166171 bytes = bytes [3.. ];
167172 }
168173
174+ // sqlite3 and stray --table
169175 const format = try detect .detectFormat (alloc , config .filename , bytes );
170-
171- // sqlite3 concerns
172176 if (format == .sqlite ) return error .SqliteRequiresFile ;
173177 if (config .table .len > 0 ) return error .SqliteTableRequiresSqlite ;
178+
179+ // json
174180 if (format == .json ) return try json .load (alloc , bytes );
175181
182+ // csv (our default)
176183 var delimiter = config .delimiter ;
177184 if (delimiter == 0 ) delimiter = sniffer .sniff (bytes ) orelse ',' ;
178185 return try csv .load (alloc , bytes , delimiter );
179186}
180187
181- fn flushPipe () anyerror ! void {
182- try util .stdout .flush ();
183- try util .stderr .flush ();
184- }
188+ //
189+ // rendering
190+ //
185191
186192fn renderToPager (alloc : std.mem.Allocator , config : types.Config , table : * Table ) ! void {
187193 const cmd = std .posix .getenv ("PAGER" ) orelse "less" ;
0 commit comments