--- name: vault_inventory_scan kind: function lang: go domain: infra version: "1.0.0" purity: impure signature: "func VaultInventoryScan(vaultPath, vaultID, vaultName string) ([]VaultFile, error)" description: "Recorre vaultPath con filepath.WalkDir y retorna un slice de VaultFile ordenado por RelPath para cada archivo regular, computando sha256 por streaming, MIME por extension/magic y bucket/sub-bucket por posicion en el arbol." tags: [vault, inventory, scan, filesystem, sha256, mime, infra] uses_functions: [] uses_types: ["vault_file_go_infra"] returns: [] returns_optional: false error_type: "error_go_core" imports: [crypto/sha256, encoding/hex, fmt, io, net/http, os, path/filepath, sort, strings] params: - name: vaultPath desc: "ruta absoluta o relativa al directorio raiz del vault" - name: vaultID desc: "identificador del vault (ej: turismo_spain_app_turismo) — se copia a cada VaultFile" - name: vaultName desc: "nombre legible del vault (ej: turismo_spain) — se copia a cada VaultFile" output: "slice de VaultFile ordenado lexicograficamente por RelPath; slice vacio (no nil) si el vault esta vacio" tested: true tests: - "tmpdir vacio retorna slice vacio" - "data layout — bucket y sub_bucket correctos" - "knowledge layout — bucket y sub_bucket correctos" - "omite vault_index.db y .git" - "sha256 determinista para mismo contenido" - "orden lexicografico del resultado" test_file_path: "functions/infra/vault_inventory_scan_test.go" file_path: "functions/infra/vault_inventory_scan.go" --- ## Ejemplo ```go files, err := VaultInventoryScan("/data/vaults/turismo_spain", "turismo_spain_v1", "turismo_spain") if err != nil { log.Fatal(err) } for _, f := range files { fmt.Printf("%s %s %s/%s\n", f.RelPath, f.Mime, f.Bucket, f.SubBucket) } ``` ## Notas ### Archivos omitidos - `vault_index.db`, `vault_index.db-shm`, `vault_index.db-wal` (siempre) - `.git/` en cualquier profundidad (SkipDir) - Entradas cuyo nombre empieza por `.` solo en la raiz del vault (nivel 0) ### Deteccion de MIME `file_validate_type_go_infra` (FileValidateType) no se usa porque su firma requiere una lista blanca de tipos permitidos y retorna (mime, bool) — esta disenada para validacion de uploads, no para escaneo inventarial donde cualquier MIME es valido. Se usan en su lugar: 1. Override por extension (prioridad alta): `.csv` → `text/csv`, `.md` → `text/markdown`, `.parquet` → `application/parquet`. Necesario porque `http.DetectContentType` clasifica CSV como `text/plain` y no conoce Parquet. 2. `http.DetectContentType` sobre primeros 512 bytes (magic bytes, stdlib) para el resto. ### SHA-256 Calculado por streaming con `io.Copy` a `sha256.New()` — no carga el archivo completo a memoria. Valido para archivos de cualquier tamano. ### Bucket / SubBucket Derivados de la posicion en el arbol: - `bucket` = primer segmento del RelPath (tipicamente "data" o "knowledge") - `subBucket` = segundo segmento si existe; vacio si el archivo esta en la raiz del bucket