@@ -247,28 +247,34 @@ pub fn parseIntoField(
247
247
248
248
inline for (info .Struct .fields ) | field | {
249
249
if (field .name [0 ] != '_' and mem .eql (u8 , field .name , key )) {
250
+ // For optional fields, we just treat it as the child type.
251
+ // This lets optional fields default to null but get set by
252
+ // the CLI.
253
+ const Field = switch (@typeInfo (field .type )) {
254
+ .Optional = > | opt | opt .child ,
255
+ else = > field .type ,
256
+ };
257
+ const fieldInfo = @typeInfo (Field );
258
+ const canHaveDecls = fieldInfo == .Struct or fieldInfo == .Union or fieldInfo == .Enum ;
259
+
250
260
// If the value is empty string (set but empty string),
251
261
// then we reset the value to the default.
252
262
if (value ) | v | default : {
253
263
if (v .len != 0 ) break :default ;
264
+ // Set default value if possible.
265
+ if (canHaveDecls and @hasDecl (Field , "init" )) {
266
+ try @field (dst , field .name ).init (alloc );
267
+ return ;
268
+ }
254
269
const raw = field .default_value orelse break :default ;
255
270
const ptr : * const field.type = @alignCast (@ptrCast (raw ));
256
271
@field (dst , field .name ) = ptr .* ;
257
272
return ;
258
273
}
259
274
260
- // For optional fields, we just treat it as the child type.
261
- // This lets optional fields default to null but get set by
262
- // the CLI.
263
- const Field = switch (@typeInfo (field .type )) {
264
- .Optional = > | opt | opt .child ,
265
- else = > field .type ,
266
- };
267
-
268
275
// If we are a type that can have decls and have a parseCLI decl,
269
276
// we call that and use that to set the value.
270
- const fieldInfo = @typeInfo (Field );
271
- if (fieldInfo == .Struct or fieldInfo == .Union or fieldInfo == .Enum ) {
277
+ if (canHaveDecls ) {
272
278
if (@hasDecl (Field , "parseCLI" )) {
273
279
const fnInfo = @typeInfo (@TypeOf (Field .parseCLI )).Fn ;
274
280
switch (fnInfo .params .len ) {
@@ -761,6 +767,29 @@ test "parseIntoField: ignore underscore-prefixed fields" {
761
767
try testing .expectEqualStrings ("12" , data ._a );
762
768
}
763
769
770
+ test "parseIntoField: struct with init func" {
771
+ const testing = std .testing ;
772
+ var arena = ArenaAllocator .init (testing .allocator );
773
+ defer arena .deinit ();
774
+ const alloc = arena .allocator ();
775
+
776
+ var data : struct {
777
+ a : struct {
778
+ const Self = @This ();
779
+
780
+ v : []const u8 ,
781
+
782
+ pub fn init (self : * Self , _alloc : Allocator ) ! void {
783
+ _ = _alloc ;
784
+ self .* = .{ .v = "HELLO!" };
785
+ }
786
+ },
787
+ } = undefined ;
788
+
789
+ try parseIntoField (@TypeOf (data ), alloc , & data , "a" , "" );
790
+ try testing .expectEqual (@as ([]const u8 , "HELLO!" ), data .a .v );
791
+ }
792
+
764
793
test "parseIntoField: string" {
765
794
const testing = std .testing ;
766
795
var arena = ArenaAllocator .init (testing .allocator );
0 commit comments