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