|
root / src / commands / outdated.reef
outdated.reef Reef 204 lines 5.0 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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/******************************************************************************
               __               ____                __
              / /   ___  ____ _/ __/_____________ _/ /__
             / /   / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \
            / /___/  __/ /_/ / __(__  ) /__/ /_/ / /  __/
           /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/

   (C)opyright 2025, Leafscale, LLC -  https://www.leafscale.com

   Project: Zygaena
  Filename: outdated.reef
   Authors: Chris Tusa <chris.tusa@leafscale.com>
   License: <see LICENSE file included with this source code>
Description: Outdated command - show packages with available updates

******************************************************************************/

module commands.outdated

import sys.args
import core.str
import core.database
import core.port
import types
import util.color
import exitcodes as ec

export
    fn execute(): int
end export

fn execute(): int
    color.print_action("Checking for available updates...")
    println("")

    // Get list of installed packages
    mut installed: [string] = new [string](512)
    let installed_count = database.list_installed(installed, 512)

    if installed_count == 0
        color.print_info("No packages installed")
        return ec.EXIT_SUCCESS()
    end if

    // Check each package for updates
    mut outdated_count = 0
    mut i = 0
    while i < installed_count
        let pkg_name = installed[i]
        let installed_pkg = database.get_installed(pkg_name)

        // Find port
        let port_result = port.find(pkg_name)

        if port_result.success
            let p = port_result.port

            // Compare versions
            let cmp = compare_versions(installed_pkg.info.version, p.info.version)

            if cmp < 0
                // Outdated
                if outdated_count == 0
                    color.print_info("Outdated packages:")
                    println("")
                end if

                print("  ")
                print(pkg_name)
                print(" ")
                print(installed_pkg.info.version)
                print("-")
                print(int_to_str(installed_pkg.info.release))
                print(" -> ")
                print(p.info.version)
                print("-")
                println(int_to_str(p.info.release))

                outdated_count = outdated_count + 1
            end if
        end if

        i = i + 1
    end while

    if outdated_count == 0
        color.print_success("All packages are up to date")
        // Return special code for scripting - no updates available
        if args.has_flag("check")
            return ec.EXIT_NO_UPDATES()
        end if
        return ec.EXIT_SUCCESS()
    else
        println("")
        color.print_info(int_to_str(outdated_count) + " package(s) can be upgraded")
        println("")
        color.print_info("Run 'coral upgrade' to upgrade all packages")
        color.print_info("Run 'coral upgrade <package>' to upgrade specific packages")
        // Return special code for scripting - updates available
        if args.has_flag("check")
            return ec.EXIT_UPDATE_AVAILABLE()
        end if
        return ec.EXIT_SUCCESS()
    end if
end execute

// Compare two version strings
// Returns: -1 if v1 < v2, 0 if equal, 1 if v1 > v2
fn compare_versions(v1: string, v2: string): int
    // Split versions into parts
    mut parts1: [string] = new [string](10)
    mut parts2: [string] = new [string](10)

    let count1 = str.split(v1, '.', parts1, 10)
    let count2 = str.split(v2, '.', parts2, 10)

    // Compare each part
    mut i = 0
    let max_parts = max_int(count1, count2)

    while i < max_parts
        mut n1 = 0
        mut n2 = 0

        if i < count1
            n1 = parse_int(parts1[i])
        end if

        if i < count2
            n2 = parse_int(parts2[i])
        end if

        if n1 < n2
            return 0 - 1
        elif n1 > n2
            return 1
        end if

        i = i + 1
    end while

    return 0
end compare_versions

fn max_int(a: int, b: int): int
    if a > b
        return a
    end if
    return b
end max_int

// Parse integer from string
fn parse_int(s: string): int
    let len = str.length(s)
    if len == 0
        return 0
    end if

    mut result = 0
    mut i = 0

    while i < len
        let c = s[i]
        if c >= '0' and c <= '9'
            result = result * 10 + (c - '0')
        else
            // Stop at non-digit
            return result
        end if
        i = i + 1
    end while

    return result
end parse_int

// Helper: convert int to string
fn int_to_str(n: int): string
    if n == 0
        return "0"
    end if

    mut negative = false
    mut value = n
    if n < 0
        negative = true
        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 negative
        result = str.concat("-", result)
    end if

    return result
end int_to_str

end module