aboutsummaryrefslogtreecommitdiff
path: root/nimrec/recordset.nim
blob: 9e6d90cb2ee4ed8002e0be831a0e065d92f73625 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import parser
import record
import utils

import sequtils
import sets
import streams
import strutils
import tables
import times

type
    RecordSet* = ref object
        kind*: string
        doc*: string
        mandatory*: HashSet[string]
        allowed*: HashSet[string]
        prohibited*: HashSet[string]

    IntegrityError* = object of Exception

proc newRecordSet*(record: Record): RecordSet =
    new(result)

    init(result.mandatory)
    init(result.prohibited)
    init(result.allowed)

    result.kind = record["%rec"]

    if "%doc" in record:
        result.doc = record["%doc"]

    for value in values(record, "%mandatory"):
        for field in split(value):
            incl(result.mandatory, field)
            incl(result.allowed, field)
            if field in result.prohibited:
                raise newException(Exception, "a field cannot be mandatory and prohibited at the same time")

    for value in values(record, "%prohibit"):
        for field in split(value):
            if field in result.mandatory:
                raise newException(Exception, "a field cannot be mandatory and prohibited at the same time")
            if field in result.allowed:
                raise newException(Exception, "a field cannot be allowed and prohibited at the same time")
            incl(result.prohibited, field)

    for value in values(record, "%allowed"):
        for field in split(value):
            if field in result.prohibited:
                raise newException(Exception, "a field cannot be allowed and prohibited at the same time")
            incl(result.allowed, field)

proc validate*(record: Record, recordSet: RecordSet) =
    let labels = toSet(toSeq(labels(record)))

    for label in labels(record):
        if len(recordSet.allowed) > 0:
            if label notin recordSet.allowed:
                raise newException(IntegrityError, format("not allowed: $1", label))

        if label in recordSet.prohibited:
            raise newException(IntegrityError, format("prohibited: $1", label))

    for mandatoryLabel in recordSet.mandatory:
        if mandatoryLabel notin labels:
            raise newException(IntegrityError, format("mandatory: $1",
                mandatoryLabel))

iterator recordsInSet*(stream: Stream, kind: string): Record =
    var
        currentSet: RecordSet = nil

    for record in records(stream):
        if currentSet == nil:
            if "%rec" in record and record["%rec"] == kind:
                currentSet = newRecordSet(record)
            continue
        else:
            if "%rec" in record and record["%rec"] == kind:
                currentSet = newRecordSet(record)
                continue
            elif "%rec" in record:
                currentSet = nil
                continue

        validate(record, currentSet)

        yield record