···8 "github.com/stormlightlabs/noteleaf/internal/store"
9)
1011-// ConfigHandler handles configuration-related operations
12type ConfigHandler struct {
13 config *store.Config
14}
1516-// NewConfigHandler creates a new ConfigHandler
17func NewConfigHandler() (*ConfigHandler, error) {
18 config, err := store.LoadConfig()
19 if err != nil {
20 return nil, fmt.Errorf("failed to load config: %w", err)
21 }
2223- return &ConfigHandler{
24- config: config,
25- }, nil
26}
2728// Get displays one or all configuration values
29func (h *ConfigHandler) Get(key string) error {
30 if key == "" {
31- // Display all configuration values
32 return h.displayAll()
33 }
3435- // Display specific key
36 value, err := h.getConfigValue(key)
37 if err != nil {
38 return err
···87 field := t.Field(i)
88 value := v.Field(i)
8990- // Get the TOML tag name
91 tomlTag := field.Tag.Get("toml")
92 if tomlTag == "" {
93 continue
94 }
9596- // Remove ",omitempty" suffix if present
97 tagName := strings.Split(tomlTag, ",")[0]
9899- // Format the output based on field type
100 switch value.Kind() {
101 case reflect.String:
102 if value.String() != "" {
···118 v := reflect.ValueOf(*h.config)
119 t := reflect.TypeOf(*h.config)
120121- // Find the field with matching TOML tag
122 for i := 0; i < v.NumField(); i++ {
123 field := t.Field(i)
124 tomlTag := field.Tag.Get("toml")
···139 v := reflect.ValueOf(h.config).Elem()
140 t := reflect.TypeOf(*h.config)
141142- // Find the field with matching TOML tag
143 for i := 0; i < v.NumField(); i++ {
144 field := t.Field(i)
145 tomlTag := field.Tag.Get("toml")
···151 if tagName == key {
152 fieldValue := v.Field(i)
153154- // Set the value based on field type
155 switch fieldValue.Kind() {
156 case reflect.String:
157 fieldValue.SetString(value)
···8 "github.com/stormlightlabs/noteleaf/internal/store"
9)
1011+// ConfigHandler handles [store.Config]-related operations
12type ConfigHandler struct {
13 config *store.Config
14}
1516+// NewConfigHandler creates a new [ConfigHandler]
17func NewConfigHandler() (*ConfigHandler, error) {
18 config, err := store.LoadConfig()
19 if err != nil {
20 return nil, fmt.Errorf("failed to load config: %w", err)
21 }
2223+ return &ConfigHandler{config: config}, nil
0024}
2526// Get displays one or all configuration values
27func (h *ConfigHandler) Get(key string) error {
28 if key == "" {
029 return h.displayAll()
30 }
31032 value, err := h.getConfigValue(key)
33 if err != nil {
34 return err
···83 field := t.Field(i)
84 value := v.Field(i)
85086 tomlTag := field.Tag.Get("toml")
87 if tomlTag == "" {
88 continue
89 }
90091 tagName := strings.Split(tomlTag, ",")[0]
92093 switch value.Kind() {
94 case reflect.String:
95 if value.String() != "" {
···111 v := reflect.ValueOf(*h.config)
112 t := reflect.TypeOf(*h.config)
1130114 for i := 0; i < v.NumField(); i++ {
115 field := t.Field(i)
116 tomlTag := field.Tag.Get("toml")
···131 v := reflect.ValueOf(h.config).Elem()
132 t := reflect.TypeOf(*h.config)
1330134 for i := 0; i < v.NumField(); i++ {
135 field := t.Field(i)
136 tomlTag := field.Tag.Get("toml")
···142 if tagName == key {
143 fieldValue := v.Field(i)
1440145 switch fieldValue.Kind() {
146 case reflect.String:
147 fieldValue.SetString(value)
+8-9
internal/handlers/handlers.go
···3import (
4 "context"
5 "fmt"
06 "os"
7 "path/filepath"
8···130}
131132// Status shows the current application status
133-func Status(ctx context.Context, args []string) error {
134 configDir, err := store.GetConfigDir()
135 if err != nil {
136 return fmt.Errorf("failed to get config directory: %w", err)
···138139 fmt.Printf("Config directory: %s\n", configDir)
140141- // Load config to determine the actual database path
142 config, err := store.LoadConfig()
143 if err != nil {
144 return fmt.Errorf("failed to load configuration: %w", err)
145 }
146147- // Determine database path using the same logic as NewDatabase
148 var dbPath string
149 if config.DatabasePath != "" {
150 dbPath = config.DatabasePath
···159 }
160161 if _, err := os.Stat(dbPath); err != nil {
162- fmt.Println("Database: Not found")
163- fmt.Println("Run 'noteleaf setup' to initialize.")
164 return nil
165 }
166167- fmt.Printf("Database: %s\n", dbPath)
168169 configPath, err := store.GetConfigPath()
170 if err != nil {
···172 }
173174 if _, err := os.Stat(configPath); err != nil {
175- fmt.Println("Configuration: Not found")
176 } else {
177- fmt.Printf("Configuration: %s\n", configPath)
178 }
179180 db, err := store.NewDatabase()
···194 return fmt.Errorf("failed to get available migrations: %w", err)
195 }
196197- fmt.Printf("Migrations: %d/%d applied\n", len(applied), len(available))
198199 return nil
200}
···3import (
4 "context"
5 "fmt"
6+ "io"
7 "os"
8 "path/filepath"
9···131}
132133// Status shows the current application status
134+func Status(ctx context.Context, args []string, w io.Writer) error {
135 configDir, err := store.GetConfigDir()
136 if err != nil {
137 return fmt.Errorf("failed to get config directory: %w", err)
···139140 fmt.Printf("Config directory: %s\n", configDir)
1410142 config, err := store.LoadConfig()
143 if err != nil {
144 return fmt.Errorf("failed to load configuration: %w", err)
145 }
1460147 var dbPath string
148 if config.DatabasePath != "" {
149 dbPath = config.DatabasePath
···158 }
159160 if _, err := os.Stat(dbPath); err != nil {
161+ fmt.Fprintln(w, "Database: Not found")
162+ fmt.Fprintln(w, "Run 'noteleaf setup' to initialize.")
163 return nil
164 }
165166+ fmt.Fprintf(w, "Database: %s\n", dbPath)
167168 configPath, err := store.GetConfigPath()
169 if err != nil {
···171 }
172173 if _, err := os.Stat(configPath); err != nil {
174+ fmt.Fprintln(w, "Configuration: Not found")
175 } else {
176+ fmt.Fprintf(w, "Configuration: %s\n", configPath)
177 }
178179 db, err := store.NewDatabase()
···193 return fmt.Errorf("failed to get available migrations: %w", err)
194 }
195196+ fmt.Fprintf(w, "Migrations: %d/%d applied\n", len(applied), len(available))
197198 return nil
199}
+25-16
internal/handlers/handlers_test.go
···1package handlers
23import (
04 "context"
5 "os"
6 "path/filepath"
···197 t.Run("reports status when setup", func(t *testing.T) {
198 _ = createTestDir(t)
199 ctx := context.Background()
0200201 err := Setup(ctx, []string{})
202 if err != nil {
203 t.Fatalf("Setup failed: %v", err)
204 }
205206- err = Status(ctx, []string{})
207 if err != nil {
208 t.Errorf("Status failed: %v", err)
209 }
···213 t.Run("reports status when not setup", func(t *testing.T) {
214 _ = createTestDir(t)
215 ctx := context.Background()
0216217- err := Status(ctx, []string{})
218 if err != nil {
219 t.Errorf("Status should not fail when not setup: %v", err)
220 }
···224 t.Run("shows migration information", func(t *testing.T) {
225 _ = createTestDir(t)
226 ctx := context.Background()
0227228 err := Setup(ctx, []string{})
229 if err != nil {
230 t.Fatalf("Setup failed: %v", err)
231 }
232233- err = Status(ctx, []string{})
234 if err != nil {
235 t.Errorf("Status failed: %v", err)
236 }
···245 originalNoteleafConfig := os.Getenv("NOTELEAF_CONFIG")
246 originalNoteleafDataDir := os.Getenv("NOTELEAF_DATA_DIR")
247248- // Unset all environment variables so GetConfigDir fails
249 os.Unsetenv("NOTELEAF_CONFIG")
250 os.Unsetenv("NOTELEAF_DATA_DIR")
251···431 os.Setenv("HOME", originalHome)
432 }()
4330434 ctx := context.Background()
435- err := Status(ctx, []string{})
436437 if err == nil {
438 t.Error("Status should fail when GetConfigDir fails")
···467 dbPath = filepath.Join(dataDir, "noteleaf.db")
468 }
469470- // Remove the database file
471 os.Remove(dbPath)
472473- // Create a directory with the same name as the database file
474- // This will cause database connection to fail
475 if err := os.MkdirAll(dbPath, 0755); err != nil {
476 t.Fatalf("Failed to create directory: %v", err)
477 }
478479- err = Status(ctx, []string{})
0480 if err == nil {
481 t.Error("Status should fail when database connection fails")
482 } else if !strings.Contains(err.Error(), "failed to connect to database") && !strings.Contains(err.Error(), "failed to open database") && !strings.Contains(err.Error(), "failed to load config") {
···510 }
511 db.Close()
512513- err = Status(ctx, []string{})
0514 if err == nil {
515 t.Error("Status should fail when migration queries fail")
516 }
···590 }
591 }
592 },
593- handlerFunc: Status,
000594 expectError: true,
595 errorSubstr: "config directory",
596 },
···620 _ = createTestDir(t)
621 ctx := context.Background()
622623- err := Status(ctx, []string{})
0624 if err != nil {
625 t.Errorf("Initial status failed: %v", err)
626 }
···630 t.Errorf("Setup failed: %v", err)
631 }
632633- err = Status(ctx, []string{})
0634 if err != nil {
635 t.Errorf("Status after setup failed: %v", err)
636 }
637638- // Determine database path using the same logic as Setup
639 config, _ := store.LoadConfig()
640 var dbPath string
641 if config.DatabasePath != "" {
···660 t.Error("Database should not exist after reset")
661 }
662663- err = Status(ctx, []string{})
0664 if err != nil {
665 t.Errorf("Status after reset failed: %v", err)
666 }
···678679 done := make(chan error, 3)
6800681 for range 3 {
682 go func() {
683 time.Sleep(time.Millisecond * 10)
684- done <- Status(ctx, []string{})
685 }()
686 }
687
···1package handlers
23import (
4+ "bytes"
5 "context"
6 "os"
7 "path/filepath"
···198 t.Run("reports status when setup", func(t *testing.T) {
199 _ = createTestDir(t)
200 ctx := context.Background()
201+ var buf bytes.Buffer
202203 err := Setup(ctx, []string{})
204 if err != nil {
205 t.Fatalf("Setup failed: %v", err)
206 }
207208+ err = Status(ctx, []string{}, &buf)
209 if err != nil {
210 t.Errorf("Status failed: %v", err)
211 }
···215 t.Run("reports status when not setup", func(t *testing.T) {
216 _ = createTestDir(t)
217 ctx := context.Background()
218+ var buf bytes.Buffer
219220+ err := Status(ctx, []string{}, &buf)
221 if err != nil {
222 t.Errorf("Status should not fail when not setup: %v", err)
223 }
···227 t.Run("shows migration information", func(t *testing.T) {
228 _ = createTestDir(t)
229 ctx := context.Background()
230+ var buf bytes.Buffer
231232 err := Setup(ctx, []string{})
233 if err != nil {
234 t.Fatalf("Setup failed: %v", err)
235 }
236237+ err = Status(ctx, []string{}, &buf)
238 if err != nil {
239 t.Errorf("Status failed: %v", err)
240 }
···249 originalNoteleafConfig := os.Getenv("NOTELEAF_CONFIG")
250 originalNoteleafDataDir := os.Getenv("NOTELEAF_DATA_DIR")
2510252 os.Unsetenv("NOTELEAF_CONFIG")
253 os.Unsetenv("NOTELEAF_DATA_DIR")
254···434 os.Setenv("HOME", originalHome)
435 }()
436437+ var buf bytes.Buffer
438 ctx := context.Background()
439+ err := Status(ctx, []string{}, &buf)
440441 if err == nil {
442 t.Error("Status should fail when GetConfigDir fails")
···471 dbPath = filepath.Join(dataDir, "noteleaf.db")
472 }
4730474 os.Remove(dbPath)
47500476 if err := os.MkdirAll(dbPath, 0755); err != nil {
477 t.Fatalf("Failed to create directory: %v", err)
478 }
479480+ var buf bytes.Buffer
481+ err = Status(ctx, []string{}, &buf)
482 if err == nil {
483 t.Error("Status should fail when database connection fails")
484 } else if !strings.Contains(err.Error(), "failed to connect to database") && !strings.Contains(err.Error(), "failed to open database") && !strings.Contains(err.Error(), "failed to load config") {
···512 }
513 db.Close()
514515+ var buf bytes.Buffer
516+ err = Status(ctx, []string{}, &buf)
517 if err == nil {
518 t.Error("Status should fail when migration queries fail")
519 }
···593 }
594 }
595 },
596+ handlerFunc: func(ctx context.Context, args []string) error {
597+ var buf bytes.Buffer
598+ return Status(ctx, args, &buf)
599+ },
600 expectError: true,
601 errorSubstr: "config directory",
602 },
···626 _ = createTestDir(t)
627 ctx := context.Background()
628629+ var buf bytes.Buffer
630+ err := Status(ctx, []string{}, &buf)
631 if err != nil {
632 t.Errorf("Initial status failed: %v", err)
633 }
···637 t.Errorf("Setup failed: %v", err)
638 }
639640+ buf.Reset()
641+ err = Status(ctx, []string{}, &buf)
642 if err != nil {
643 t.Errorf("Status after setup failed: %v", err)
644 }
6450646 config, _ := store.LoadConfig()
647 var dbPath string
648 if config.DatabasePath != "" {
···667 t.Error("Database should not exist after reset")
668 }
669670+ buf.Reset()
671+ err = Status(ctx, []string{}, &buf)
672 if err != nil {
673 t.Errorf("Status after reset failed: %v", err)
674 }
···686687 done := make(chan error, 3)
688689+ var buf bytes.Buffer
690 for range 3 {
691 go func() {
692 time.Sleep(time.Millisecond * 10)
693+ done <- Status(ctx, []string{}, &buf)
694 }()
695 }
696