You can also go crazy and get rid of even more boiler plate (I've added types as well): # env.nim import macros, tables import std/envvars, std/options export envvars, options type EnvVarInfo = tuple[envType: string, envBody: NimNode] # Store all of the compile time info var envTable {.compileTime.}: TableRef[string, EnvVarInfo] = newTable[string, EnvVarInfo]() macro defineEnv*(fieldName, fieldType, body: untyped): untyped = # create the new field name let newName = ident("m_" & fieldName.strVal) # body of our code let body = quote do: # Use fieldName and fieldType as per needed. # Also, inject Env into the body code so that it can be # referenced inside the body of the env code with the envs # that were already declared proc `fieldName`*(Env {.inject.}: var EnvType): `fieldType` = if isNone(Env.`newName`): result = `body` Env.`newName` = some result else: result = Env.`newName`.get envTable[newName.strVal] = (fieldType.strVal, body) proc createEnvTypeField(envName: string, envType: string): NimNode = result = nnkIdentDefs.newTree( ident(envName), nnkBracketExpr.newTree( ident("Option"), ident(envType) ), newEmptyNode() ) macro buildEnvType*(): untyped = # build the env type and all of the procs and the var declaration let recList = nnkRecList.newNimNode() result = nnkStmtList.newTree() let envTy = nnkTypeSection.newTree( nnkTypeDef.newTree( ident("EnvType"), newEmptyNode(), nnkObjectTy.newTree( newEmptyNode(), newEmptyNode(), recList ) ) ) # add the type before the procs result.add(envTy) for envName, env in envTable.pairs: recList.add(createEnvTypeField(envName, env.envType)) result.add(env.envBody) # This is needed so that nim makes the var name # exactly as spelled and doesn't add any hash values # to the name. This makes it able to be referenced outside of # the macro let envVarName = ident("Env") result.add quote do: var `envVarName`*: EnvType Run
Usage: # yourfile.nim import env defineEnv user, string: "USER".getEnv defineEnv icloud_dir, string: "/Users/" & Env.user & "/Library/CloudStorage/Box-Box" defineEnv num_things, int: 42 # Must call this AFTER all defineEnv calls buildEnvType() echo Env.user echo Env.icloud_dir echo Env.num_things Run