feat: add Go core string and version utility functions

5 funciones Go puras del dominio core: parse_version y compare_versions para
parsing y comparacion semantica de versiones, longest_common_prefix para
encontrar el prefijo comun mas largo entre strings, rel_or_full para devolver
rutas relativas cuando es posible, y split_command_and_arg para separar
comandos de sus argumentos. Todas sin dependencias externas.
This commit is contained in:
2026-04-12 13:54:36 +02:00
parent c383e6464a
commit fce8c48c30
10 changed files with 317 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
package core
import (
"strconv"
"strings"
)
// CompareVersions compares two semver strings (e.g. "v1.4.0", "1.2.3").
// Returns -1 if a < b, 0 if equal, 1 if a > b.
func CompareVersions(a, b string) int {
pa := parseVersionParts(a)
pb := parseVersionParts(b)
for i := 0; i < 3; i++ {
if pa[i] < pb[i] {
return -1
}
if pa[i] > pb[i] {
return 1
}
}
return 0
}
func parseVersionParts(v string) [3]int {
v = strings.TrimPrefix(v, "v")
parts := strings.SplitN(v, ".", 3)
var nums [3]int
for i, p := range parts {
if i >= 3 {
break
}
n, _ := strconv.Atoi(p)
nums[i] = n
}
return nums
}
+42
View File
@@ -0,0 +1,42 @@
---
name: compare_versions
kind: function
lang: go
domain: core
version: "1.0.0"
purity: pure
signature: "func CompareVersions(a, b string) int"
description: "Compara dos strings de version semantica (semver). Soporta formato con o sin prefijo 'v'. Retorna -1, 0 o 1."
tags: [core, version, semver, compare, parse]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: [strconv, strings]
params:
- name: a
desc: "primera version (ej: 'v1.4.0' o '1.4.0')"
- name: b
desc: "segunda version"
output: "-1 si a < b, 0 si iguales, 1 si a > b"
tested: false
tests: []
test_file_path: ""
file_path: "functions/core/compare_versions.go"
source_repo: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/egutierrez/DevLauncher.git"
source_license: "MIT"
source_file: "installer/core/version.go"
---
## Ejemplo
```go
core.CompareVersions("v1.4.0", "v1.3.9") // 1
core.CompareVersions("1.0.0", "1.0.0") // 0
core.CompareVersions("v0.9.0", "v1.0.0") // -1
```
## Notas
Compara major.minor.patch como enteros. Ignora pre-release tags y metadata (solo los 3 numeros). Partes faltantes se tratan como 0.
+21
View File
@@ -0,0 +1,21 @@
package core
import "strings"
// LongestCommonPrefix returns the longest string that is a prefix of every item.
// Returns "" if the slice is empty or no common prefix exists.
func LongestCommonPrefix(items []string) string {
if len(items) == 0 {
return ""
}
prefix := items[0]
for _, item := range items[1:] {
for !strings.HasPrefix(item, prefix) {
if prefix == "" {
return ""
}
prefix = prefix[:len(prefix)-1]
}
}
return prefix
}
+42
View File
@@ -0,0 +1,42 @@
---
name: longest_common_prefix
kind: function
lang: go
domain: core
version: "1.0.0"
purity: pure
signature: "func LongestCommonPrefix(items []string) string"
description: "Retorna el prefijo comun mas largo compartido por todos los strings del slice. Util para autocompletado de rutas y comandos."
tags: [core, string, prefix, autocomplete, common]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: [strings]
params:
- name: items
desc: "slice de strings para buscar prefijo comun"
output: "prefijo comun mas largo, o vacio si no hay coincidencia"
tested: false
tests: []
test_file_path: ""
file_path: "functions/core/longest_common_prefix.go"
source_repo: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/egutierrez/DevLauncher.git"
source_license: "MIT"
source_file: "launcher/core/commands.go"
---
## Ejemplo
```go
prefix := core.LongestCommonPrefix([]string{"/home/user/docs", "/home/user/downloads", "/home/user/desktop"})
// prefix == "/home/user/d"
empty := core.LongestCommonPrefix([]string{"abc", "xyz"})
// empty == ""
```
## Notas
Compara caracter por caracter acortando el prefijo progresivamente. Complejidad O(n*m) donde n es el numero de strings y m la longitud del prefijo.
+15
View File
@@ -0,0 +1,15 @@
package core
import "strings"
// ParseVersion extracts the version tag from the first word of the first line.
// For example, "v1.4.0 - Description" returns "v1.4.0".
// Returns "" if the input is empty.
func ParseVersion(content string) string {
line := strings.SplitN(strings.TrimSpace(content), "\n", 2)[0]
word := strings.Fields(line)
if len(word) == 0 {
return ""
}
return word[0]
}
+42
View File
@@ -0,0 +1,42 @@
---
name: parse_version
kind: function
lang: go
domain: core
version: "1.0.0"
purity: pure
signature: "func ParseVersion(content string) string"
description: "Extrae el tag de version del primer campo de la primera linea de texto. Util para parsear archivos VERSION.txt o salida de comandos --version."
tags: [core, version, parse, text, extract]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: [strings]
params:
- name: content
desc: "texto con version en la primera palabra de la primera linea"
output: "string con el tag de version (ej: 'v1.4.0'), o vacio"
tested: false
tests: []
test_file_path: ""
file_path: "functions/core/parse_version.go"
source_repo: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/egutierrez/DevLauncher.git"
source_license: "MIT"
source_file: "installer/core/version.go"
---
## Ejemplo
```go
v := core.ParseVersion("v0.4.9 - Instalador Julia Windows (2026-02-28)")
// v == "v0.4.9"
v2 := core.ParseVersion("1.0.0\nChangelog...")
// v2 == "1.0.0"
```
## Notas
Toma solo el primer campo (separado por whitespace) de la primera linea. No valida formato semver — simplemente extrae el primer token.
+13
View File
@@ -0,0 +1,13 @@
package core
import "path/filepath"
// RelOrFull returns target relative to base when possible,
// otherwise returns target unchanged.
func RelOrFull(base, target string) string {
rel, err := filepath.Rel(base, target)
if err != nil {
return target
}
return rel
}
+44
View File
@@ -0,0 +1,44 @@
---
name: rel_or_full
kind: function
lang: go
domain: core
version: "1.0.0"
purity: pure
signature: "func RelOrFull(base, target string) string"
description: "Retorna el path de target relativo a base. Si filepath.Rel falla, retorna target sin cambios. Util para mostrar paths cortos en UIs."
tags: [core, path, relative, filepath, display]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: [path/filepath]
params:
- name: base
desc: "directorio base para calcular la ruta relativa"
- name: target
desc: "path absoluto o relativo a convertir"
output: "path relativo a base, o target original si la conversion falla"
tested: false
tests: []
test_file_path: ""
file_path: "functions/core/rel_or_full.go"
source_repo: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/egutierrez/DevLauncher.git"
source_license: "MIT"
source_file: "launcher/core/script_query.go"
---
## Ejemplo
```go
rel := core.RelOrFull("/home/user/project", "/home/user/project/src/main.go")
// rel == "src/main.go"
full := core.RelOrFull("/home/user", "/other/path")
// full == "../../other/path"
```
## Notas
Wrapper seguro sobre `filepath.Rel` que nunca retorna error. Si los paths estan en volúmenes distintos (Windows), retorna el target original.
+20
View File
@@ -0,0 +1,20 @@
package core
import "strings"
// SplitCommandAndArg splits "cmd arg" into its two parts.
// Returns (cmd, arg, true) if a space separator is found,
// or ("", "", false) if the input has no space or is empty.
func SplitCommandAndArg(input string) (cmd, arg string, ok bool) {
trimmed := strings.TrimSpace(input)
idx := strings.Index(trimmed, " ")
if idx == -1 {
return "", "", false
}
cmd = strings.TrimSpace(trimmed[:idx])
arg = strings.TrimSpace(trimmed[idx+1:])
if cmd == "" {
return "", "", false
}
return cmd, arg, true
}
+42
View File
@@ -0,0 +1,42 @@
---
name: split_command_and_arg
kind: function
lang: go
domain: core
version: "1.0.0"
purity: pure
signature: "func SplitCommandAndArg(input string) (cmd, arg string, ok bool)"
description: "Separa un string 'comando argumento' en sus dos partes por el primer espacio. Retorna ok=false si no hay espacio o el input esta vacio."
tags: [core, string, split, command, parse, cli]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: [strings]
params:
- name: input
desc: "string con formato 'comando argumento'"
output: "tupla (cmd, arg, ok) donde ok indica si se encontro separador"
tested: false
tests: []
test_file_path: ""
file_path: "functions/core/split_command_and_arg.go"
source_repo: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/egutierrez/DevLauncher.git"
source_license: "MIT"
source_file: "launcher/core/commands.go"
---
## Ejemplo
```go
cmd, arg, ok := core.SplitCommandAndArg("cd /home/user")
// cmd == "cd", arg == "/home/user", ok == true
_, _, ok2 := core.SplitCommandAndArg("exit")
// ok2 == false
```
## Notas
Solo separa por el primer espacio. El argumento puede contener espacios adicionales. Trims whitespace en ambos extremos del input y de cada parte.