|
root / src / condition.reef
condition.reef Reef 133 lines 3.9 KB
  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