| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- package config
- import (
- "fmt"
- "os"
- "path/filepath"
- "slices"
- "strings"
- "sync/atomic"
- "github.com/charmbracelet/crush/internal/fsext"
- )
- const (
- InitFlagFilename = "init"
- )
- type ProjectInitFlag struct {
- Initialized bool `json:"initialized"`
- }
- // TODO: we need to remove the global config instance keeping it now just until everything is migrated
- var instance atomic.Pointer[Config]
- func Init(workingDir, dataDir string, debug bool) (*Config, error) {
- cfg, err := Load(workingDir, dataDir, debug)
- if err != nil {
- return nil, err
- }
- instance.Store(cfg)
- return instance.Load(), nil
- }
- func Get() *Config {
- cfg := instance.Load()
- return cfg
- }
- func ProjectNeedsInitialization() (bool, error) {
- cfg := Get()
- if cfg == nil {
- return false, fmt.Errorf("config not loaded")
- }
- flagFilePath := filepath.Join(cfg.Options.DataDirectory, InitFlagFilename)
- _, err := os.Stat(flagFilePath)
- if err == nil {
- return false, nil
- }
- if !os.IsNotExist(err) {
- return false, fmt.Errorf("failed to check init flag file: %w", err)
- }
- someContextFileExists, err := contextPathsExist(cfg.WorkingDir())
- if err != nil {
- return false, fmt.Errorf("failed to check for context files: %w", err)
- }
- if someContextFileExists {
- return false, nil
- }
- // If the working directory has no non-ignored files, skip initialization step
- empty, err := dirHasNoVisibleFiles(cfg.WorkingDir())
- if err != nil {
- return false, fmt.Errorf("failed to check if directory is empty: %w", err)
- }
- if empty {
- return false, nil
- }
- return true, nil
- }
- func contextPathsExist(dir string) (bool, error) {
- entries, err := os.ReadDir(dir)
- if err != nil {
- return false, err
- }
- // Create a slice of lowercase filenames for lookup with slices.Contains
- var files []string
- for _, entry := range entries {
- if !entry.IsDir() {
- files = append(files, strings.ToLower(entry.Name()))
- }
- }
- // Check if any of the default context paths exist in the directory
- for _, path := range defaultContextPaths {
- // Extract just the filename from the path
- _, filename := filepath.Split(path)
- filename = strings.ToLower(filename)
- if slices.Contains(files, filename) {
- return true, nil
- }
- }
- return false, nil
- }
- // dirHasNoVisibleFiles returns true if the directory has no files/dirs after applying ignore rules
- func dirHasNoVisibleFiles(dir string) (bool, error) {
- files, _, err := fsext.ListDirectory(dir, nil, 1, 1)
- if err != nil {
- return false, err
- }
- return len(files) == 0, nil
- }
- func MarkProjectInitialized() error {
- cfg := Get()
- if cfg == nil {
- return fmt.Errorf("config not loaded")
- }
- flagFilePath := filepath.Join(cfg.Options.DataDirectory, InitFlagFilename)
- file, err := os.Create(flagFilePath)
- if err != nil {
- return fmt.Errorf("failed to create init flag file: %w", err)
- }
- defer file.Close()
- return nil
- }
- func HasInitialDataConfig() bool {
- cfgPath := GlobalConfigData()
- if _, err := os.Stat(cfgPath); err != nil {
- return false
- }
- return Get().IsConfigured()
- }
|