package main import ( "encoding/json" "fmt" "os" "time" "github.com/gocql/gocql" ) // Config holds the connection settings loaded from the JSON config file. type Config struct { Server string `json:"servername"` Port int `json:"port"` Keyspace string `json:"keyspace"` Username string `json:"username"` Password string `json:"password"` } func LoadConfig(path string) (*Config, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("reading config file: %w", err) } var cfg Config if err := json.Unmarshal(data, &cfg); err != nil { return nil, fmt.Errorf("parsing config file: %w", err) } if cfg.Server == "" { return nil, fmt.Errorf("servername is required in config") } if cfg.Port == 0 { cfg.Port = 9042 } return &cfg, nil } func Connect(cfg *Config) (*gocql.Session, error) { cluster := gocql.NewCluster(cfg.Server) cluster.Port = cfg.Port cluster.Keyspace = cfg.Keyspace cluster.Consistency = gocql.Quorum cluster.Timeout = 10 * time.Second if cfg.Username != "" { cluster.Authenticator = gocql.PasswordAuthenticator{ Username: cfg.Username, Password: cfg.Password, } } session, err := cluster.CreateSession() if err != nil { return nil, fmt.Errorf("connecting to cassandra: %w", err) } return session, nil } // QueryResult holds column names and row data returned from a CQL query. type QueryResult struct { Columns []string Rows [][]string } func RunQuery(session *gocql.Session, query string) (*QueryResult, error) { iter := session.Query(query).Iter() columns := iter.Columns() colNames := make([]string, len(columns)) for i, c := range columns { colNames[i] = c.Name } result := &QueryResult{Columns: colNames} row := make(map[string]interface{}) for iter.MapScan(row) { strRow := make([]string, len(colNames)) for i, name := range colNames { strRow[i] = formatValue(row[name]) } result.Rows = append(result.Rows, strRow) row = make(map[string]interface{}) } if err := iter.Close(); err != nil { return nil, fmt.Errorf("query error: %w", err) } return result, nil } func formatValue(v interface{}) string { if v == nil { return "null" } switch val := v.(type) { case []byte: return string(val) case time.Time: return val.Format(time.RFC3339) case gocql.UUID: return val.String() default: return fmt.Sprintf("%v", val) } }