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) (*ConfigStore, error) {
 22	store, err := Load(workingDir, dataDir, debug)
 23	if err != nil {
 24		return nil, err
 25	}
 26	return store, nil
 27}
 28
 29func ProjectNeedsInitialization(store *ConfigStore) (bool, error) {
 30	if store == nil {
 31		return false, fmt.Errorf("config not loaded")
 32	}
 33
 34	cfg := store.Config()
 35	flagFilePath := filepath.Join(cfg.Options.DataDirectory, InitFlagFilename)
 36
 37	_, err := os.Stat(flagFilePath)
 38	if err == nil {
 39		return false, nil
 40	}
 41
 42	if !os.IsNotExist(err) {
 43		return false, fmt.Errorf("failed to check init flag file: %w", err)
 44	}
 45
 46	someContextFileExists, err := contextPathsExist(store.WorkingDir())
 47	if err != nil {
 48		return false, fmt.Errorf("failed to check for context files: %w", err)
 49	}
 50	if someContextFileExists {
 51		return false, nil
 52	}
 53
 54	// If the working directory has no non-ignored files, skip initialization step
 55	empty, err := dirHasNoVisibleFiles(store.WorkingDir())
 56	if err != nil {
 57		return false, fmt.Errorf("failed to check if directory is empty: %w", err)
 58	}
 59	if empty {
 60		return false, nil
 61	}
 62
 63	return true, nil
 64}
 65
 66func contextPathsExist(dir string) (bool, error) {
 67	entries, err := os.ReadDir(dir)
 68	if err != nil {
 69		return false, err
 70	}
 71
 72	// Create a slice of lowercase filenames for lookup with slices.Contains
 73	var files []string
 74	for _, entry := range entries {
 75		if !entry.IsDir() {
 76			files = append(files, strings.ToLower(entry.Name()))
 77		}
 78	}
 79
 80	// Check if any of the default context paths exist in the directory
 81	for _, path := range defaultContextPaths {
 82		// Extract just the filename from the path
 83		_, filename := filepath.Split(path)
 84		filename = strings.ToLower(filename)
 85
 86		if slices.Contains(files, filename) {
 87			return true, nil
 88		}
 89	}
 90
 91	return false, nil
 92}
 93
 94// dirHasNoVisibleFiles returns true if the directory has no files/dirs after applying ignore rules.
 95func dirHasNoVisibleFiles(dir string) (bool, error) {
 96	files, _, err := fsext.ListDirectory(dir, nil, 1, 1)
 97	if err != nil {
 98		return false, err
 99	}
100	return len(files) == 0, nil
101}
102
103func MarkProjectInitialized(store *ConfigStore) error {
104	if store == nil {
105		return fmt.Errorf("config not loaded")
106	}
107	flagFilePath := filepath.Join(store.Config().Options.DataDirectory, InitFlagFilename)
108
109	file, err := os.Create(flagFilePath)
110	if err != nil {
111		return fmt.Errorf("failed to create init flag file: %w", err)
112	}
113	defer file.Close()
114
115	return nil
116}
117
118func HasInitialDataConfig(store *ConfigStore) bool {
119	if store == nil {
120		return false
121	}
122	cfgPath := GlobalConfigData()
123	if _, err := os.Stat(cfgPath); err != nil {
124		return false
125	}
126	return store.Config().IsConfigured()
127}