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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
/******************************************************************************
__ ____ __
/ / ___ ____ _/ __/_____________ _/ /__
/ / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \
/ /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/
/_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/
(C)opyright 2025-2026, Leafscale, LLC - https://www.leafscale.com
Project: zyginit
Filename: condition.reef
Authors: Chris Tusa <chris.tusa@leafscale.com>
License: <see LICENSE file included with this source code>
Description: Evaluate [condition] blocks: exists_file, exists_file_any, command. Returns pass/fail + reason.
******************************************************************************/
module condition
import core.str as str
import io.file
import config
export
type ConditionResult
fn evaluate_conditions(def: config.ServiceDef): ConditionResult
fn cond_passed(r: ConditionResult): bool
fn cond_reason(r: ConditionResult): string
end export
type ConditionResult = struct
passed: bool
reason: string
end ConditionResult
extern "C" fn run_command_with_timeout(cmd: string, timeout_sec: int): int
// Sentinel return values from run_command_with_timeout.
fn TIMEOUT_RC(): int return 0 - 2 end TIMEOUT_RC
fn NOT_FOUND_RC(): int return 0 - 3 end NOT_FOUND_RC
fn EXEC_FAILED_RC(): int return 0 - 4 end EXEC_FAILED_RC
fn cond_passed(r: ConditionResult): bool
return r.passed
end cond_passed
fn cond_reason(r: ConditionResult): string
return r.reason
end cond_reason
fn evaluate_conditions(def: config.ServiceDef): ConditionResult
// exists_file: single path must exist
let f = config.svc_cond_exists_file(def)
if str.length(f) > 0
if not file.fileExists(f)
return ConditionResult{
passed: false,
reason: str.concat("missing ", f)
}
end if
end if
// exists_file_any: at least one must exist
let n = config.svc_cond_exists_file_any_count(def)
if n > 0
let paths = config.svc_cond_exists_file_any(def)
mut found = false
mut i = 0
while i < n
if file.fileExists(paths[i])
found = true
end if
i = i + 1
end while
if not found
return ConditionResult{
passed: false,
reason: "none of exists_file_any paths exist"
}
end if
end if
// command: exit 0 = pass; non-zero = fail; bounded by 5-second timeout
let cmd = config.svc_cond_command(def)
if str.length(cmd) > 0
let rc = run_command_with_timeout(cmd, 5)
if rc != 0
if rc == TIMEOUT_RC()
return ConditionResult{ passed: false, reason: "command timeout" }
elif rc == NOT_FOUND_RC()
return ConditionResult{ passed: false, reason: str.concat("command not found: ", cmd) }
elif rc == EXEC_FAILED_RC()
return ConditionResult{ passed: false, reason: "command exec failed" }
else
return ConditionResult{
passed: false,
reason: str.concat("command exit=", int_to_str(rc))
}
end if
end if
end if
return ConditionResult{ passed: true, reason: "" }
end evaluate_conditions
// Local int_to_str — Reef has no stdlib version (RFE-004).
// Copied verbatim from contract.reef / socket.reef.
fn int_to_str(n: int): string
if n == 0
return "0"
end if
mut value = n
if n < 0
value = 0 - n
end if
mut result = ""
while value > 0
let digit = value % 10
result = str.concat(str.substring("0123456789", digit, 1), result)
value = value / 10
end while
if n < 0
result = str.concat("-", result)
end if
return result
end int_to_str
end module
|