I'm not 100% sure if I understand you correctly. This is what seems closest to
what I understand you have in mind. Note the comments I added:
import std/envvars, std/options
type
EnvType = object
m_user: Option[string]
m_icloud_dir: Option[string]
var Env*: EnvType
## - `prcname` is the name of the produced helper procedure
## - `name` is the field name of the `EnvType`
## - `body` is the code inserted for the `isNone` `result = body`
## Note:
## - assumes all types are of field `Option[string]` (one could either pass
a
## type or make it much more complicated by trying to determine the type
from
## the `EnvType` (but that is problematic, because it would need to be a
`typed` macro
## which clashes with using free identifiers)
## - the `{.inject.}` is used to make the `env` available in the body of
the template
## - I switched away from a `do` approach and instead just use a block body
template define_env(prcname, name, body: untyped): untyped =
proc `prcname`*(env {.inject.}: var EnvType): string =
if isNone(env.name):
result = body
env.name = some result
else:
result = env.name.get
define_env(user, m_user): "USER".getenv
define_env(icloud_dir, m_icloud_dir): "/Users/" & env.user &
"/Library/CloudStorage/Box-Box"
when isMainModule:
echo Env.user
echo Env.icloud_dir
Run