From: Johannes Keßler Date: Fri, 18 Nov 2022 14:00:52 +0000 (+0100) Subject: go client experiments with bubbletea X-Git-Tag: 1.1~20 X-Git-Url: http://91.132.146.200/gitweb/?a=commitdiff_plain;h=7243d72bd3974ceca9d7f0d3bb041878b856190f;p=scientia.git go client experiments with bubbletea --- diff --git a/client/go-client/README b/client/go-client/README new file mode 100644 index 0000000..7c64d47 --- /dev/null +++ b/client/go-client/README @@ -0,0 +1,9 @@ +This is a client written in go to be used with scientia +https://://www.bananas-playground.net/projekt/scientia + +!WARNING! +This is a very simple, with limited experience written, go program. +Use at own risk and feel free to improve. + +Howto build: +Nothing special, just go build -o scientia \ No newline at end of file diff --git a/client/go-client/_scientia.go b/client/go-client/_scientia.go new file mode 100644 index 0000000..9ce1858 --- /dev/null +++ b/client/go-client/_scientia.go @@ -0,0 +1,254 @@ +package main + +import ( + "errors" + "fmt" + "log" + "math/rand" + "os" + "flag" + "gopkg.in/yaml.v2" + "net/http" + "io/ioutil" + "bytes" + "mime/multipart" + "encoding/json" +) + +/** + * scientia + * + * Copyright 2022 Johannes Keßler + * + * https://www.bananas-playground.net/projekt/scientia/ + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the COMMON DEVELOPMENT AND DISTRIBUTION LICENSE + * + * You should have received a copy of the + * COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 + * along with this program. If not, see http://www.sun.com/cddl/cddl.html + */ + +const website = "https://www.bananas-playground.net/projekt/scientia" +const version = "1.0" +// used for non-existing default config +const letters = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-_" + +// command line parameters +var optsVerbose bool +var optsCreateConfig bool +var optsDebug bool + +// config +var cfg Config +// config file struct +type Config struct { + Endpoint struct { + Host string `yaml:"host"` + Secret string `yaml:"secret"` + } `yaml:"endpoint"` +} + +// response json struct +type Response struct { + Message string `json:"message"` + Status int `json:"status"` +} + +/** + * Main + */ +func main() { + + // parse commandline parameters + flag.BoolVar(&optsVerbose, "verbose", false, "Produce verbose output") + flag.BoolVar(&optsCreateConfig, "create-config-file", false, "Create default config file") + flag.BoolVar(&optsDebug, "debug", false, "Print debug infos") + flag.Parse() + if optsDebug { + fmt.Println("verbose:", optsVerbose) + fmt.Println("create-config-file:", optsCreateConfig) + fmt.Println("debug:", optsDebug) + } + + // load the config and populate Config + loadConfig() + + // get the payload + payload := getInput() + if optsDebug { log.Println(payload) } + + // do the upload and get the response + responseString := uploadCall(payload) + response := Response{} + json.Unmarshal([]byte(responseString), &response) + + // print the result and link to the pasty + fmt.Printf("Status: %d\n", response.Status) + fmt.Printf("Message: %s\n", response.Message) +} + + + + +/** + * Check and display error with additional message + */ +func errorCheck(e error, msg string) { + if e != nil { + log.Fatal(msg,e) + } +} + +/** + * just a random string + */ +func randStringBytes(n int) string { + b := make([]byte, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return string(b) +} + +/** + * load or even create a default config + * $HOME/.scientia.yaml + */ +func loadConfig() { + homeDir, err := os.UserHomeDir() + errorCheck(err, "No $HOME directory available?") + if optsVerbose { log.Printf("Your $HOME: %s \n", homeDir) } + + var configFile = homeDir + "/.scientia.yaml" + + if _, err := os.Stat(configFile); errors.Is(err, os.ErrNotExist) { + log.Printf("Config file not found: %s \n", configFile) + + if optsCreateConfig { + log.Printf("Creating new default config file: %s \n", configFile) + + newConfig, err := os.Create(configFile) + errorCheck(err, "Can not create config file!") + defer newConfig.Close() + + + _, err = fmt.Fprintf(newConfig, "# scientia go client config file.\n") + errorCheck(err, "Can not write to new config file") + fmt.Fprintf(newConfig, "# See %s for more details.\n", website) + fmt.Fprintf(newConfig, "# Version: %s\n", version) + fmt.Fprintf(newConfig, "endpoint:\n") + fmt.Fprintf(newConfig, " host: http://your-scientia-endpoi.nt\n") + fmt.Fprintf(newConfig, " secret: %s\n", randStringBytes(50)) + + log.Fatalf("New default config file created: - %s - Edit and launch again!",configFile) + } + } + + existingConfigFile, err := os.Open(configFile) + errorCheck(err, "Can not open config file") + defer existingConfigFile.Close() + if optsVerbose { log.Printf("Reading config file: %s \n", configFile) } + + var decoder = yaml.NewDecoder(existingConfigFile) + err = decoder.Decode(&cfg) + errorCheck(err, "Can not decode config file") + + if cfg.Endpoint.Host == "" || cfg.Endpoint.Secret == "" { + log.Fatal("Empty config?") + } + + if optsDebug { + log.Println(cfg.Endpoint.Host) + log.Println(cfg.Endpoint.Secret) + } +} + +/** + * Do a http POST call to the defined endpoint + * and upload the payload + * Return response body as string + */ +func uploadCall(payload string) string { + + if optsVerbose { log.Println("Starting to upload data") } + if optsDebug { log.Println(payload) } + if len(payload) == 0 { + log.Fatal("Nothing provided to upload") + } + + // Buffer to store our request body as bytes + var requestBody bytes.Buffer + + // Create a multipart writer + multiPartWriter := multipart.NewWriter(&requestBody) + + // file field + fileWriter, err := multiPartWriter.CreateFormFile("pasty", "pastyfile") + errorCheck(err, "Can not create form file field") + fileWriter.Write([]byte(payload)) + + dlField, err := multiPartWriter.CreateFormField("dl") + errorCheck(err, "Can not create form dl field") + dlField.Write([]byte(cfg.Endpoint.Secret)) + + multiPartWriter.Close() + + req, err := http.NewRequest(http.MethodPost, cfg.Endpoint.Host, &requestBody) + errorCheck(err, "Can not create http request") + // We need to set the content type from the writer, it includes necessary boundary as well + req.Header.Set("Content-Type", multiPartWriter.FormDataContentType()) + req.Header.Set("User-Agent", "scientiaAgent/1.0"); + + // Do the request + client := &http.Client{} + response, err := client.Do(req) + errorCheck(err, "POST request failed") + + responseBody, err := ioutil.ReadAll(response.Body) + errorCheck(err, "Can not read response body") + + if optsVerbose { log.Println("Request done") } + if optsDebug { + log.Printf("Response status code: %d\n",response.StatusCode) + log.Printf("Response headers: %#v\n", response.Header) + log.Println(string(responseBody)) + } + + return string(responseBody) +} + +/** + * check if file is provided as commandline argument + * or piped into + * return the read data as string + */ +func getInput() string { + if optsVerbose { log.Println("Getting input") } + + var inputString string + + if filename := flag.Arg(0); filename != "" { + if optsVerbose { log.Println("Read from file argument") } + + bytes, err := os.ReadFile(filename) + errorCheck(err, "Error opening file") + inputString = string(bytes) + } else { + stat, _ := os.Stdin.Stat() + if (stat.Mode() & os.ModeCharDevice) == 0 { + if optsVerbose { log.Println("data is being piped") } + + bytes, _ := ioutil.ReadAll(os.Stdin) + inputString = string(bytes) + } + } + + if len(inputString) == 0 { + log.Fatal("Nothing provided to upload") + } + + return inputString +} diff --git a/client/go-client/go.mod b/client/go-client/go.mod new file mode 100644 index 0000000..bd776c7 --- /dev/null +++ b/client/go-client/go.mod @@ -0,0 +1,28 @@ +module scientia/go-client + +go 1.19 + +require ( + github.com/charmbracelet/bubbles v0.14.0 + github.com/charmbracelet/bubbletea v0.23.1 + github.com/charmbracelet/lipgloss v0.6.0 +) + +require ( + github.com/atotto/clipboard v0.1.4 // indirect + github.com/aymanbagabas/go-osc52 v1.0.3 // indirect + github.com/containerd/console v1.0.3 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/reflow v0.3.0 // indirect + github.com/muesli/termenv v0.13.0 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/sahilm/fuzzy v0.1.0 // indirect + golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + golang.org/x/text v0.3.7 // indirect +) diff --git a/client/go-client/go.sum b/client/go-client/go.sum new file mode 100644 index 0000000..45ebbf2 --- /dev/null +++ b/client/go-client/go.sum @@ -0,0 +1,58 @@ +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 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZspWD+Mg= +github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= +github.com/charmbracelet/bubbles v0.14.0 h1:DJfCwnARfWjZLvMglhSQzo76UZ2gucuHPy9jLWX45Og= +github.com/charmbracelet/bubbles v0.14.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc= +github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4= +github.com/charmbracelet/bubbletea v0.23.1 h1:CYdteX1wCiCzKNUlwm25ZHBIc1GXlYFyUIte8WPvhck= +github.com/charmbracelet/bubbletea v0.23.1/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU= +github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= +github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= +github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY= +github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk= +github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +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/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +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-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= +github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= +github.com/muesli/cancelreader v0.2.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +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/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= +github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= +github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= +github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0= +github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= +github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/client/go-client/scientia b/client/go-client/scientia new file mode 100755 index 0000000..2eacc3a Binary files /dev/null and b/client/go-client/scientia differ diff --git a/client/go-client/scientia.go b/client/go-client/scientia.go index e1f669c..fc60015 100644 --- a/client/go-client/scientia.go +++ b/client/go-client/scientia.go @@ -1,254 +1,128 @@ package main import ( - "errors" "fmt" - "log" - "math/rand" + "io" "os" - "flag" - "gopkg.in/yaml.v2" - "net/http" - "io/ioutil" - "bytes" - "mime/multipart" - "encoding/json" -) - -/** - * scientia - * - * Copyright 2022 Johannes Keßler - * - * https://www.bananas-playground.net/projekt/scientia/ - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the COMMON DEVELOPMENT AND DISTRIBUTION LICENSE - * - * You should have received a copy of the - * COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 - * along with this program. If not, see http://www.sun.com/cddl/cddl.html - */ - -const website = "https://www.bananas-playground.net/projekt/selfpaste" -const version = "1.0" -// used for non-existing default config -const letters = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-_" - -// command line parameters -var optsVerbose bool -var optsCreateConfig bool -var optsDebug bool - -// config -var cfg Config -// config file struct -type Config struct { - Endpoint struct { - Host string `yaml:"host"` - Secret string `yaml:"secret"` - } `yaml:"endpoint"` -} - -// response json struct -type Response struct { - Message string `json:"message"` - Status int `json:"status"` -} -/** - * Main - */ -func main() { + "github.com/charmbracelet/bubbles/list" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" +) - // parse commandline parameters - flag.BoolVar(&optsVerbose, "verbose", false, "Produce verbose output") - flag.BoolVar(&optsCreateConfig, "create-config-file", false, "Create default config file") - flag.BoolVar(&optsDebug, "debug", false, "Print debug infos") - flag.Parse() - if optsDebug { - fmt.Println("verbose:", optsVerbose) - fmt.Println("create-config-file:", optsCreateConfig) - fmt.Println("debug:", optsDebug) - } +const listHeight = 14 - // load the config and populate Config - loadConfig() +var ( + titleStyle = lipgloss.NewStyle().MarginLeft(2) + itemStyle = lipgloss.NewStyle().PaddingLeft(4) + selectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170")) + paginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4) + helpStyle = list.DefaultStyles().HelpStyle.PaddingLeft(4).PaddingBottom(1) + quitTextStyle = lipgloss.NewStyle().Margin(1, 0, 2, 4) +) - // get the payload - payload := getInput() - if optsDebug { log.Println(payload) } +type item string - // do the upload and get the response - responseString := uploadCall(payload) - response := Response{} - json.Unmarshal([]byte(responseString), &response) +func (i item) FilterValue() string { return "" } - // print the result and link to the pasty - fmt.Printf("Status: %d\n", response.Status) - fmt.Printf("Message: %s\n", response.Message) -} +type itemDelegate struct{} +func (d itemDelegate) Height() int { return 1 } +func (d itemDelegate) Spacing() int { return 0 } +func (d itemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil } +func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) { + i, ok := listItem.(item) + if !ok { + return + } + str := fmt.Sprintf("%d. %s", index+1, i) -/** - * Check and display error with additional message - */ -func errorCheck(e error, msg string) { - if e != nil { - log.Fatal(msg,e) + fn := itemStyle.Render + if index == m.Index() { + fn = func(s string) string { + return selectedItemStyle.Render("> " + s) + } } -} -/** - * just a random string - */ -func randStringBytes(n int) string { - b := make([]byte, n) - for i := range b { - b[i] = letters[rand.Intn(len(letters))] - } - return string(b) + fmt.Fprint(w, fn(str)) } -/** - * load or even create a default config - * $HOME/.scientia.yaml - */ -func loadConfig() { - homeDir, err := os.UserHomeDir() - errorCheck(err, "No $HOME directory available?") - if optsVerbose { log.Printf("Your $HOME: %s \n", homeDir) } - - var configFile = homeDir + "/.selfpaste.yaml" - - if _, err := os.Stat(configFile); errors.Is(err, os.ErrNotExist) { - log.Printf("Config file not found: %s \n", configFile) - - if optsCreateConfig { - log.Printf("Creating new default config file: %s \n", configFile) - - newConfig, err := os.Create(configFile) - errorCheck(err, "Can not create config file!") - defer newConfig.Close() - +type model struct { + list list.Model + choice string + quitting bool +} - _, err = fmt.Fprintf(newConfig, "# scientia go client config file.\n") - errorCheck(err, "Can not write to new config file") - fmt.Fprintf(newConfig, "# See %s for more details.\n", website) - fmt.Fprintf(newConfig, "# Version: %s\n", version) - fmt.Fprintf(newConfig, "endpoint:\n") - fmt.Fprintf(newConfig, " host: http://your-scientia-endpoi.nt\n") - fmt.Fprintf(newConfig, " secret: %s\n", randStringBytes(50)) +func (m model) Init() tea.Cmd { + return nil +} - log.Fatalf("New default config file created: - %s - Edit and launch again!",configFile) - } +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.WindowSizeMsg: + m.list.SetWidth(msg.Width) + return m, nil + + case tea.KeyMsg: + switch keypress := msg.String(); keypress { + case "ctrl+c": + m.quitting = true + return m, tea.Quit + + case "enter": + i, ok := m.list.SelectedItem().(item) + if ok { + m.choice = string(i) + } + return m, tea.Quit + } } - existingConfigFile, err := os.Open(configFile) - errorCheck(err, "Can not open config file") - defer existingConfigFile.Close() - if optsVerbose { log.Printf("Reading config file: %s \n", configFile) } - - var decoder = yaml.NewDecoder(existingConfigFile) - err = decoder.Decode(&cfg) - errorCheck(err, "Can not decode config file") + var cmd tea.Cmd + m.list, cmd = m.list.Update(msg) + return m, cmd +} - if cfg.Endpoint.Host == "" || cfg.Endpoint.Secret == "" { - log.Fatal("Empty config?") +func (m model) View() string { + if m.choice != "" { + return quitTextStyle.Render(fmt.Sprintf("%s? Sounds good to me.", m.choice)) } - - if optsDebug { - log.Println(cfg.Endpoint.Host) - log.Println(cfg.Endpoint.Secret) + if m.quitting { + return quitTextStyle.Render("Not hungry? That’s cool.") } + return "\n" + m.list.View() } -/** - * Do a http POST call to the defined endpoint - * and upload the payload - * Return response body as string - */ -func uploadCall(payload string) string { - - if optsVerbose { log.Println("Starting to upload data") } - if optsDebug { log.Println(payload) } - if len(payload) == 0 { - log.Fatal("Nothing provided to upload") +func main() { + items := []list.Item{ + item("Ramen"), + item("Tomato Soup"), + item("Hamburgers"), + item("Cheeseburgers"), + item("Currywurst"), + item("Okonomiyaki"), + item("Pasta"), + item("Fillet Mignon"), + item("Caviar"), + item("Just Wine"), } - // Buffer to store our request body as bytes - var requestBody bytes.Buffer - - // Create a multipart writer - multiPartWriter := multipart.NewWriter(&requestBody) - - // file field - fileWriter, err := multiPartWriter.CreateFormFile("pasty", "pastyfile") - errorCheck(err, "Can not create form file field") - fileWriter.Write([]byte(payload)) - - dlField, err := multiPartWriter.CreateFormField("dl") - errorCheck(err, "Can not create form dl field") - dlField.Write([]byte(cfg.Endpoint.Secret)) - - multiPartWriter.Close() + const defaultWidth = 20 - req, err := http.NewRequest(http.MethodPost, cfg.Endpoint.Host, &requestBody) - errorCheck(err, "Can not create http request") - // We need to set the content type from the writer, it includes necessary boundary as well - req.Header.Set("Content-Type", multiPartWriter.FormDataContentType()) - req.Header.Set("User-Agent", "selfpasteAgent/1.0"); + l := list.New(items, itemDelegate{}, defaultWidth, listHeight) + l.Title = "What do you want for dinner?" + l.SetShowStatusBar(false) + l.SetFilteringEnabled(false) + l.Styles.Title = titleStyle + l.Styles.PaginationStyle = paginationStyle + l.Styles.HelpStyle = helpStyle - // Do the request - client := &http.Client{} - response, err := client.Do(req) - errorCheck(err, "POST request failed") + m := model{list: l} - responseBody, err := ioutil.ReadAll(response.Body) - errorCheck(err, "Can not read response body") - - if optsVerbose { log.Println("Request done") } - if optsDebug { - log.Printf("Response status code: %d\n",response.StatusCode) - log.Printf("Response headers: %#v\n", response.Header) - log.Println(string(responseBody)) - } - - return string(responseBody) -} - -/** - * check if file is provided as commandline argument - * or piped into - * return the read data as string - */ -func getInput() string { - if optsVerbose { log.Println("Getting input") } - - var inputString string - - if filename := flag.Arg(0); filename != "" { - if optsVerbose { log.Println("Read from file argument") } - - bytes, err := os.ReadFile(filename) - errorCheck(err, "Error opening file") - inputString = string(bytes) - } else { - stat, _ := os.Stdin.Stat() - if (stat.Mode() & os.ModeCharDevice) == 0 { - if optsVerbose { log.Println("data is being piped") } - - bytes, _ := ioutil.ReadAll(os.Stdin) - inputString = string(bytes) - } - } - - if len(inputString) == 0 { - log.Fatal("Nothing provided to upload") + if _, err := tea.NewProgram(m).Run(); err != nil { + fmt.Println("Error running program:", err) + os.Exit(1) } - - return inputString -} +} \ No newline at end of file