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