summaryrefslogtreecommitdiff
path: root/doc/execution
blob: 9b8959092450ab9bb37f0c00f972aabb37c3fc41 (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
91
92
93
Execution of Lace rulesets
==========================

Once compiled, a ruleset is essentially a sequence of functions to
call on the execution context.  The simplest execution context is an
empty table.  If Lace is going to store anything it will use a "_lace"
prefix as with compilation contexts.

A few important functions make up the execution engine.  The top level
function is simply:

    result, msg = lace.engine.run(ruleset, exec_context)

This will run the ruleset with the given execution context and return
a simple result.

If the result is nil, then msg is a long-form string error explaining
what went wrong.  It represents a Lua error being caught and as such
you may not want to report it to your users.

If the result is false, then msg is a long-form string error
explaining that something returned an error during execution which it
would be reasonable to report to users.

If the result is "allow", then msg is an optional string saying why
the ruleset resulted in an allow.  Ditto for "deny".  Essentially any
string might be a reason.  This is covered below in Commands.

Commands
========

When a command is being run, it is called as:

    result, msg = command_fn(exec_context, unpack(args))

where args are the arguments it returned when being compiled.

If the function throws an error, that will be caught and processed by
the execution engine.

If result is falsehood (nil, false) then the command is considered to
have failed for some reason and msg contains an "internal" error
message to report to the user.  This aborts the execution of the
ruleset.

If result is true, then the command successfully ran, and execution
continues at the next rule.

If result is a string, then the command returned a result.  This
ceases execution of the ruleset and the result and message (which must
be a string explanation) are returned to the caller.  Typically such
results would be "allow" or "deny" although there's nothing forcing
that to be the case.

Control Types
=============

When a control type function is being run, it is called as:

    result, msg = ct_fn(exec_context, unpack(args))

where args are the arguments it returned when being compiled.

If the function throws an error, it will be caught and processed by
the execution engine.

If result is nil then msg is an "internal" error, execution will be
stopped and the issue reported to the caller.

If result is false, the control call failed and returned falsehood.
Anything else and the control call succeeded and returns truth.

Control type functions are called at the point of test, not at the
point of definition.  Control type results are *NOT* cached.  It is up
to the called functions to perform any caching/memoising of results as
needed to ensure suitably performant behaviour.

Helper functions
================

Since sometimes you need to know if a given define rule passes, Lace
provides a function to do this.  It is bound up in the behaviour of
Lace's internal 'define' command and as such, you should treat it as a
black box.

    result, msg = lace.engine.test(exec_context, name)

This, via the magic of the execution context calls through to the
appropriate control type functions, returning their results directly.

This means that it can throw an error in the case of a Lua error,
otherwise it returns the two values as above.