diff --git a/cmd/create.go b/cmd/create.go index 3886387..77ccfe6 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -33,7 +33,6 @@ func init() { // and all subcommands, e.g.: createCmd.PersistentFlags().StringP("input", "i", "", "Specify an input file") - createCmd.PersistentFlags().Bool("stdin", false, "Consume a string from stdin") // Cobra supports local flags which will only run when this command // is called directly, e.g.: diff --git a/cmd/filter.go b/cmd/filter.go index fd99380..cd1c475 100644 --- a/cmd/filter.go +++ b/cmd/filter.go @@ -8,10 +8,7 @@ import ( "os" "git.andreafazzi.eu/andrea/probo/cmd/filter" - - "bufio" - "io" - "strings" + "git.andreafazzi.eu/andrea/probo/cmd/util" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" @@ -23,56 +20,22 @@ import ( var filterCmd = &cobra.Command{ Use: "filter", Short: "Create a new filter", - Long: "Create a new filter to select quizzes and participants", + Long: "Create a new filter for selecting quizzes and participants", Run: createFilter, } func init() { createCmd.AddCommand(filterCmd) - // Here you will define your flags and configuration settings. - - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // filterCmd.PersistentFlags().String("foo", "", "A help for foo") - - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: filterCmd.Flags().StringP("type", "t", "participants", "Select the type of filter (participants or quizzes)") } 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) - } + f := util.LogToFile() + if f != nil { defer f.Close() } - var b strings.Builder - - stat, err := os.Stdin.Stat() - if err != nil { - panic(err) - } - - if stat.Mode()&os.ModeNamedPipe != 0 || stat.Size() != 0 { - reader := bufio.NewReader(os.Stdin) - for { - r, _, err := reader.ReadRune() - if err != nil && err == io.EOF { - break - } - _, err = b.WriteRune(r) - if err != nil { - fmt.Println("Error getting input:", err) - os.Exit(1) - } - } - } - path, err := cmd.Flags().GetString("input") if err != nil { panic(err) @@ -84,15 +47,15 @@ func createFilter(cmd *cobra.Command, args []string) { } lipgloss.SetColorProfile(termenv.TrueColor) + model, err := tea.NewProgram( - filter.New(path, filterType, strings.TrimSpace(b.String())), + filter.New(path, filterType, util.ReadStdin()), tea.WithOutput(os.Stderr), ).Run() if err != nil { fmt.Println("Error running program:", err) os.Exit(1) } - result := model.(*filter.FilterModel) if result.Result != "" { fmt.Fprintf(os.Stdout, result.Result) diff --git a/cmd/filter/filter.go b/cmd/filter/filter.go index bcd318f..f83de47 100644 --- a/cmd/filter/filter.go +++ b/cmd/filter/filter.go @@ -232,23 +232,24 @@ func (m *FilterModel) marshalJSON() { return } if m.InputJson != "" { - result := make([]interface{}, 2) - err := json.Unmarshal([]byte(m.InputJson), &result[0]) - if err != nil { - panic(err) - } + // result := make([]interface{}, 2) + // err := json.Unmarshal([]byte(m.InputJson), &result[0]) + // if err != nil { + // panic(err) + // } - filtered := fmt.Sprintf("{\"%s\": %s}", m.filterType, m.FilteredJson) - err = json.Unmarshal([]byte(filtered), &result[1]) - if err != nil { - panic(err) - } + // filtered := fmt.Sprintf("{\"%s\": %s}", m.filterType, m.FilteredJson) + // err = json.Unmarshal([]byte(filtered), &result[1]) + // if err != nil { + // panic(err) + // } - resultJson, err := json.Marshal(result) - if err != nil { - panic(err) - } - m.Result = string(resultJson) + // resultJson, err := json.Marshal(result) + // if err != nil { + // panic(err) + // } + // m.Result = string(resultJson) + m.Result = fmt.Sprintf("{%s, \"%s\": %s}", strings.Trim(m.InputJson, "{}"), m.filterType, m.FilteredJson) } else { var result interface{} diff --git a/cmd/session.go b/cmd/session.go index a8a00da..df235f5 100644 --- a/cmd/session.go +++ b/cmd/session.go @@ -1,40 +1,59 @@ /* Copyright © 2024 NAME HERE - */ package cmd import ( "fmt" + "os" + "git.andreafazzi.eu/andrea/probo/cmd/session" + "git.andreafazzi.eu/andrea/probo/cmd/util" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "github.com/muesli/termenv" "github.com/spf13/cobra" ) // sessionCmd represents the session command var sessionCmd = &cobra.Command{ Use: "session", - 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) { - fmt.Println("session called") - }, + Short: "Create a new session or update a given one", + Long: "Create a new session or update a given one.", + Run: updateSession, } func init() { - createCmd.AddCommand(sessionCmd) + updateCmd.AddCommand(sessionCmd) + + sessionCmd.Flags().StringP("script", "s", "", "Execute a tengo script to initiate a session") +} + +func updateSession(cmd *cobra.Command, args []string) { + f := util.LogToFile() + if f != nil { + defer f.Close() + } + + path, err := cmd.Flags().GetString("script") + if err != nil { + panic(err) + } + + lipgloss.SetColorProfile(termenv.TrueColor) + + model, err := tea.NewProgram( + session.New(path, util.ReadStdin()), + tea.WithOutput(os.Stderr), + ).Run() + if err != nil { + fmt.Println("Error running program:", err) + os.Exit(1) + } + result := model.(*session.SessionModel) + if result.Result != "" { + fmt.Fprintf(os.Stdout, result.Result) + } + fmt.Println("session called", path, model) - // Here you will define your flags and configuration settings. - - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // sessionCmd.PersistentFlags().String("foo", "", "A help for foo") - - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - // sessionCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") } diff --git a/cmd/session/format.go b/cmd/session/format.go new file mode 100644 index 0000000..6f99728 --- /dev/null +++ b/cmd/session/format.go @@ -0,0 +1,9 @@ +package session + +var ( + stateFormats = map[int][]string{ + BrowseState: []string{"BROWSE 📖", "Use the navigation keys to browse the content. Press enter to return the result.", "STORE 🟢"}, + LoadingStoreState: []string{"LOAD %s", "Loading the store...", "STORE 🔴"}, + ErrorState: []string{"ERROR 📖", "%v", "STORE 🟢"}, + } +) diff --git a/cmd/session/message.go b/cmd/session/message.go new file mode 100644 index 0000000..9bb76fd --- /dev/null +++ b/cmd/session/message.go @@ -0,0 +1,19 @@ +package session + +import "git.andreafazzi.eu/andrea/probo/pkg/store/file" + +type storeLoadedMsg struct { + store *file.SessionFileStore +} + +type resultMsg struct { + result []any +} + +type errorMsg struct { + error error +} + +type scriptExecutedMsg struct { + result string +} diff --git a/cmd/session/session.go b/cmd/session/session.go new file mode 100644 index 0000000..9e56e29 --- /dev/null +++ b/cmd/session/session.go @@ -0,0 +1,471 @@ +package session + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "strings" + + "git.andreafazzi.eu/andrea/probo/pkg/models" + "git.andreafazzi.eu/andrea/probo/pkg/store/file" + "github.com/alecthomas/chroma/quick" + "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/key" + "github.com/charmbracelet/bubbles/spinner" + btTable "github.com/charmbracelet/bubbles/table" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/glamour" + "github.com/charmbracelet/huh" + "github.com/charmbracelet/lipgloss" + "github.com/d5/tengo/v2" + "github.com/d5/tengo/v2/stdlib" + "github.com/remogatto/sugarfoam/components/form" + "github.com/remogatto/sugarfoam/components/group" + "github.com/remogatto/sugarfoam/components/header" + "github.com/remogatto/sugarfoam/components/statusbar" + "github.com/remogatto/sugarfoam/components/table" + "github.com/remogatto/sugarfoam/components/viewport" + "github.com/remogatto/sugarfoam/layout" + "github.com/remogatto/sugarfoam/layout/tiled" +) + +type SessionModel struct { + // UI + form *form.Model + viewport *viewport.Model + table *table.Model + group *group.Model + help help.Model + statusBar *statusbar.Model + spinner spinner.Model + + // Layout + document *layout.Layout + + // Key bindings + bindings *keyBindings + + // file store + store *file.SessionFileStore + result []any + + // json + FilteredJson string + InputJson string + Result string + + // session + session *models.Session + + // markdown + mdRenderer *glamour.TermRenderer + + // filter file + scriptFilePath string + + state int +} + +type keyBindings struct { + group *group.Model + + quit, enter key.Binding +} + +func (k *keyBindings) ShortHelp() []key.Binding { + keys := make([]key.Binding, 0) + + current := k.group.Current() + + switch item := current.(type) { + case *viewport.Model: + keys = append( + keys, + item.KeyMap.Up, + item.KeyMap.Down, + ) + } + + keys = append( + keys, + k.quit, + ) + + return keys +} + +func (k keyBindings) FullHelp() [][]key.Binding { + return [][]key.Binding{ + { + k.quit, + }, + } +} + +func newBindings(g *group.Model) *keyBindings { + return &keyBindings{ + group: g, + quit: key.NewBinding( + key.WithKeys("esc"), key.WithHelp("esc", "Quit app"), + ), + enter: key.NewBinding( + key.WithKeys("enter"), key.WithHelp("enter", "Quit app and return the results"), + ), + } +} + +func New(path string, stdin string) *SessionModel { + form := form.New( + form.WithGroups(huh.NewGroup( + huh.NewInput(). + Title("Session name"). + Description("Enter the name of the session"), + ))) + + form. + WithShowHelp(false). + WithTheme(huh.ThemeDracula()) + + viewport := viewport.New() + + table := table.New(table.WithRelWidths(20, 30, 30, 20)) + table.Model.SetColumns([]btTable.Column{ + {Title: "Token", Width: 20}, + {Title: "Lastname", Width: 20}, + {Title: "Firstname", Width: 20}, + {Title: "Class", Width: 20}, + }) + + help := help.New() + + group := group.New( + group.WithItems(form, table, viewport), + group.WithLayout( + layout.New( + layout.WithStyles(&layout.Styles{Container: lipgloss.NewStyle().Padding(1, 0, 1, 0)}), + layout.WithItem(form), + layout.WithItem(tiled.New(table, viewport)), + ), + ), + ) + + bindings := newBindings(group) + statusBar := statusbar.New(bindings) + + s := spinner.New( + spinner.WithStyle( + lipgloss.NewStyle().Foreground(lipgloss.Color("265"))), + ) + s.Spinner = spinner.Dot + + header := header.New( + header.WithContent( + lipgloss.NewStyle(). + Bold(true). + Border(lipgloss.NormalBorder(), false, false, true, false). + Render("✨ Create session ✨"), + ), + ) + + document := layout.New( + layout.WithStyles(&layout.Styles{Container: lipgloss.NewStyle().Margin(1)}), + layout.WithItem(header), + layout.WithItem(group), + layout.WithItem(statusBar), + ) + + renderer, err := glamour.NewTermRenderer( + glamour.WithStandardStyle("dracula"), + glamour.WithWordWrap(80), + ) + if err != nil { + panic(err) + } + + return &SessionModel{ + form: form, + table: table, + viewport: viewport, + group: group, + statusBar: statusBar, + spinner: s, + document: document, + mdRenderer: renderer, + bindings: bindings, + help: help, + scriptFilePath: path, + InputJson: stdin, + } + +} + +func (m *SessionModel) Init() tea.Cmd { + var cmds []tea.Cmd + + cmds = append(cmds, m.group.Init(), m.loadStore(), m.spinner.Tick) + + m.group.Focus() + + return tea.Batch(cmds...) +} + +func (m *SessionModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var cmds []tea.Cmd + + switch msg := msg.(type) { + + case tea.WindowSizeMsg: + m.handleWindowSize(msg) + + case tea.KeyMsg: + switch { + case key.Matches(msg, m.bindings.quit): + m.FilteredJson = "" + return m, tea.Quit + + case key.Matches(msg, m.bindings.enter): + m.marshalJSON() + return m, tea.Quit + } + + case storeLoadedMsg: + cmds = append(cmds, m.handleStoreLoaded(msg)) + + case scriptExecutedMsg: + m.handleScriptExecuted(msg) + + case errorMsg: + m.handleError(msg) + m.state = ErrorState + } + + cmds = m.handleState(msg, cmds) + + return m, tea.Batch(cmds...) +} + +func (m *SessionModel) View() string { + return m.document.View() +} + +func (m *SessionModel) executeScript(path string) tea.Cmd { + return func() tea.Msg { + if m.scriptFilePath == "" { + return nil + } + + sessionJson, err := json.Marshal(models.Session{Exams: map[string]*models.Exam{}}) + if err != nil { + panic(err) + } + + script, err := os.ReadFile(m.scriptFilePath) + if err != nil { + return errorMsg{err} + } + + s := tengo.NewScript(script) + + s.SetImports(stdlib.GetModuleMap("fmt", "json")) + _ = s.Add("input", m.InputJson) + _ = s.Add("output", string(sessionJson)) + + // compile the source + c, err := s.Compile() + if err != nil { + return errorMsg{err} + } + + if err := c.Run(); err != nil { + return errorMsg{err} + } + + return scriptExecutedMsg{fmt.Sprintf("%s", c.Get("output"))} + + } +} + +func (m *SessionModel) marshalJSON() { + var result interface{} + + err := json.Unmarshal([]byte(m.FilteredJson), &result) + if err != nil { + panic(err) + } + + resultJson, err := json.Marshal(result) + if err != nil { + panic(err) + } + m.Result = string(resultJson) + +} + +func (m *SessionModel) showErrorOnStatusBar(err error) { + m.statusBar.SetContent( + stateFormats[ErrorState][0], + fmt.Sprintf(stateFormats[ErrorState][1], err), + stateFormats[ErrorState][2], + ) +} + +func (m *SessionModel) updateTableContent(session *models.Session) { + rows := make([]btTable.Row, 0) + + for token, exam := range session.Exams { + rows = append(rows, btTable.Row{ + token, + exam.Participant.Lastname, + exam.Participant.Firstname, + exam.Participant.Attributes.Get("class"), + }) + + } + m.table.SetRows(rows) +} + +func (m *SessionModel) updateViewportContent(session *models.Session) { + currentToken := m.table.SelectedRow()[0] + currentExam := session.Exams[currentToken] + if currentExam == nil { + panic("Current token is not associate to any exam") + } + + md, err := currentExam.ToMarkdown() + if err != nil { + m.showErrorOnStatusBar(err) + } + + result, err := m.mdRenderer.Render(md) + if err != nil { + m.showErrorOnStatusBar(err) + } + + m.viewport.SetContent(result) + +} + +func (m *SessionModel) createMDRenderer(width int) *glamour.TermRenderer { + renderer, err := glamour.NewTermRenderer( + glamour.WithStandardStyle("dracula"), + glamour.WithWordWrap(m.viewport.GetWidth()), + ) + if err != nil { + panic(err) + } + return renderer +} + +func (m *SessionModel) handleWindowSize(msg tea.WindowSizeMsg) { + m.group.SetSize(msg.Width, msg.Height) + m.document.SetSize(msg.Width, msg.Height) + m.mdRenderer = m.createMDRenderer(msg.Width) +} + +func (m *SessionModel) handleError(msg tea.Msg) { + err := msg.(errorMsg) + + m.statusBar.SetContent( + stateFormats[ErrorState][0], + fmt.Sprintf(stateFormats[ErrorState][1], err.error), + stateFormats[ErrorState][2], + ) +} + +func (m *SessionModel) handleScriptExecuted(msg tea.Msg) { + session := new(models.Session) + jsonData := []byte(msg.(scriptExecutedMsg).result) + + err := json.Unmarshal(jsonData, &session) + if err != nil { + panic(err) + } + + m.session = session + + m.updateTableContent(session) + m.updateViewportContent(session) + + m.state = BrowseState +} + +func (m *SessionModel) handleStoreLoaded(msg tea.Msg) tea.Cmd { + storeMsg := msg.(storeLoadedMsg) + m.store = storeMsg.store + + return m.executeScript(m.scriptFilePath) + +} + +func (m *SessionModel) handleState(msg tea.Msg, cmds []tea.Cmd) []tea.Cmd { + _, cmd := m.group.Update(msg) + + if m.state == LoadingStoreState { + return m.updateSpinner(msg, cmd, cmds) + } + + if m.state == BrowseState { + m.updateViewportContent(m.session) + } + + cmds = append(cmds, cmd /*, m.query(m.textInput.Value())*/) + + if m.state != ErrorState { + m.statusBar.SetContent(stateFormats[BrowseState]...) + } + + return cmds +} + +func (m *SessionModel) updateSpinner(msg tea.Msg, cmd tea.Cmd, cmds []tea.Cmd) []tea.Cmd { + m.spinner, cmd = m.spinner.Update(msg) + + m.statusBar.SetContent(fmt.Sprintf(stateFormats[m.state][0], m.spinner.View()), stateFormats[m.state][1], stateFormats[m.state][2]) + + cmds = append(cmds, cmd) + + return cmds +} + +func (m *SessionModel) loadStore() tea.Cmd { + return func() tea.Msg { + sStore, err := file.NewDefaultSessionFileStore() + if err != nil { + return errorMsg{err} + } + + return storeLoadedMsg{sStore} + } +} + +func toColoredJson(data []any) (string, error) { + result, err := json.MarshalIndent(data, "", " ") + if err != nil { + return "", err + } + + coloredBytes := make([]byte, 0) + buffer := bytes.NewBuffer(coloredBytes) + + err = quick.Highlight(buffer, string(result), "json", "terminal16m", "dracula") + if err != nil { + panic(err) + } + + return sanitize(buffer.String()), nil +} + +func toJson(data []any) (string, error) { + result, err := json.Marshal(data) + if err != nil { + return "", err + } + return 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) +} diff --git a/cmd/session/state.go b/cmd/session/state.go new file mode 100644 index 0000000..4a28a77 --- /dev/null +++ b/cmd/session/state.go @@ -0,0 +1,7 @@ +package session + +const ( + LoadingStoreState = iota + BrowseState + ErrorState +) diff --git a/cmd/update.go b/cmd/update.go new file mode 100644 index 0000000..c0bf16e --- /dev/null +++ b/cmd/update.go @@ -0,0 +1,40 @@ +/* +Copyright © 2024 NAME HERE + +*/ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// updateCmd represents the update command +var updateCmd = &cobra.Command{ + Use: "update", + 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) { + fmt.Println("update called") + }, +} + +func init() { + rootCmd.AddCommand(updateCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // updateCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // updateCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/util/util.go b/cmd/util/util.go new file mode 100644 index 0000000..b503c6f --- /dev/null +++ b/cmd/util/util.go @@ -0,0 +1,49 @@ +package util + +import ( + "bufio" + "fmt" + "io" + "os" + "strings" + + tea "github.com/charmbracelet/bubbletea" +) + +func LogToFile() *os.File { + if len(os.Getenv("DEBUG")) > 0 { + f, err := tea.LogToFile("probo-debug.log", "[DEBUG]") + if err != nil { + fmt.Println("fatal:", err) + os.Exit(1) + } + return f + } + return nil +} + +func ReadStdin() string { + var b strings.Builder + + stat, err := os.Stdin.Stat() + if err != nil { + panic(err) + } + + if stat.Mode()&os.ModeNamedPipe != 0 || stat.Size() != 0 { + reader := bufio.NewReader(os.Stdin) + for { + r, _, err := reader.ReadRune() + if err != nil && err == io.EOF { + break + } + _, err = b.WriteRune(r) + if err != nil { + fmt.Println("Error getting input:", err) + os.Exit(1) + } + } + } + + return strings.TrimSpace(b.String()) +} diff --git a/go.mod b/go.mod index 65ea53f..6727a60 100644 --- a/go.mod +++ b/go.mod @@ -3,38 +3,38 @@ module git.andreafazzi.eu/andrea/probo go 1.21.6 require ( - github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 + github.com/alecthomas/chroma v0.10.0 github.com/charmbracelet/bubbles v0.18.1-0.20240309002305-b9e62cbfe181 github.com/charmbracelet/bubbletea v0.25.0 + github.com/charmbracelet/huh v0.3.0 github.com/charmbracelet/lipgloss v0.10.0 + github.com/d5/tengo/v2 v2.17.0 github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a github.com/google/uuid v1.6.0 github.com/itchyny/gojq v0.12.14 + github.com/lmittmann/tint v1.0.4 + github.com/muesli/termenv v0.15.2 github.com/remogatto/prettytest v0.0.0-20200211072524-6d385e11dcb8 - github.com/remogatto/sugarfoam v0.0.0-20240324175639-28e6bae1b225 + github.com/remogatto/sugarfoam v0.0.0-20240418083243-766dd70853af github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.18.2 gopkg.in/yaml.v2 v2.4.0 - gorm.io/gorm v1.25.6 ) require ( github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/catppuccin/go v0.2.0 // indirect github.com/containerd/console v1.0.4 // indirect - github.com/fatih/color v1.14.1 // indirect + github.com/dlclark/regexp2 v1.4.0 // indirect github.com/fsnotify/fsnotify v1.7.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/itchyny/timefmt-go v0.1.5 // indirect - github.com/jinzhu/inflection v1.0.0 // indirect - github.com/jinzhu/now v1.1.5 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magiconair/properties v1.8.7 // 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-runewidth v0.0.15 // indirect @@ -42,7 +42,6 @@ require ( github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect - github.com/muesli/termenv v0.15.2 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect diff --git a/go.sum b/go.sum index 2a8ba18..b53536e 100644 --- a/go.sum +++ b/go.sum @@ -1,25 +1,35 @@ -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/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= +github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= +github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= 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/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA= +github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= github.com/charmbracelet/bubbles v0.18.1-0.20240309002305-b9e62cbfe181 h1:ntdtXC9+kcgQYvqa6nyLZLniCEUOhWQknLlz38JpDpM= 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/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= +github.com/charmbracelet/huh v0.3.0 h1:CxPplWkgW2yUTDDG0Z4S5HH8SJOosWHd4LxCvi0XsKE= +github.com/charmbracelet/huh v0.3.0/go.mod h1:fujUdKX8tC45CCSaRQdw789O6uaCRwx8l2NDyKfC4jA= github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s= github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE= github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= 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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/d5/tengo/v2 v2.17.0 h1:BWUN9NoJzw48jZKiYDXDIF3QrIVZRm1uV1gTzeZ2lqM= +github.com/d5/tengo/v2 v2.17.0/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8= 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.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/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= -github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= +github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= +github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= 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/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -32,29 +42,22 @@ 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/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/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/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/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= -github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lmittmann/tint v1.0.4 h1:LeYihpJ9hyGvE0w+K2okPTGUdVLfng1+nDNVR4vWISc= +github.com/lmittmann/tint v1.0.4/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= 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/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -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= @@ -80,8 +83,8 @@ 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/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/sugarfoam v0.0.0-20240324175639-28e6bae1b225 h1:LYFmm/8fYZQNhGBC8bHHZbRkfLeA0W1d25nfuPnRG8U= -github.com/remogatto/sugarfoam v0.0.0-20240324175639-28e6bae1b225/go.mod h1:MkNrg58aCSx3bijbdHD+E02TmJ6TfGgOim78puJjBOU= +github.com/remogatto/sugarfoam v0.0.0-20240418083243-766dd70853af h1:rSEwVRdJxMq4RK2kI1LEVhM5J3yg3pcvlRYy1vjn7mQ= +github.com/remogatto/sugarfoam v0.0.0-20240418083243-766dd70853af/go.mod h1:WeyW6WPrlPDwa48kDIytaLxXKyRjOwLp4BEd2tEGY1Y= 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.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= @@ -109,6 +112,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= @@ -123,7 +127,6 @@ golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= 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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= @@ -142,5 +145,3 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/gorm v1.25.6 h1:V92+vVda1wEISSOMtodHVRcUIOPYa2tgQtyF+DfFx+A= -gorm.io/gorm v1.25.6/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= diff --git a/go.work.sum b/go.work.sum index ab4116c..c25bbcf 100644 --- a/go.work.sum +++ b/go.work.sum @@ -12,6 +12,8 @@ github.com/aymanbagabas/go-osc52 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZs github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI= +github.com/disintegration/imageorient v0.0.0-20180920195336-8147d86e83ec/go.mod h1:K0KBFIr1gWu/C1Gp10nFAcAE4hsB7JxE6OgLijrJ8Sk= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -41,11 +43,14 @@ github.com/nats-io/nkeys v0.4.6/go.mod h1:4DxZNzenSVd1cYQoAa8948QY3QDjrHfcfVADym github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= +github.com/remogatto/imgcat v0.0.0-20240318115229-ee6a34ad38fe/go.mod h1:e6G+BhMs87z7k9UKiGmV8tLWguKaNic9zlb3N+yC5Vc= github.com/sagikazarmark/crypt v0.17.0/go.mod h1:SMtHTvdmsZMuY/bpZoqokSoChIrcJ/epOxZN58PbZDg= github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y= github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= +github.com/srwiley/oksvg v0.0.0-20220128195007-1f435e4c2b44/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q= +github.com/srwiley/rasterx v0.0.0-20220128185129-2efea2b9ea41/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI= go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U= @@ -54,12 +59,12 @@ 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.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/image v0.0.0-20220413100746-70e8d0d3baa9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= 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.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.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/pkg/models/exam.go b/pkg/models/exam.go index cd36235..00b9785 100644 --- a/pkg/models/exam.go +++ b/pkg/models/exam.go @@ -10,9 +10,8 @@ import ( type Exam struct { Meta - SessionID string - Participant *Participant - Quizzes []*Quiz + Participant *Participant `json:"participant"` + Quizzes []*Quiz `json:"quizzes"` } func (e *Exam) String() string { @@ -35,3 +34,17 @@ func (e *Exam) Marshal() ([]byte, error) { func (e *Exam) Unmarshal(data []byte) error { return json.Unmarshal(data, e) } + +func (e *Exam) ToMarkdown() (string, error) { + result := "" + for _, quiz := range e.Quizzes { + quizMD, err := QuizToMarkdown(quiz) + if err != nil { + return "", err + } + result += fmt.Sprintf(quizMD) + result += "\n" + } + + return strings.TrimRight(fmt.Sprintf("# %s %s \n %s", e.Participant.Lastname, e.Participant.Firstname, result), "\n"), nil +} diff --git a/pkg/models/session.go b/pkg/models/session.go index 3e3e0fa..bf566dd 100644 --- a/pkg/models/session.go +++ b/pkg/models/session.go @@ -9,16 +9,16 @@ import ( type Session struct { Meta - Name string - Exams map[string]*Exam + Title string `json:"name"` + Exams map[string]*Exam `json:"exams"` } func (s *Session) String() string { - return s.Name + return s.Title } func (s *Session) GetHash() string { - return fmt.Sprintf("%x", sha256.Sum256([]byte(s.Name))) + return fmt.Sprintf("%x", sha256.Sum256([]byte(s.Title))) } func (s *Session) Marshal() ([]byte, error) {