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:
@@ -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
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
@@ -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]
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
Reference in New Issue
Block a user