|  | 
|  | 1 | +package gallery | 
|  | 2 | + | 
|  | 3 | +import ( | 
|  | 4 | +	"fmt" | 
|  | 5 | +	"io" | 
|  | 6 | +	"os" | 
|  | 7 | +	"path/filepath" | 
|  | 8 | +	"strings" | 
|  | 9 | + | 
|  | 10 | +	"github.com/google/btree" | 
|  | 11 | +	"gopkg.in/yaml.v3" | 
|  | 12 | +) | 
|  | 13 | + | 
|  | 14 | +type Gallery[T GalleryElement] struct { | 
|  | 15 | +	*btree.BTreeG[T] | 
|  | 16 | +} | 
|  | 17 | + | 
|  | 18 | +func less[T GalleryElement](a, b T) bool { | 
|  | 19 | +	return a.GetName() < b.GetName() | 
|  | 20 | +} | 
|  | 21 | + | 
|  | 22 | +func (g *Gallery[T]) Search(term string) GalleryElements[T] { | 
|  | 23 | +	var filteredModels GalleryElements[T] | 
|  | 24 | +	g.Ascend(func(item T) bool { | 
|  | 25 | +		if strings.Contains(item.GetName(), term) || | 
|  | 26 | +			strings.Contains(item.GetDescription(), term) || | 
|  | 27 | +			strings.Contains(item.GetGallery().Name, term) || | 
|  | 28 | +			strings.Contains(strings.Join(item.GetTags(), ","), term) { | 
|  | 29 | +			filteredModels = append(filteredModels, item) | 
|  | 30 | +		} | 
|  | 31 | +		return true | 
|  | 32 | +	}) | 
|  | 33 | + | 
|  | 34 | +	return filteredModels | 
|  | 35 | +} | 
|  | 36 | + | 
|  | 37 | +// processYAMLFile takes a single file path and adds its models to the existing tree. | 
|  | 38 | +func processYAMLFile[T GalleryElement](filePath string, tree *Gallery[T]) error { | 
|  | 39 | +	file, err := os.Open(filePath) | 
|  | 40 | +	if err != nil { | 
|  | 41 | +		return fmt.Errorf("could not open %s: %w", filePath, err) | 
|  | 42 | +	} | 
|  | 43 | +	defer file.Close() | 
|  | 44 | + | 
|  | 45 | +	decoder := yaml.NewDecoder(file) | 
|  | 46 | + | 
|  | 47 | +	// Stream documents from the file | 
|  | 48 | +	for { | 
|  | 49 | +		var model T | 
|  | 50 | +		err := decoder.Decode(&model) | 
|  | 51 | +		if err == io.EOF { | 
|  | 52 | +			break // End of file | 
|  | 53 | +		} | 
|  | 54 | +		if err != nil { | 
|  | 55 | +			return fmt.Errorf("error decoding %s: %w", filePath, err) | 
|  | 56 | +		} | 
|  | 57 | + | 
|  | 58 | +		tree.ReplaceOrInsert(model) | 
|  | 59 | +	} | 
|  | 60 | +	return nil | 
|  | 61 | +} | 
|  | 62 | + | 
|  | 63 | +// loadModelsFromDirectory scans a directory and processes all .yaml/.yml files. | 
|  | 64 | +func LoadGalleryFromDirectory[T GalleryElement](dirPath string) (*Gallery[T], error) { | 
|  | 65 | +	tree := &Gallery[T]{btree.NewG[T](2, less[T])} | 
|  | 66 | + | 
|  | 67 | +	entries, err := os.ReadDir(dirPath) | 
|  | 68 | +	if err != nil { | 
|  | 69 | +		return nil, fmt.Errorf("could not read directory: %w", err) | 
|  | 70 | +	} | 
|  | 71 | + | 
|  | 72 | +	for _, entry := range entries { | 
|  | 73 | +		// Skip subdirectories and non-YAML files. | 
|  | 74 | +		if entry.IsDir() { | 
|  | 75 | +			continue | 
|  | 76 | +		} | 
|  | 77 | +		fileName := entry.Name() | 
|  | 78 | +		if !strings.HasSuffix(fileName, ".yaml") && !strings.HasSuffix(fileName, ".yml") { | 
|  | 79 | +			continue | 
|  | 80 | +		} | 
|  | 81 | + | 
|  | 82 | +		fullPath := filepath.Join(dirPath, fileName) | 
|  | 83 | + | 
|  | 84 | +		err := processYAMLFile(fullPath, tree) | 
|  | 85 | +		if err != nil { | 
|  | 86 | +			return nil, err | 
|  | 87 | +		} | 
|  | 88 | +	} | 
|  | 89 | + | 
|  | 90 | +	return tree, nil | 
|  | 91 | +} | 
0 commit comments