Filters partially work
This commit is contained in:
parent
e72e79d1f7
commit
588cf064c1
12 changed files with 886 additions and 59 deletions
290
backup/main.go
Normal file
290
backup/main.go
Normal file
|
@ -0,0 +1,290 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"log/slog"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.andreafazzi.eu/andrea/probo/pkg/models"
|
||||||
|
"git.andreafazzi.eu/andrea/probo/pkg/store"
|
||||||
|
"git.andreafazzi.eu/andrea/probo/pkg/store/file"
|
||||||
|
"github.com/lmittmann/tint"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultAssetDir = "assets"
|
||||||
|
DefaultDataDir = "data"
|
||||||
|
DefaultSessionDir = "sessions"
|
||||||
|
DefaultResponseDir = "responses"
|
||||||
|
DefaultTemplateDir = "templates"
|
||||||
|
DefaultStaticDir = "static"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
SessionDir string
|
||||||
|
ResponseDir string
|
||||||
|
TemplateDir string
|
||||||
|
StaticDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExamTemplateData struct {
|
||||||
|
*models.Exam
|
||||||
|
|
||||||
|
SessionID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
config *Config
|
||||||
|
mux *http.ServeMux
|
||||||
|
|
||||||
|
sessionFileStore *file.SessionFileStore
|
||||||
|
responseFileStore *file.ResponseFileStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDefaultTemplateDir() string {
|
||||||
|
return filepath.Join(DefaultAssetDir, DefaultTemplateDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDefaultStaticDir() string {
|
||||||
|
return filepath.Join(DefaultAssetDir, DefaultStaticDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDefaultSessionDir() string {
|
||||||
|
return filepath.Join(DefaultDataDir, DefaultSessionDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDefaultResponseDir() string {
|
||||||
|
return filepath.Join(DefaultDataDir, DefaultResponseDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(config *Config) (*Server, error) {
|
||||||
|
_, err := os.Stat(config.SessionDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = os.Stat(config.TemplateDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = os.Stat(config.StaticDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sStore, err := file.NewSessionFileStore(
|
||||||
|
&file.FileStoreConfig[*models.Session, *store.SessionStore]{
|
||||||
|
FilePathConfig: file.FilePathConfig{Dir: config.SessionDir, FilePrefix: "session", FileSuffix: ".json"},
|
||||||
|
IndexDirFunc: file.DefaultIndexDirFunc[*models.Session, *store.SessionStore],
|
||||||
|
CreateEntityFunc: func() *models.Session {
|
||||||
|
return &models.Session{}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rStore, err := file.NewResponseFileStore(
|
||||||
|
&file.FileStoreConfig[*models.Response, *store.ResponseStore]{
|
||||||
|
FilePathConfig: file.FilePathConfig{Dir: config.ResponseDir, FilePrefix: "response", FileSuffix: ".json"},
|
||||||
|
IndexDirFunc: file.DefaultIndexDirFunc[*models.Response, *store.ResponseStore],
|
||||||
|
CreateEntityFunc: func() *models.Response {
|
||||||
|
return &models.Response{}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rStore.FilePathConfig = file.FilePathConfig{
|
||||||
|
Dir: config.ResponseDir,
|
||||||
|
FilePrefix: "response",
|
||||||
|
FileSuffix: ".json",
|
||||||
|
}
|
||||||
|
|
||||||
|
s := &Server{
|
||||||
|
config,
|
||||||
|
http.NewServeMux(),
|
||||||
|
sStore,
|
||||||
|
rStore,
|
||||||
|
}
|
||||||
|
|
||||||
|
s.mux.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir(config.StaticDir))))
|
||||||
|
s.mux.HandleFunc("/create", s.createExamSessionHandler)
|
||||||
|
s.mux.HandleFunc("/responses/", s.getResponsesHandler)
|
||||||
|
s.mux.HandleFunc("/", s.getExamHandler)
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultServer() (*Server, error) {
|
||||||
|
return NewServer(&Config{
|
||||||
|
SessionDir: GetDefaultSessionDir(),
|
||||||
|
ResponseDir: GetDefaultResponseDir(),
|
||||||
|
TemplateDir: GetDefaultTemplateDir(),
|
||||||
|
StaticDir: GetDefaultStaticDir(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getResponsesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
result := make([]*models.Response, 0)
|
||||||
|
|
||||||
|
urlParts := strings.Split(r.URL.Path, "/")
|
||||||
|
|
||||||
|
sessionID := urlParts[2]
|
||||||
|
|
||||||
|
if r.Method == "GET" {
|
||||||
|
session, err := s.sessionFileStore.Read(sessionID)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, exam := range session.Exams {
|
||||||
|
responses := s.responseFileStore.ReadAll()
|
||||||
|
for _, r := range responses {
|
||||||
|
if r.ID == exam.ID {
|
||||||
|
result = append(result, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = json.NewEncoder(w).Encode(result)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) createExamSessionHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
session := new(models.Session)
|
||||||
|
|
||||||
|
data, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = session.Unmarshal(data)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
memorySession, err := s.sessionFileStore.Create(session)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.NewEncoder(w).Encode(memorySession)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info("Received a new session", "session", memorySession)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getExamHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
urlParts := strings.Split(r.URL.Path, "/")
|
||||||
|
|
||||||
|
sessionID := urlParts[1]
|
||||||
|
token := urlParts[2]
|
||||||
|
|
||||||
|
session, err := s.sessionFileStore.Read(sessionID)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
exam := session.Exams[token]
|
||||||
|
|
||||||
|
if r.Method == "GET" {
|
||||||
|
w.Header().Set("Content-Type", "text/html")
|
||||||
|
|
||||||
|
tplData, err := os.ReadFile(filepath.Join(GetDefaultTemplateDir(), "exam.tpl"))
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
tmpl := template.Must(template.New("exam").Parse(string(tplData)))
|
||||||
|
|
||||||
|
err = tmpl.Execute(w, ExamTemplateData{exam, session.ID})
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Method == "POST" {
|
||||||
|
err := r.ParseForm()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response := new(models.Response)
|
||||||
|
response.UniqueIDFunc = func() string {
|
||||||
|
return exam.GetID()
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Questions = make(map[string]string)
|
||||||
|
for qID, values := range r.Form {
|
||||||
|
for _, aID := range values {
|
||||||
|
response.Questions[qID] = aID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s.responseFileStore.Create(response)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write([]byte("<p>Thank you for your response.</p>"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
s.mux.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateRandomID() string {
|
||||||
|
id := ""
|
||||||
|
for i := 0; i < 6; i++ {
|
||||||
|
id += strconv.Itoa(rand.Intn(9) + 1)
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
slog.SetDefault(slog.New(
|
||||||
|
tint.NewHandler(os.Stdout, &tint.Options{
|
||||||
|
Level: slog.LevelInfo,
|
||||||
|
TimeFormat: time.Kitchen,
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
|
||||||
|
server, err := NewDefaultServer()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info("Probo server started.")
|
||||||
|
http.ListenAndServe(":8080", server)
|
||||||
|
}
|
289
backup/server_test.go
Normal file
289
backup/server_test.go
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.andreafazzi.eu/andrea/probo/pkg/models"
|
||||||
|
"github.com/lmittmann/tint"
|
||||||
|
"github.com/remogatto/prettytest"
|
||||||
|
)
|
||||||
|
|
||||||
|
var examPayload = `
|
||||||
|
{
|
||||||
|
"ID": "fe0a7ee0-f31a-413d-f123-ab5068bcaaaa",
|
||||||
|
"Name": "Test session",
|
||||||
|
"Exams": {
|
||||||
|
"111222": {
|
||||||
|
"id": "fe0a7ee0-f31a-413d-ba81-ab5068bc4c73",
|
||||||
|
"created_at": "0001-01-01T00:00:00Z",
|
||||||
|
"updated_at": "0001-01-01T00:00:00Z",
|
||||||
|
"Participant": {
|
||||||
|
"ID": "1234",
|
||||||
|
"Firstname": "John",
|
||||||
|
"Lastname": "Smith",
|
||||||
|
"Token": "111222",
|
||||||
|
"Attributes": {
|
||||||
|
"class": "1 D LIN"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Quizzes": [
|
||||||
|
{
|
||||||
|
"id": "0610939b-a1a3-4d0e-bbc4-2aae0e8ee4b9",
|
||||||
|
"created_at": "2023-11-27T17:51:53.910642221+01:00",
|
||||||
|
"updated_at": "0001-01-01T00:00:00Z",
|
||||||
|
"hash": "",
|
||||||
|
"question": {
|
||||||
|
"id": "98c0eec9-677f-464e-9e3e-864a859f29a3",
|
||||||
|
"created_at": "0001-01-01T00:00:00Z",
|
||||||
|
"updated_at": "0001-01-01T00:00:00Z",
|
||||||
|
"text": "Question text with #tag1."
|
||||||
|
},
|
||||||
|
"answers": [
|
||||||
|
{
|
||||||
|
"id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc",
|
||||||
|
"text": "Answer 1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "74547724-b905-476f-8cfc-6ee633f92ef3",
|
||||||
|
"text": "Answer 2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "96c1a8ee-c50c-4ebc-89e4-9f3ca356adbd",
|
||||||
|
"text": "Answer 3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "16c4b95e-64ce-4666-8cbe-b66fa59eb23b",
|
||||||
|
"text": "Answer 4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"CreatedAt": "0001-01-01T00:00:00Z",
|
||||||
|
"UpdatedAt": "0001-01-01T00:00:00Z",
|
||||||
|
"DeletedAt": null,
|
||||||
|
"name": "#tag1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"correct": {
|
||||||
|
"id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc",
|
||||||
|
"text": "Answer 1"
|
||||||
|
},
|
||||||
|
"CorrectPos": 0,
|
||||||
|
"type": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "915818c4-b0ce-4efc-8def-7fc8d5fa7454",
|
||||||
|
"created_at": "2023-11-27T17:51:53.91077796+01:00",
|
||||||
|
"updated_at": "0001-01-01T00:00:00Z",
|
||||||
|
"hash": "",
|
||||||
|
"question": {
|
||||||
|
"id": "70793f0d-2855-4140-814e-40167464424b",
|
||||||
|
"created_at": "0001-01-01T00:00:00Z",
|
||||||
|
"updated_at": "0001-01-01T00:00:00Z",
|
||||||
|
"text": "Another question text with #tag1."
|
||||||
|
},
|
||||||
|
"answers": [
|
||||||
|
{
|
||||||
|
"id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc",
|
||||||
|
"text": "Answer 1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "74547724-b905-476f-8cfc-6ee633f92ef3",
|
||||||
|
"text": "Answer 2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "96c1a8ee-c50c-4ebc-89e4-9f3ca356adbd",
|
||||||
|
"text": "Answer 3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "16c4b95e-64ce-4666-8cbe-b66fa59eb23b",
|
||||||
|
"text": "Answer 4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"CreatedAt": "0001-01-01T00:00:00Z",
|
||||||
|
"UpdatedAt": "0001-01-01T00:00:00Z",
|
||||||
|
"DeletedAt": null,
|
||||||
|
"name": "#tag1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"correct": {
|
||||||
|
"id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc",
|
||||||
|
"text": "Answer 1"
|
||||||
|
},
|
||||||
|
"CorrectPos": 0,
|
||||||
|
"type": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"333444": {
|
||||||
|
"id": "12345678-abcd-efgh-ijkl-9876543210ef",
|
||||||
|
"created_at": "2023-12-01T12:00:00Z",
|
||||||
|
"updated_at": "2023-12-01T12:00:00Z",
|
||||||
|
"Participant": {
|
||||||
|
"ID": "5678",
|
||||||
|
"Firstname": "Jane",
|
||||||
|
"Lastname": "Doe",
|
||||||
|
"Token": "333444",
|
||||||
|
"Attributes": {
|
||||||
|
"class": "2 A SCI"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Quizzes": [
|
||||||
|
{
|
||||||
|
"id": "22222222-abcd-efgh-ijkl-9876543210ef",
|
||||||
|
"created_at": "2023-12-01T12:00:00Z",
|
||||||
|
"updated_at": "2023-12-01T12:00:00Z",
|
||||||
|
"hash": "",
|
||||||
|
"question": {
|
||||||
|
"id": "33333333-abcd-efgh-ijkl-9876543210ef",
|
||||||
|
"created_at": "2023-12-01T12:00:00Z",
|
||||||
|
"updated_at": "2023-12-01T12:00:00Z",
|
||||||
|
"text": "Sample question text."
|
||||||
|
},
|
||||||
|
"answers": [
|
||||||
|
{
|
||||||
|
"id": "44444444-abcd-efgh-ijkl-9876543210ef",
|
||||||
|
"text": "Option 1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "55555555-abcd-efgh-ijkl-9876543210ef",
|
||||||
|
"text": "Option 2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "66666666-abcd-efgh-ijkl-9876543210ef",
|
||||||
|
"text": "Option 3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "77777777-abcd-efgh-ijkl-9876543210ef",
|
||||||
|
"text": "Option 4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"CreatedAt": "2023-12-01T12:00:00Z",
|
||||||
|
"UpdatedAt": "2023-12-01T12:00:00Z",
|
||||||
|
"DeletedAt": null,
|
||||||
|
"name": "#tag2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"correct": {
|
||||||
|
"id": "44444444-abcd-efgh-ijkl-9876543210ef",
|
||||||
|
"text": "Option 1"
|
||||||
|
},
|
||||||
|
"CorrectPos": 0,
|
||||||
|
"type": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
type serverTestSuite struct {
|
||||||
|
prettytest.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunner(t *testing.T) {
|
||||||
|
slog.SetDefault(slog.New(
|
||||||
|
tint.NewHandler(os.Stderr, &tint.Options{
|
||||||
|
Level: slog.LevelError,
|
||||||
|
TimeFormat: time.Kitchen,
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
|
||||||
|
prettytest.Run(
|
||||||
|
t,
|
||||||
|
new(serverTestSuite),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *serverTestSuite) TestCreate() {
|
||||||
|
|
||||||
|
DefaultDataDir = "testdata"
|
||||||
|
|
||||||
|
s, err := NewDefaultServer()
|
||||||
|
t.Nil(err)
|
||||||
|
|
||||||
|
if !t.Failed() {
|
||||||
|
request, _ := http.NewRequest(http.MethodPost, "/create", strings.NewReader(examPayload))
|
||||||
|
response := httptest.NewRecorder()
|
||||||
|
|
||||||
|
handler := http.HandlerFunc(s.createExamSessionHandler)
|
||||||
|
|
||||||
|
handler.ServeHTTP(response, request)
|
||||||
|
|
||||||
|
t.Equal(http.StatusOK, response.Code)
|
||||||
|
|
||||||
|
if !t.Failed() {
|
||||||
|
var session *models.Session
|
||||||
|
|
||||||
|
err := json.Unmarshal(response.Body.Bytes(), &session)
|
||||||
|
t.Nil(err)
|
||||||
|
|
||||||
|
path := filepath.Join(GetDefaultSessionDir(), "session_fe0a7ee0-f31a-413d-f123-ab5068bcaaaa.json")
|
||||||
|
|
||||||
|
_, err = os.Stat(path)
|
||||||
|
t.Nil(err)
|
||||||
|
|
||||||
|
defer os.Remove(path)
|
||||||
|
|
||||||
|
t.Equal("fe0a7ee0-f31a-413d-f123-ab5068bcaaaa", session.ID)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *serverTestSuite) TestRead() {
|
||||||
|
|
||||||
|
DefaultDataDir = "testdata"
|
||||||
|
|
||||||
|
s, err := NewDefaultServer()
|
||||||
|
t.Nil(err)
|
||||||
|
|
||||||
|
if !t.Failed() {
|
||||||
|
request, _ := http.NewRequest(http.MethodPost, "/create", strings.NewReader(examPayload))
|
||||||
|
response := httptest.NewRecorder()
|
||||||
|
|
||||||
|
handler := http.HandlerFunc(s.createExamSessionHandler)
|
||||||
|
|
||||||
|
handler.ServeHTTP(response, request)
|
||||||
|
|
||||||
|
t.Equal(http.StatusOK, response.Code)
|
||||||
|
|
||||||
|
if !t.Failed() {
|
||||||
|
var session *models.Session
|
||||||
|
|
||||||
|
err := json.Unmarshal(response.Body.Bytes(), &session)
|
||||||
|
t.Nil(err)
|
||||||
|
|
||||||
|
path := filepath.Join(GetDefaultSessionDir(), "session_fe0a7ee0-f31a-413d-f123-ab5068bcaaaa.json")
|
||||||
|
_, err = os.Stat(path)
|
||||||
|
t.Nil(err)
|
||||||
|
|
||||||
|
if !t.Failed() {
|
||||||
|
defer os.RemoveAll(path)
|
||||||
|
|
||||||
|
request, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("/%s/%s", session.ID, "111222"), nil)
|
||||||
|
response := httptest.NewRecorder()
|
||||||
|
|
||||||
|
handler := http.HandlerFunc(s.getExamHandler)
|
||||||
|
|
||||||
|
handler.ServeHTTP(response, request)
|
||||||
|
|
||||||
|
t.Equal(http.StatusOK, response.Code)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,6 +36,15 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFilter(cmd *cobra.Command, args []string) {
|
func createFilter(cmd *cobra.Command, args []string) {
|
||||||
|
if len(os.Getenv("DEBUG")) > 0 {
|
||||||
|
f, err := tea.LogToFile("debug.log", "debug")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("fatal:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := tea.NewProgram(filter.New()).Run(); err != nil {
|
if _, err := tea.NewProgram(filter.New()).Run(); err != nil {
|
||||||
fmt.Println("Error running program:", err)
|
fmt.Println("Error running program:", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
package filter
|
package filter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.andreafazzi.eu/andrea/probo/pkg/store"
|
|
||||||
"git.andreafazzi.eu/andrea/probo/pkg/store/file"
|
"git.andreafazzi.eu/andrea/probo/pkg/store/file"
|
||||||
|
"github.com/TylerBrock/colorjson"
|
||||||
"github.com/charmbracelet/bubbles/help"
|
"github.com/charmbracelet/bubbles/help"
|
||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
|
"github.com/charmbracelet/bubbles/spinner"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
"github.com/itchyny/gojq"
|
||||||
"github.com/remogatto/sugarfoam/components/group"
|
"github.com/remogatto/sugarfoam/components/group"
|
||||||
"github.com/remogatto/sugarfoam/components/header"
|
"github.com/remogatto/sugarfoam/components/header"
|
||||||
"github.com/remogatto/sugarfoam/components/statusbar"
|
"github.com/remogatto/sugarfoam/components/statusbar"
|
||||||
|
@ -19,18 +23,26 @@ import (
|
||||||
"github.com/remogatto/sugarfoam/layout/tiled"
|
"github.com/remogatto/sugarfoam/layout/tiled"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Store interface {
|
type storeLoadedMsg struct {
|
||||||
|
store []any
|
||||||
}
|
}
|
||||||
|
|
||||||
type storeLoadedMsg struct {
|
type resultMsg struct {
|
||||||
store file.FileStorer[store.Storable]
|
result string
|
||||||
|
}
|
||||||
|
|
||||||
|
type errorMsg struct {
|
||||||
|
error error
|
||||||
}
|
}
|
||||||
|
|
||||||
type FilterModel struct {
|
type FilterModel struct {
|
||||||
// UI
|
// UI
|
||||||
|
textInput *textinput.Model
|
||||||
|
viewport *viewport.Model
|
||||||
group *group.Model
|
group *group.Model
|
||||||
help help.Model
|
help help.Model
|
||||||
statusBar *statusbar.Model
|
statusBar *statusbar.Model
|
||||||
|
spinner spinner.Model
|
||||||
|
|
||||||
// Layout
|
// Layout
|
||||||
document *layout.Layout
|
document *layout.Layout
|
||||||
|
@ -38,8 +50,11 @@ type FilterModel struct {
|
||||||
// Key bindings
|
// Key bindings
|
||||||
bindings *keyBindings
|
bindings *keyBindings
|
||||||
|
|
||||||
// Store
|
// file store
|
||||||
store file.FileStorer[store.Storable]
|
store []any
|
||||||
|
|
||||||
|
// jq
|
||||||
|
lastQuery string
|
||||||
|
|
||||||
state int
|
state int
|
||||||
}
|
}
|
||||||
|
@ -90,20 +105,21 @@ func newBindings(g *group.Model) *keyBindings {
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *FilterModel {
|
func New() *FilterModel {
|
||||||
textinput := textinput.New(
|
textInput := textinput.New(
|
||||||
textinput.WithPlaceholder("Write your jq filter here..."),
|
textinput.WithPlaceholder("Write your jq filter here..."),
|
||||||
)
|
)
|
||||||
|
|
||||||
table := table.New()
|
table := table.New()
|
||||||
viewport := viewport.New()
|
viewport := viewport.New()
|
||||||
|
|
||||||
help := help.New()
|
help := help.New()
|
||||||
|
|
||||||
group := group.New(
|
group := group.New(
|
||||||
group.WithItems(textinput, viewport, table),
|
group.WithItems(textInput, viewport, table),
|
||||||
group.WithLayout(
|
group.WithLayout(
|
||||||
layout.New(
|
layout.New(
|
||||||
layout.WithStyles(&layout.Styles{Container: lipgloss.NewStyle().Padding(1, 0, 1, 0)}),
|
layout.WithStyles(&layout.Styles{Container: lipgloss.NewStyle().Padding(1, 0, 1, 0)}),
|
||||||
layout.WithItem(textinput),
|
layout.WithItem(textInput),
|
||||||
layout.WithItem(tiled.New(viewport, table)),
|
layout.WithItem(tiled.New(viewport, table)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -112,6 +128,12 @@ func New() *FilterModel {
|
||||||
bindings := newBindings(group)
|
bindings := newBindings(group)
|
||||||
statusBar := statusbar.New(bindings)
|
statusBar := statusbar.New(bindings)
|
||||||
|
|
||||||
|
s := spinner.New(
|
||||||
|
spinner.WithStyle(
|
||||||
|
lipgloss.NewStyle().Foreground(lipgloss.Color("265"))),
|
||||||
|
)
|
||||||
|
s.Spinner = spinner.Dot
|
||||||
|
|
||||||
header := header.New(
|
header := header.New(
|
||||||
header.WithContent(
|
header.WithContent(
|
||||||
lipgloss.NewStyle().
|
lipgloss.NewStyle().
|
||||||
|
@ -129,8 +151,11 @@ func New() *FilterModel {
|
||||||
)
|
)
|
||||||
|
|
||||||
return &FilterModel{
|
return &FilterModel{
|
||||||
|
textInput: textInput,
|
||||||
|
viewport: viewport,
|
||||||
group: group,
|
group: group,
|
||||||
statusBar: statusBar,
|
statusBar: statusBar,
|
||||||
|
spinner: s,
|
||||||
document: document,
|
document: document,
|
||||||
bindings: bindings,
|
bindings: bindings,
|
||||||
help: help,
|
help: help,
|
||||||
|
@ -141,7 +166,7 @@ func New() *FilterModel {
|
||||||
func (m *FilterModel) Init() tea.Cmd {
|
func (m *FilterModel) Init() tea.Cmd {
|
||||||
var cmds []tea.Cmd
|
var cmds []tea.Cmd
|
||||||
|
|
||||||
cmds = append(cmds, m.group.Init(), loadParticipantStore())
|
cmds = append(cmds, m.group.Init(), m.loadStore(), m.spinner.Tick)
|
||||||
|
|
||||||
m.group.Focus()
|
m.group.Focus()
|
||||||
|
|
||||||
|
@ -164,6 +189,9 @@ func (m *FilterModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
}
|
}
|
||||||
case storeLoadedMsg:
|
case storeLoadedMsg:
|
||||||
m.handleStoreLoaded(msg)
|
m.handleStoreLoaded(msg)
|
||||||
|
|
||||||
|
case resultMsg:
|
||||||
|
m.handleFiltered(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmds = m.handleState(msg, cmds)
|
cmds = m.handleState(msg, cmds)
|
||||||
|
@ -179,29 +207,121 @@ func (m *FilterModel) handleStoreLoaded(msg tea.Msg) {
|
||||||
storeMsg := msg.(storeLoadedMsg)
|
storeMsg := msg.(storeLoadedMsg)
|
||||||
m.store = storeMsg.store
|
m.store = storeMsg.store
|
||||||
m.state = FilterState
|
m.state = FilterState
|
||||||
|
|
||||||
|
jsonStore, err := m.storeToJson()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.viewport.SetContent(jsonStore)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *FilterModel) handleFiltered(msg tea.Msg) {
|
||||||
|
m.viewport.SetContent(sanitize(msg.(resultMsg).result))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *FilterModel) handleState(msg tea.Msg, cmds []tea.Cmd) []tea.Cmd {
|
func (m *FilterModel) handleState(msg tea.Msg, cmds []tea.Cmd) []tea.Cmd {
|
||||||
_, cmd := m.group.Update(msg)
|
_, cmd := m.group.Update(msg)
|
||||||
|
|
||||||
cmds = append(cmds, cmd)
|
if m.state == LoadingStoreState {
|
||||||
|
return m.updateSpinner(msg, cmd, cmds)
|
||||||
|
}
|
||||||
|
|
||||||
m.statusBar.SetContent(
|
cmds = append(cmds, cmd, m.query(m.textInput.Value()))
|
||||||
formats[FilterState][0],
|
|
||||||
fmt.Sprintf(formats[FilterState][1], len(m.store.ReadAll())),
|
m.statusBar.SetContent(formats[FilterState]...)
|
||||||
formats[FilterState][2],
|
|
||||||
)
|
|
||||||
|
|
||||||
return cmds
|
return cmds
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadParticipantStore() tea.Cmd {
|
func (m *FilterModel) updateSpinner(msg tea.Msg, cmd tea.Cmd, cmds []tea.Cmd) []tea.Cmd {
|
||||||
|
m.spinner, cmd = m.spinner.Update(msg)
|
||||||
|
|
||||||
|
m.statusBar.SetContent(fmt.Sprintf(formats[m.state][0], m.spinner.View()), formats[m.state][1], formats[m.state][2])
|
||||||
|
|
||||||
|
cmds = append(cmds, cmd)
|
||||||
|
|
||||||
|
return cmds
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *FilterModel) loadStore() tea.Cmd {
|
||||||
return func() tea.Msg {
|
return func() tea.Msg {
|
||||||
pStore, err := file.NewDefaultParticipantFileStore()
|
pStore, err := file.NewDefaultParticipantFileStore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return errorMsg{err}
|
||||||
}
|
}
|
||||||
|
|
||||||
return storeLoadedMsg{pStore}
|
jsonStore, err := pStore.Storer.Json()
|
||||||
|
if err != nil {
|
||||||
|
return errorMsg{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
v := make([]any, 0)
|
||||||
|
err = json.Unmarshal(jsonStore, &v)
|
||||||
|
if err != nil {
|
||||||
|
return errorMsg{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
return storeLoadedMsg{v}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *FilterModel) query(input string) tea.Cmd {
|
||||||
|
return func() tea.Msg {
|
||||||
|
if input == m.lastQuery {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.state == LoadingStoreState {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m.lastQuery = input
|
||||||
|
|
||||||
|
query, err := gojq.Parse(input)
|
||||||
|
if err != nil {
|
||||||
|
return errorMsg{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []string
|
||||||
|
|
||||||
|
iter := query.Run(m.store)
|
||||||
|
for {
|
||||||
|
v, ok := iter.Next()
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err, ok := v.(error); ok {
|
||||||
|
return errorMsg{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
f := colorjson.NewFormatter()
|
||||||
|
f.Indent = 2
|
||||||
|
b, err := f.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return errorMsg{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultMsg{strings.Join(result, "\n")}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *FilterModel) storeToJson() (string, error) {
|
||||||
|
f := colorjson.NewFormatter()
|
||||||
|
f.Indent = 2
|
||||||
|
result, err := f.Marshal(m.store)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return sanitize(string(result)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanitize(text string) string {
|
||||||
|
// FIXME: The use of a standard '-' character causes rendering
|
||||||
|
// issues within the viewport. Further investigation is
|
||||||
|
// required to resolve this problem.
|
||||||
|
return strings.Replace(text, "-", "–", -1)
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package filter
|
||||||
|
|
||||||
var (
|
var (
|
||||||
formats = map[int][]string{
|
formats = map[int][]string{
|
||||||
FilterState: []string{"FILTER 📖", "Currently selected %d elements from the store", "STORE 🟢"},
|
FilterState: []string{"FILTER 📖", "Write your jq command in the input box to start filtering. Press enter to return the result.", "STORE 🟢"},
|
||||||
LoadingStoreState: []string{"LOAD %S", "Loading the store...", "STORE 🔴"},
|
LoadingStoreState: []string{"LOAD %s", "Loading the store...", "STORE 🔴"},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
39
cmd/import.go
Normal file
39
cmd/import.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// importCmd represents the import command
|
||||||
|
var importCmd = &cobra.Command{
|
||||||
|
Use: "import",
|
||||||
|
Short: "Import entities",
|
||||||
|
Long: `A longer description that spans multiple lines and likely contains examples
|
||||||
|
and usage of using your command. For example:
|
||||||
|
|
||||||
|
Cobra is a CLI library for Go that empowers applications.
|
||||||
|
This application is a tool to generate the needed files
|
||||||
|
to quickly create a Cobra application.`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("import called")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(importCmd)
|
||||||
|
|
||||||
|
// Here you will define your flags and configuration settings.
|
||||||
|
|
||||||
|
// Cobra supports Persistent Flags which will work for this command
|
||||||
|
// and all subcommands, e.g.:
|
||||||
|
// importCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||||
|
|
||||||
|
// Cobra supports local flags which will only run when this command
|
||||||
|
// is called directly, e.g.:
|
||||||
|
// importCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
|
}
|
50
cmd/participants.go
Normal file
50
cmd/participants.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.andreafazzi.eu/andrea/probo/pkg/store/file"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// participantsCmd represents the participants command
|
||||||
|
var participantsCmd = &cobra.Command{
|
||||||
|
Use: "participants",
|
||||||
|
Short: "A brief description of your command",
|
||||||
|
Long: `A longer description that spans multiple lines and likely contains examples
|
||||||
|
and usage of using your command. For example:
|
||||||
|
|
||||||
|
Cobra is a CLI library for Go that empowers applications.
|
||||||
|
This application is a tool to generate the needed files
|
||||||
|
to quickly create a Cobra application.`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
importCSV(cmd, args)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
importCmd.AddCommand(participantsCmd)
|
||||||
|
|
||||||
|
// Here you will define your flags and configuration settings.
|
||||||
|
|
||||||
|
// Cobra supports Persistent Flags which will work for this command
|
||||||
|
// and all subcommands, e.g.:
|
||||||
|
// participantsCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||||
|
|
||||||
|
// Cobra supports local flags which will only run when this command
|
||||||
|
// is called directly, e.g.:
|
||||||
|
// participantsCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
|
}
|
||||||
|
|
||||||
|
func importCSV(cmd *cobra.Command, args []string) {
|
||||||
|
pStore, err := file.NewDefaultParticipantFileStore()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = pStore.ImportCSV(args[0])
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
28
go.mod
28
go.mod
|
@ -3,12 +3,15 @@ module git.andreafazzi.eu/andrea/probo
|
||||||
go 1.21.6
|
go 1.21.6
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2
|
||||||
|
github.com/charmbracelet/bubbles v0.18.1-0.20240309002305-b9e62cbfe181
|
||||||
github.com/charmbracelet/bubbletea v0.25.0
|
github.com/charmbracelet/bubbletea v0.25.0
|
||||||
github.com/charmbracelet/lipgloss v0.9.1
|
github.com/charmbracelet/lipgloss v0.10.0
|
||||||
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a
|
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/itchyny/gojq v0.12.14
|
||||||
github.com/remogatto/prettytest v0.0.0-20200211072524-6d385e11dcb8
|
github.com/remogatto/prettytest v0.0.0-20200211072524-6d385e11dcb8
|
||||||
github.com/remogatto/sugarfoam v0.0.0-20240206073346-8d2fef68eb8b
|
github.com/remogatto/sugarfoam v0.0.0-20240324175639-28e6bae1b225
|
||||||
github.com/spf13/cobra v1.8.0
|
github.com/spf13/cobra v1.8.0
|
||||||
github.com/spf13/viper v1.18.2
|
github.com/spf13/viper v1.18.2
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
|
@ -18,27 +21,30 @@ require (
|
||||||
require (
|
require (
|
||||||
github.com/atotto/clipboard v0.1.4 // indirect
|
github.com/atotto/clipboard v0.1.4 // indirect
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
github.com/charmbracelet/bubbles v0.18.0 // indirect
|
github.com/containerd/console v1.0.4 // indirect
|
||||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
|
github.com/fatih/color v1.14.1 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
|
github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
github.com/itchyny/timefmt-go v0.1.5 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/kr/pretty v0.3.1 // indirect
|
github.com/kr/pretty v0.3.1 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||||
github.com/muesli/reflow v0.3.0 // indirect
|
github.com/muesli/reflow v0.3.0 // indirect
|
||||||
github.com/muesli/termenv v0.15.2 // indirect
|
github.com/muesli/termenv v0.15.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||||
github.com/rivo/uniseg v0.4.6 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
|
@ -49,10 +55,10 @@ require (
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect
|
||||||
golang.org/x/sync v0.5.0 // indirect
|
golang.org/x/sync v0.6.0 // indirect
|
||||||
golang.org/x/sys v0.15.0 // indirect
|
golang.org/x/sys v0.18.0 // indirect
|
||||||
golang.org/x/term v0.6.0 // indirect
|
golang.org/x/term v0.18.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
|
|
58
go.sum
58
go.sum
|
@ -1,21 +1,25 @@
|
||||||
|
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4=
|
||||||
|
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w=
|
||||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||||
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
|
github.com/charmbracelet/bubbles v0.18.1-0.20240309002305-b9e62cbfe181 h1:ntdtXC9+kcgQYvqa6nyLZLniCEUOhWQknLlz38JpDpM=
|
||||||
github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
|
github.com/charmbracelet/bubbles v0.18.1-0.20240309002305-b9e62cbfe181/go.mod h1:Zlzkn8WOd6QS7RC1BXAY1iw1VLq+xT70UZ1vkEtnrvo=
|
||||||
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
|
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
|
||||||
github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
|
github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
|
||||||
github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
|
github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s=
|
||||||
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
|
github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE=
|
||||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
|
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
|
||||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
|
||||||
|
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
|
@ -28,8 +32,14 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f h1:7LYC+Yfkj3CTRcShK0KOL/w6iTiKyqqBA9a41Wnggw8=
|
||||||
|
github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
|
github.com/itchyny/gojq v0.12.14 h1:6k8vVtsrhQSYgSGg827AD+PVVaB1NLXEdX+dda2oZCc=
|
||||||
|
github.com/itchyny/gojq v0.12.14/go.mod h1:y1G7oO7XkcR1LPZO59KyoCRy08T3j9vDYRV0GgYSS+s=
|
||||||
|
github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE=
|
||||||
|
github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8=
|
||||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
|
@ -42,8 +52,11 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
|
@ -51,8 +64,8 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ
|
||||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
||||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||||
|
@ -67,12 +80,12 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/remogatto/prettytest v0.0.0-20200211072524-6d385e11dcb8 h1:nRDwTcxV9B3elxMt+1xINX0bwaPdpouqp5fbynexY8U=
|
github.com/remogatto/prettytest v0.0.0-20200211072524-6d385e11dcb8 h1:nRDwTcxV9B3elxMt+1xINX0bwaPdpouqp5fbynexY8U=
|
||||||
github.com/remogatto/prettytest v0.0.0-20200211072524-6d385e11dcb8/go.mod h1:jOEnp79oIHy5cvQSHeLcgVJk1GHOOHJHQWps/d1N5Yo=
|
github.com/remogatto/prettytest v0.0.0-20200211072524-6d385e11dcb8/go.mod h1:jOEnp79oIHy5cvQSHeLcgVJk1GHOOHJHQWps/d1N5Yo=
|
||||||
github.com/remogatto/sugarfoam v0.0.0-20240206073346-8d2fef68eb8b h1:GY4q+f7GZo6c8aeLKsXFu5gpIG+EgoWSyPvz7qmP+K8=
|
github.com/remogatto/sugarfoam v0.0.0-20240324175639-28e6bae1b225 h1:LYFmm/8fYZQNhGBC8bHHZbRkfLeA0W1d25nfuPnRG8U=
|
||||||
github.com/remogatto/sugarfoam v0.0.0-20240206073346-8d2fef68eb8b/go.mod h1:wRQC/69u0SRWrXFi8360WNbzrWq70mVoUxY7v8Uykw8=
|
github.com/remogatto/sugarfoam v0.0.0-20240324175639-28e6bae1b225/go.mod h1:MkNrg58aCSx3bijbdHD+E02TmJ6TfGgOim78puJjBOU=
|
||||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
@ -106,16 +119,17 @@ go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o=
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
|
||||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|
|
@ -54,8 +54,10 @@ go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7
|
||||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||||
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
|
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||||
|
@ -64,6 +66,7 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||||
|
@ -72,6 +75,7 @@ golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||||
|
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||||
google.golang.org/api v0.153.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY=
|
google.golang.org/api v0.153.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY=
|
||||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
|
|
@ -25,7 +25,7 @@ type FileStorable interface {
|
||||||
Unmarshal([]byte) error
|
Unmarshal([]byte) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileStorer[T store.Storable] interface {
|
type FileStorer[T FileStorable] interface {
|
||||||
// store.Storer[T]
|
// store.Storer[T]
|
||||||
Create(T, ...string) (T, error)
|
Create(T, ...string) (T, error)
|
||||||
ReadAll() []T
|
ReadAll() []T
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -26,13 +27,14 @@ type Storer[T Storable] interface {
|
||||||
Read(string) (T, error)
|
Read(string) (T, error)
|
||||||
Update(T, string) (T, error)
|
Update(T, string) (T, error)
|
||||||
Delete(string) (T, error)
|
Delete(string) (T, error)
|
||||||
|
Json() ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type FilterStorer[T Storable] interface {
|
// type FilterStorer[T Storable] interface {
|
||||||
Storer[T]
|
// Storer[T]
|
||||||
|
|
||||||
Filter([]T, func(T) bool) []T
|
// Filter([]T, func(T) bool) []T
|
||||||
}
|
// }
|
||||||
|
|
||||||
type Store[T Storable] struct {
|
type Store[T Storable] struct {
|
||||||
ids map[string]T
|
ids map[string]T
|
||||||
|
@ -169,3 +171,7 @@ func (s *Store[T]) Delete(id string) (T, error) {
|
||||||
|
|
||||||
return sEntity, nil
|
return sEntity, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Store[T]) Json() ([]byte, error) {
|
||||||
|
return json.Marshal(s.ReadAll())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue