|
root / src / commands / ports.reef
ports.reef Reef 243 lines 7.6 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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/******************************************************************************
               __               ____                __
              / /   ___  ____ _/ __/_____________ _/ /__
             / /   / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \
            / /___/  __/ /_/ / __(__  ) /__/ /_/ / /  __/
           /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/

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

   Project: Zygaena
  Filename: ports.reef
   Authors: Chris Tusa <chris.tusa@leafscale.com>
   License: <see LICENSE file included with this source code>
Description: Ports command - port scaffolding and cache management

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

module commands.ports

import sys.args
import io.file
import io.dir
import io.path
import core.str
import core.config
import core.portindex
import types
import util.color
import exitcodes as ec
import core.result as res

export
    fn execute(opts: types.GlobalOptions): int
end export

fn execute(opts: types.GlobalOptions): int
    let argc = args.count()

    if argc <= opts.cmd_index + 1
        print_usage()
        return ec.EXIT_USAGE()
    end if

    let subcommand = args.get(opts.cmd_index + 1)

    if subcommand == "new"
        return ports_new(opts)
    elif subcommand == "cache"
        return ports_cache(opts)
    else
        color.print_error("Unknown subcommand: " + subcommand)
        print_usage()
        return ec.EXIT_USAGE()
    end if
end execute

// ============================================================================
// coral ports new <category/name>
// ============================================================================

fn ports_new(opts: types.GlobalOptions): int
    let argc = args.count()

    if argc <= opts.cmd_index + 2
        color.print_error("Missing argument: category/name")
        println("Usage: coral ports new <category/name>")
        return ec.EXIT_USAGE()
    end if

    let arg = args.get(opts.cmd_index + 2)

    // Require slash format
    let slash_idx = str.index_of(arg, "/")
    if slash_idx <= 0
        color.print_error("Invalid format: '" + arg + "'")
        println("Use category/name format, e.g.: coral ports new hammerhead/my-port")
        return ec.EXIT_USAGE()
    end if

    let category = str.substring(arg, 0, slash_idx)
    let name = str.substring(arg, slash_idx + 1, str.length(arg) - slash_idx - 1)

    if str.length(name) == 0
        color.print_error("Port name cannot be empty")
        return ec.EXIT_USAGE()
    end if

    // Get ports directory
    let cfg = config.load()
    let ports_dir = config.get_ports_dir(cfg)

    // Create port directory
    let port_dir = path.join_path(ports_dir, path.join_path(category, name))

    if dir.dir_exists(port_dir)
        color.print_error("Port directory already exists: " + port_dir)
        return ec.EXIT_PKG_ALREADY_INSTALLED()
    end if

    if not res.is_ok(dir.create_dir_all(port_dir))
        color.print_error("Failed to create directory: " + port_dir)
        return ec.EXIT_PERMISSION_DENIED()
    end if

    // Generate package.toml
    let toml_path = path.join_path(port_dir, "package.toml")
    let toml_content = "[package]\nname = \"" + name + "\"\nversion = \"0.1.0\"\nrelease = 1\ndescription = \"\"\nurl = \"\"\nlicense = \"\"\nmaintainer = \"\"\narch = \"x86_64\"\n\n[dependencies]\nruntime = []\nbuild = []\n\n[[source]]\nfile = \"\"\nurls = []\nchecksum = \"SKIP\"\n\n[build]\nparallel = true\njobs = 0\n"

    if not res.is_ok(file.writeFile(toml_path, toml_content))
        color.print_error("Failed to write package.toml")
        return ec.EXIT_PERMISSION_DENIED()
    end if

    // Generate build.sh
    let build_path = path.join_path(port_dir, "build.sh")
    let dollar = "$"
    let build_content = "#!/bin/sh\n# Build script for " + name + "\nset -e\n\n# cd " + name + "-" + dollar + "{VERSION}\n\nMAKE=gmake\nif [ \"" + dollar + "(uname -s)\" != \"SunOS\" ]; then\n    MAKE=make\nfi\n\n# ./configure --prefix=/usr\n# " + dollar + "MAKE " + dollar + "{MAKEFLAGS}\n# " + dollar + "MAKE DESTDIR=\"" + dollar + "{DESTDIR}\" install\n"

    if not res.is_ok(file.writeFile(build_path, build_content))
        color.print_error("Failed to write build.sh")
        return ec.EXIT_PERMISSION_DENIED()
    end if

    color.print_success("Port created: " + category + "/" + name)
    println("")
    println("  Directory: " + port_dir)
    println("  package.toml: " + toml_path)
    println("  build.sh: " + build_path)
    println("")
    color.print_info("Next steps:")
    println("  1. Edit package.toml with package details")
    println("  2. Add source URLs and checksums")
    println("  3. Write the build.sh script")
    println("  4. Test with: coral build " + category + "/" + name)

    return ec.EXIT_SUCCESS()
end ports_new

// ============================================================================
// coral ports cache [status|rebuild]
// ============================================================================

fn ports_cache(opts: types.GlobalOptions): int
    let argc = args.count()

    if argc <= opts.cmd_index + 2
        // Default to status
        return cache_status()
    end if

    let subcmd = args.get(opts.cmd_index + 2)

    if subcmd == "status"
        return cache_status()
    elif subcmd == "rebuild"
        return cache_rebuild()
    else
        color.print_error("Unknown cache subcommand: " + subcmd)
        println("Usage: coral ports cache [status|rebuild]")
        return ec.EXIT_USAGE()
    end if
end ports_cache

fn cache_status(): int
    color.print_action("Ports index status:")
    println("")

    if portindex.port_index_exists()
        let count = portindex.get_port_count()
        println("  Status: built")
        println("  Ports indexed: " + int_to_str(count))
    else
        println("  Status: not built")
        color.print_info("Run 'coral ports cache rebuild' to build the index")
    end if

    return ec.EXIT_SUCCESS()
end cache_status

fn cache_rebuild(): int
    color.print_action("Rebuilding ports index...")
    println("")

    if portindex.rebuild_port_index()
        let count = portindex.get_port_count()
        color.print_success("Ports index rebuilt successfully")
        println("  Ports indexed: " + int_to_str(count))
        return ec.EXIT_SUCCESS()
    else
        color.print_error("Failed to rebuild ports index")
        return ec.EXIT_DB_ERROR()
    end if
end cache_rebuild

// ============================================================================
// Usage
// ============================================================================

proc print_usage()
    println("Usage: coral ports <command> [args]")
    println("")
    println("Port development and cache management.")
    println("")
    println("Commands:")
    println("  new <category/name>   Scaffold a new port")
    println("  cache [status]        Show ports index status")
    println("  cache rebuild         Rebuild ports index")
    println("")
    println("Examples:")
    println("  coral ports new hammerhead/my-port")
    println("  coral ports cache status")
    println("  coral ports cache rebuild")
end print_usage

// 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