/ chronicles / formats.nim
formats.nim
 1  ## Module that separately can be imported by projects wanting to provide custom
 2  ## chronicles formatting for their types without importing the full chronicles
 3  ## library.
 4  
 5  import std/strformat, stew/shims/macros
 6  
 7  template chroniclesFormatItIMPL*(value: auto): auto =
 8    # By default, values are passed as-is to the log output
 9    value
10  
11  template formatIt*(T: type, body: untyped) {.dirty.} =
12    template chroniclesFormatItIMPL*(it: T): auto =
13      body
14  
15  # enabled: SinksBitMask
16  template chroniclesExpandItIMPL*[RecordType: tuple](
17      record: RecordType, field: static string, value: auto, enabled: auto
18  ) =
19    mixin setProperty, chroniclesFormatItIMPL
20    setProperty(record, field, chroniclesFormatItIMPL(value), enabled)
21  
22  template chroniclesExpandItIMPL*[RecordType](
23      record: RecordType, field: static string, value: auto
24  ) =
25    mixin setProperty, chroniclesFormatItIMPL
26    setProperty(record, field, chroniclesFormatItIMPL(value))
27  
28  macro expandIt*(T: type, expandedProps: untyped): untyped =
29    let
30      chroniclesFormatItIMPL = bindSym("chroniclesFormatItIMPL", brForceOpen)
31      record = ident "record"
32      it = ident "it"
33      it_name = ident "it_name"
34      enabled = ident "enabled"
35      setPropertyTupleCalls = newStmtList()
36      setPropertyCalls = newStmtList()
37  
38    for prop in expandedProps:
39      if prop.kind != nnkAsgn:
40        error "An `expandIt` definition should consist only of key-value assignments",
41          prop
42  
43      var key = prop[0]
44      let value = prop[1]
45      case key.kind
46      of nnkAccQuoted:
47        proc toStrLit(n: NimNode): NimNode =
48          let nAsStr = $n
49          if nAsStr == "it":
50            it_name
51          else:
52            newLit(nAsStr)
53  
54        if key.len < 2:
55          key = key.toStrLit
56        else:
57          var concatCall = infix(key[0].toStrLit, "&", key[1].toStrLit)
58          for i in 2 ..< key.len:
59            concatCall = infix(concatCall, "&", key[i].toStrLit)
60          key = newTree(nnkStaticExpr, concatCall)
61      of nnkIdent, nnkSym:
62        key = newLit($key)
63      else:
64        error &"Unexpected AST kind for an `expandIt` key: {key.kind} ", key
65  
66      setPropertyCalls.add newCall(
67        "setProperty", record, key, newCall(chroniclesFormatItIMPL, value)
68      )
69      setPropertyTupleCalls.add newCall(
70        "setProperty", record, key, newCall(chroniclesFormatItIMPL, value), enabled
71      )
72  
73    # Both single- and multisink expanders are added here - the tradeoff would be
74    # to import ./options and check if runtime filtering is enabled and skip the
75    # latter if not
76    result = quote:
77      template chroniclesExpandItIMPL*[RecordType: tuple](
78          `record`: RecordType, `it_name`: static string, `it`: `T`, `enabled`: auto
79      ) =
80        mixin setProperty, chroniclesFormatItIMPL
81        `setPropertyTupleCalls`
82  
83      template chroniclesExpandItIMPL*(
84          `record`: auto, `it_name`: static string, `it`: `T`
85      ) =
86        mixin setProperty, chroniclesFormatItIMPL
87        `setPropertyCalls`
88  
89    when defined(debugLogImpl):
90      echo result.repr