1313 os,
1414 std/ [enumutils, options, strutils, wordwrap],
1515 stew/ shims/ macros,
16- confutils/ [defs, cli_parser, config_file]
16+ confutils/ [defs, cli_parser, config_file, utils ]
1717
1818export
1919 options, defs, config_file
@@ -693,24 +693,83 @@ template debugMacroResult(macroName: string) {.dirty.} =
693693 echo " \n -------- " , macroName, " ----------------------"
694694 echo result .repr
695695
696+ type
697+ ConfFieldDescRef = ref ConfFieldDesc
698+ ConfFieldDesc = object
699+ field: FieldDescription
700+ parent: ConfFieldDescRef
701+
702+ proc newConfFieldDesc (
703+ field: FieldDescription , parent: ConfFieldDescRef
704+ ): ConfFieldDescRef =
705+ ConfFieldDescRef (field: field, parent: parent)
706+
707+ proc fieldCaseBranch (cf: ConfFieldDesc ): NimNode =
708+ if cf.field.caseBranch != nil :
709+ cf.field.caseBranch
710+ elif cf.parent != nil :
711+ fieldCaseBranch (cf.parent[])
712+ else :
713+ nil
714+
715+ proc fieldCaseField (cf: ConfFieldDesc ): NimNode =
716+ if cf.field.caseField != nil :
717+ cf.field.caseField
718+ elif cf.parent != nil :
719+ fieldCaseField (cf.parent[])
720+ else :
721+ nil
722+
723+ proc confFields (typeImpl: NimNode , parent: ConfFieldDescRef = nil ): seq [ConfFieldDesc ] =
724+ result = newSeq [ConfFieldDesc ]()
725+ for field in recordFields (typeImpl):
726+ if field.readPragma " flatten" != nil :
727+ for cf in confFields (getImpl (field.typ), newConfFieldDesc (field, parent)):
728+ result .add cf
729+ else :
730+ result .add ConfFieldDesc (field: field, parent: parent)
731+
732+ proc genFieldDotExpr (cf: ConfFieldDesc ): NimNode =
733+ if cf.parent != nil :
734+ dotExpr (genFieldDotExpr (cf.parent[]), cf.field.name)
735+ else :
736+ cf.field.name
737+
738+ proc fullFieldName (cf: ConfFieldDesc ): string =
739+ if cf.parent != nil :
740+ $ fullFieldName (cf.parent[]) & " Dot" & $ cf.field.name
741+ else :
742+ $ cf.field.name
743+
744+ proc fieldCaseFieldFullName (cf: ConfFieldDesc ): string =
745+ if cf.field.caseField != nil :
746+ if cf.parent != nil :
747+ fullFieldName (cf.parent[]) & " Dot" & $ cf.field.caseField.getFieldName
748+ else :
749+ $ cf.field.caseField.getFieldName
750+ else :
751+ doAssert cf.parent != nil , " caseField not found"
752+ fieldCaseFieldFullName (cf.parent[])
753+
696754proc generateFieldSetters (RecordType: NimNode ): NimNode =
697755 var recordDef = getImpl (RecordType )
698756 let makeDefaultValue = bindSym " makeDefaultValue"
699757
700758 result = newTree (nnkStmtListExpr)
701759 var settersArray = newTree (nnkBracket)
702760
703- for field in recordFields (recordDef):
761+ for cf in confFields (recordDef):
762+ let field = cf.field
704763 var
705- setterName = ident ($ field.name & " Setter" )
764+ setterName = ident (cf. fullFieldName () & " Setter" )
706765 fieldName = field.name
707766 namePragma = field.readPragma " name"
708767 paramName = if namePragma != nil : namePragma
709768 else : fieldName
710769 configVar = ident " config"
711- configField = newTree (nnkDotExpr, configVar, fieldName )
770+ configField = dotExpr ( configVar, genFieldDotExpr (cf) )
712771 defaultValue = field.readPragma " defaultValue"
713- completerName = ident ($ field.name & " Complete" )
772+ completerName = ident (cf. fullFieldName () & " Complete" )
714773 isFieldDiscriminator = newLit field.isDiscriminator
715774
716775 if defaultValue == nil :
@@ -761,6 +820,8 @@ proc generateFieldSetters(RecordType: NimNode): NimNode =
761820 debugMacroResult " Field Setters"
762821
763822func checkDuplicate (cmd: CmdInfo , opt: OptInfo , fieldName: NimNode ) =
823+ if opt.kind == Discriminator and opt.isCommand:
824+ return
764825 for x in cmd.opts:
765826 if opt.name == x.name:
766827 error " duplicate name detected: " & opt.name, fieldName
@@ -795,11 +856,12 @@ proc cmdInfoFromType(T: NimNode): CmdInfo =
795856
796857 var
797858 recordDef = getImpl (T)
798- discriminatorFields = newSeq [OptInfo ]()
859+ discriminatorFields = newSeq [( string , OptInfo ) ]()
799860 fieldIdx = 0
800861
801- for field in recordFields (recordDef):
862+ for cf in confFields (recordDef):
802863 let
864+ field = cf.field
803865 isImplicitlySelectable = field.readPragma " implicitlySelectable" != nil
804866 defaultValue = field.readPragma " defaultValue"
805867 defaultValueDesc = field.readPragma " defaultValueDesc"
@@ -834,7 +896,7 @@ proc cmdInfoFromType(T: NimNode): CmdInfo =
834896 inc fieldIdx
835897
836898 if field.isDiscriminator:
837- discriminatorFields.add opt
899+ discriminatorFields.add (cf. fullFieldName (), opt)
838900 let cmdType = field.typ.getImpl[^ 1 ]
839901 if cmdType.kind != nnkEnumTy:
840902 error " Only enums are supported as case object discriminators" , field.name
@@ -860,18 +922,23 @@ proc cmdInfoFromType(T: NimNode): CmdInfo =
860922 if opt.defaultSubCmd == - 1 :
861923 error " The default value is not a valid enum value" , defaultValue
862924
863- if field.caseField != nil and field.caseBranch != nil :
864- let fieldName = field.caseField.getFieldName
865- var discriminator = findOpt (discriminatorFields, $ fieldName)
925+ let caseField = cf.fieldCaseField ()
926+ let caseBranch = cf.fieldCaseBranch ()
927+ if caseField != nil and caseBranch != nil :
928+ let fieldName = cf.fieldCaseFieldFullName ()
929+ var discriminator: OptInfo
930+ for (name, opt) in discriminatorFields:
931+ if fieldName == name:
932+ discriminator = opt
866933
867934 if discriminator == nil :
868- error " Unable to find " & $ fieldName
935+ error " Unable to find " & $ caseField.getFieldName
869936
870- if field. caseBranch.kind == nnkElse:
937+ if caseBranch.kind == nnkElse:
871938 error " Sub-command parameters cannot appear in an else branch. " &
872- " Please specify the sub-command branch precisely" , field. caseBranch[0 ]
939+ " Please specify the sub-command branch precisely" , caseBranch[0 ]
873940
874- var branchEnumVal = field. caseBranch[0 ]
941+ var branchEnumVal = caseBranch[0 ]
875942 if branchEnumVal.kind == nnkDotExpr:
876943 branchEnumVal = branchEnumVal[1 ]
877944 var cmd = findCmd (discriminator.subCmds, $ branchEnumVal)
@@ -899,6 +966,8 @@ macro configurationRtti(RecordType: type): untyped =
899966
900967 result = newTree (nnkPar, newLitFixed cmdInfo, fieldSetters)
901968
969+ debugMacroResult " configurationRtti"
970+
902971when hasSerialization:
903972 proc addConfigFile * (secondarySources: auto ,
904973 Format: type ,
@@ -1331,4 +1400,25 @@ func load*(f: TypedInputFile): f.ContentType =
13311400 mixin loadFile
13321401 loadFile (f.Format , f.string , f.ContentType )
13331402
1403+ proc flattenedAccessorsImpl (RecordType: NimNode ): NimNode =
1404+ result = newTree (nnkStmtListExpr)
1405+ let recordDef = getImpl (RecordType .getType[1 ])
1406+ for cf in confFields (recordDef):
1407+ if cf.parent != nil :
1408+ let
1409+ configVar = ident " config"
1410+ configField = dotExpr (configVar, genFieldDotExpr (cf))
1411+ accessorName = if cf.field.isPublic:
1412+ newTree (nnkPostfix, ident (" *" ), cf.field.name)
1413+ else :
1414+ ident $ cf.field.name
1415+ result .add quote do :
1416+ template `accessorName` (`configVar`: `RecordType`): untyped =
1417+ `configField`
1418+
1419+ debugMacroResult " Flattened Accessors"
1420+
1421+ macro flattenedAccessors * (Configuration: type , public = false ): untyped =
1422+ flattenedAccessorsImpl (Configuration )
1423+
13341424{.pop .}
0 commit comments