/****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2025-2026, Leafscale, LLC - https://www.leafscale.com Project: zyginit Filename: condition.reef Authors: Chris Tusa License: 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