init.go

  1package config
  2
  3import (
  4	"fmt"
  5	"os"
  6	"path/filepath"
  7	"slices"
  8	"strings"
  9
 10	"github.com/charmbracelet/crush/internal/fsext"
 11)
 12
 13const (
 14	InitFlagFilename = "init"
 15)
 16
 17type ProjectInitFlag struct {
 18	Initialized bool `json:"initialized"`
 19}
 20
 21func Init(workingDir, dataDir string, debug bool) (*Service, error) {
 22	svc, err := Load(workingDir, dataDir, debug)
 23	if err != nil {
 24		return nil, err
 25	}
 26	return svc, nil
 27}
 28
 29func ProjectNeedsInitialization(svc *Service) (bool, error) {
 30	if svc == nil {
 31		return false, fmt.Errorf("config not loaded")
 32	}
 33
 34	flagFilePath := filepath.Join(svc.DataDirectory(), InitFlagFilename)
 35
 36	_, err := os.Stat(flagFilePath)
 37	if err == nil {
 38		return false, nil
 39	}
 40
 41	if !os.IsNotExist(err) {
 42		return false, fmt.Errorf("failed to check init flag file: %w", err)
 43	}
 44
 45	someContextFileExists, err := contextPathsExist(svc.WorkingDir())
 46	if err != nil {
 47		return false, fmt.Errorf("failed to check for context files: %w", err)
 48	}
 49	if someContextFileExists {
 50		return false, nil
 51	}
 52
 53	empty, err := dirHasNoVisibleFiles(svc.WorkingDir())
 54	if err != nil {
 55		return false, fmt.Errorf("failed to check if directory is empty: %w", err)
 56	}
 57	if empty {
 58		return false, nil
 59	}
 60
 61	return true, nil
 62}
 63
 64func contextPathsExist(dir string) (bool, error) {
 65	entries, err := os.ReadDir(dir)
 66	if err != nil {
 67		return false, err
 68	}
 69
 70	// Create a slice of lowercase filenames for lookup with slices.Contains
 71	var files []string
 72	for _, entry := range entries {
 73		if !entry.IsDir() {
 74			files = append(files, strings.ToLower(entry.Name()))
 75		}
 76	}
 77
 78	// Check if any of the default context paths exist in the directory
 79	for _, path := range defaultContextPaths {
 80		// Extract just the filename from the path
 81		_, filename := filepath.Split(path)
 82		filename = strings.ToLower(filename)
 83
 84		if slices.Contains(files, filename) {
 85			return true, nil
 86		}
 87	}
 88
 89	return false, nil
 90}
 91
 92// dirHasNoVisibleFiles returns true if the directory has no files/dirs after applying ignore rules
 93func dirHasNoVisibleFiles(dir string) (bool, error) {
 94	files, _, err := fsext.ListDirectory(dir, nil, 1, 1)
 95	if err != nil {
 96		return false, err
 97	}
 98	return len(files) == 0, nil
 99}
100
101func MarkProjectInitialized(svc *Service) error {
102	if svc == nil {
103		return fmt.Errorf("config not loaded")
104	}
105	flagFilePath := filepath.Join(svc.DataDirectory(), InitFlagFilename)
106
107	file, err := os.Create(flagFilePath)
108	if err != nil {
109		return fmt.Errorf("failed to create init flag file: %w", err)
110	}
111	defer file.Close()
112
113	return nil
114}
115
116func HasInitialDataConfig(svc *Service) bool {
117	if svc == nil {
118		return false
119	}
120	cfgPath := GlobalConfigData()
121	if _, err := os.Stat(cfgPath); err != nil {
122		return false
123	}
124	return svc.IsConfigured()
125}