diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cb7b615..79465e94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org). +## 4.0.5 + +- Fixes runtime crash #135 +- Option to disable colors in cli help `mangal config info -k cli.colored` (why not? =P) +- Improved `config info` command output. It now shows default value and env variable name. +- Internal improvements + ## 4.0.4 - Fix manga tags and genres being the same inside ComicInfo.xml #133 diff --git a/anilist/search.go b/anilist/search.go index d855e16d..e2eb7dbd 100644 --- a/anilist/search.go +++ b/anilist/search.go @@ -86,7 +86,6 @@ func GetByID(id int) (*Manga, error) { } // SearchByName returns a list of mangas that match the given name. -// TODO: keep failed names in cache for a minute func SearchByName(name string) ([]*Manga, error) { name = normalizedName(name) _ = query.Remember(name, 1) diff --git a/cmd/config.go b/cmd/config.go index f5f8c2a6..3c3e8646 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -94,6 +94,7 @@ var configInfoCmd = &cobra.Command{ if i < len(fields)-1 { fmt.Println() + fmt.Println() } } }, diff --git a/cmd/inline.go b/cmd/inline.go index 34da2691..923bedf9 100644 --- a/cmd/inline.go +++ b/cmd/inline.go @@ -6,10 +6,10 @@ import ( "fmt" "github.com/invopop/jsonschema" "github.com/metafates/mangal/anilist" - "github.com/metafates/mangal/constant" "github.com/metafates/mangal/converter" "github.com/metafates/mangal/filesystem" "github.com/metafates/mangal/inline" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/provider" "github.com/metafates/mangal/query" "github.com/metafates/mangal/source" @@ -36,7 +36,7 @@ func init() { inlineCmd.Flags().BoolP("populate-pages", "p", false, "Populate chapters pages") inlineCmd.Flags().BoolP("fetch-metadata", "f", false, "Populate manga metadata") inlineCmd.Flags().BoolP("include-anilist-manga", "a", false, "Include anilist manga in the output") - lo.Must0(viper.BindPFlag(constant.MetadataFetchAnilist, inlineCmd.Flags().Lookup("fetch-metadata"))) + lo.Must0(viper.BindPFlag(key.MetadataFetchAnilist, inlineCmd.Flags().Lookup("fetch-metadata"))) inlineCmd.Flags().StringP("output", "o", "", "output file") @@ -81,7 +81,7 @@ When using the json flag manga selector could be omitted. That way, it will sele lo.Must0(cmd.MarkFlagRequired("json")) } - if _, err := converter.Get(viper.GetString(constant.FormatsUse)); err != nil { + if _, err := converter.Get(viper.GetString(key.FormatsUse)); err != nil { handleErr(err) } }, @@ -91,7 +91,7 @@ When using the json flag manga selector could be omitted. That way, it will sele err error ) - for _, name := range viper.GetStringSlice(constant.DownloaderDefaultSources) { + for _, name := range viper.GetStringSlice(key.DownloaderDefaultSources) { if name == "" { handleErr(errors.New("source not set")) } diff --git a/cmd/integration.go b/cmd/integration.go index 4a1b8201..91fa95ad 100644 --- a/cmd/integration.go +++ b/cmd/integration.go @@ -3,9 +3,9 @@ package cmd import ( "fmt" "github.com/AlecAivazis/survey/v2" - "github.com/metafates/mangal/constant" "github.com/metafates/mangal/icon" "github.com/metafates/mangal/integration/anilist" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/log" "github.com/metafates/mangal/open" "github.com/samber/lo" @@ -32,15 +32,15 @@ var integrationAnilistCmd = &cobra.Command{ See https://github.com/metafates/mangal/wiki/Anilist-Integration for more information`, Run: func(cmd *cobra.Command, args []string) { if lo.Must(cmd.Flags().GetBool("disable")) { - viper.Set(constant.AnilistEnable, false) - viper.Set(constant.AnilistCode, "") - viper.Set(constant.AnilistSecret, "") - viper.Set(constant.AnilistID, "") + viper.Set(key.AnilistEnable, false) + viper.Set(key.AnilistCode, "") + viper.Set(key.AnilistSecret, "") + viper.Set(key.AnilistID, "") log.Info("Anilist integration disabled") handleErr(viper.WriteConfig()) } - if !viper.GetBool(constant.AnilistEnable) { + if !viper.GetBool(key.AnilistEnable) { confirm := survey.Confirm{ Message: "Anilist is disabled. Enable?", Default: false, @@ -53,7 +53,7 @@ See https://github.com/metafates/mangal/wiki/Anilist-Integration for more inform return } - viper.Set(constant.AnilistEnable, response) + viper.Set(key.AnilistEnable, response) err = viper.WriteConfig() if err != nil { switch err.(type) { @@ -67,7 +67,7 @@ See https://github.com/metafates/mangal/wiki/Anilist-Integration for more inform } } - if viper.GetString(constant.AnilistID) == "" { + if viper.GetString(key.AnilistID) == "" { input := survey.Input{ Message: "Anilsit client ID is not set. Please enter it:", Help: "", @@ -80,12 +80,12 @@ See https://github.com/metafates/mangal/wiki/Anilist-Integration for more inform return } - viper.Set(constant.AnilistID, response) + viper.Set(key.AnilistID, response) err = viper.WriteConfig() handleErr(err) } - if viper.GetString(constant.AnilistSecret) == "" { + if viper.GetString(key.AnilistSecret) == "" { input := survey.Input{ Message: "Anilsit client secret is not set. Please enter it:", Help: "", @@ -98,12 +98,12 @@ See https://github.com/metafates/mangal/wiki/Anilist-Integration for more inform return } - viper.Set(constant.AnilistSecret, response) + viper.Set(key.AnilistSecret, response) err = viper.WriteConfig() handleErr(err) } - if viper.GetString(constant.AnilistCode) == "" { + if viper.GetString(key.AnilistCode) == "" { authURL := anilist.New().AuthURL() confirmOpenInBrowser := survey.Confirm{ Message: "Open browser to authenticate with Anilist?", @@ -134,7 +134,7 @@ See https://github.com/metafates/mangal/wiki/Anilist-Integration for more inform return } - viper.Set(constant.AnilistCode, response) + viper.Set(key.AnilistCode, response) err = viper.WriteConfig() handleErr(err) } diff --git a/cmd/mini.go b/cmd/mini.go index 9b16aa7e..0225e9a4 100644 --- a/cmd/mini.go +++ b/cmd/mini.go @@ -1,8 +1,8 @@ package cmd import ( - "github.com/metafates/mangal/constant" "github.com/metafates/mangal/converter" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/mini" "github.com/samber/lo" "github.com/spf13/cobra" @@ -24,7 +24,7 @@ var miniCmd = &cobra.Command{ Long: `Launch mangal in the mini mode. Will try to mimic ani-cli.`, PreRun: func(cmd *cobra.Command, args []string) { - if _, err := converter.Get(viper.GetString(constant.FormatsUse)); err != nil { + if _, err := converter.Get(viper.GetString(key.FormatsUse)); err != nil { handleErr(err) } }, diff --git a/cmd/root.go b/cmd/root.go index 08fa018f..0970c65a 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -7,6 +7,7 @@ import ( "github.com/metafates/mangal/constant" "github.com/metafates/mangal/converter" "github.com/metafates/mangal/icon" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/log" "github.com/metafates/mangal/provider" "github.com/metafates/mangal/style" @@ -28,16 +29,16 @@ func init() { lo.Must0(rootCmd.RegisterFlagCompletionFunc("format", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return converter.Available(), cobra.ShellCompDirectiveDefault })) - lo.Must0(viper.BindPFlag(constant.FormatsUse, rootCmd.PersistentFlags().Lookup("format"))) + lo.Must0(viper.BindPFlag(key.FormatsUse, rootCmd.PersistentFlags().Lookup("format"))) rootCmd.PersistentFlags().StringP("icons", "I", "", "icons variant") lo.Must0(rootCmd.RegisterFlagCompletionFunc("icons", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return icon.AvailableVariants(), cobra.ShellCompDirectiveDefault })) - lo.Must0(viper.BindPFlag(constant.IconsVariant, rootCmd.PersistentFlags().Lookup("icons"))) + lo.Must0(viper.BindPFlag(key.IconsVariant, rootCmd.PersistentFlags().Lookup("icons"))) rootCmd.PersistentFlags().BoolP("write-history", "H", true, "write history of the read chapters") - lo.Must0(viper.BindPFlag(constant.HistorySaveOnRead, rootCmd.PersistentFlags().Lookup("write-history"))) + lo.Must0(viper.BindPFlag(key.HistorySaveOnRead, rootCmd.PersistentFlags().Lookup("write-history"))) rootCmd.PersistentFlags().StringSliceP("source", "S", []string{}, "default source to use") lo.Must0(rootCmd.RegisterFlagCompletionFunc("source", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { @@ -53,7 +54,7 @@ func init() { return sources, cobra.ShellCompDirectiveDefault })) - lo.Must0(viper.BindPFlag(constant.DownloaderDefaultSources, rootCmd.PersistentFlags().Lookup("source"))) + lo.Must0(viper.BindPFlag(key.DownloaderDefaultSources, rootCmd.PersistentFlags().Lookup("source"))) rootCmd.Flags().BoolP("continue", "c", false, "continue reading") @@ -76,7 +77,7 @@ var rootCmd = &cobra.Command{ Long: constant.AsciiArtLogo + "\n" + style.New().Italic(true).Foreground(color.HiRed).Render(" - The ultimate cli manga downloader"), PreRun: func(cmd *cobra.Command, args []string) { - if _, err := converter.Get(viper.GetString(constant.FormatsUse)); err != nil { + if _, err := converter.Get(viper.GetString(key.FormatsUse)); err != nil { handleErr(err) } }, @@ -95,16 +96,18 @@ var rootCmd = &cobra.Command{ // Execute adds all child commands to the root command and sets flags appropriately. func Execute() { - // colored cobra injection - cc.Init(&cc.Config{ - RootCmd: rootCmd, - Headings: cc.HiCyan + cc.Bold + cc.Underline, - Commands: cc.HiYellow + cc.Bold, - Example: cc.Italic, - ExecName: cc.Bold, - Flags: cc.Bold, - FlagsDataType: cc.Italic + cc.HiBlue, - }) + if viper.GetBool(key.CliColored) { + // colored cobra injection + cc.Init(&cc.Config{ + RootCmd: rootCmd, + Headings: cc.HiCyan + cc.Bold + cc.Underline, + Commands: cc.HiYellow + cc.Bold, + Example: cc.Italic, + ExecName: cc.Bold, + Flags: cc.Bold, + FlagsDataType: cc.Italic + cc.HiBlue, + }) + } if err := rootCmd.Execute(); err != nil { fmt.Println(err) diff --git a/cmd/sources.go b/cmd/sources.go index 362f8181..12c624bf 100644 --- a/cmd/sources.go +++ b/cmd/sources.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/metafates/mangal/color" "github.com/metafates/mangal/constant" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/tui" "github.com/metafates/mangal/util" "github.com/spf13/viper" @@ -147,7 +148,7 @@ var sourcesGenCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { cmd.SetOut(os.Stdout) - author := viper.GetString(constant.GenAuthor) + author := viper.GetString(key.GenAuthor) if author == "" { usr, err := user.Current() if err == nil { diff --git a/config/config.go b/config/config.go index 3d808bb9..db6628cd 100644 --- a/config/config.go +++ b/config/config.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/metafates/mangal/constant" "github.com/metafates/mangal/filesystem" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/where" "github.com/samber/lo" "github.com/spf13/viper" @@ -74,7 +75,7 @@ func setDefaults() { // resolveAliases resolves the aliases for the paths func resolveAliases() { home := lo.Must(os.UserHomeDir()) - path := viper.GetString(constant.DownloaderPath) + path := viper.GetString(key.DownloaderPath) if path == "~" { path = home @@ -84,5 +85,5 @@ func resolveAliases() { path = os.ExpandEnv(path) - viper.Set(constant.DownloaderPath, path) + viper.Set(key.DownloaderPath, path) } diff --git a/config/default.go b/config/default.go index 909bdca8..96d6b400 100644 --- a/config/default.go +++ b/config/default.go @@ -5,8 +5,14 @@ import ( "fmt" "github.com/metafates/mangal/color" "github.com/metafates/mangal/constant" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/style" + "github.com/samber/lo" "github.com/spf13/viper" + "reflect" + "strconv" + "strings" + "text/template" ) // Field represents a single config field @@ -55,24 +61,73 @@ func (f *Field) MarshalJSON() ([]byte, error) { return json.Marshal(field) } -// Pretty format field as string for further cli output +var prettyTemplate = lo.Must(template.New("pretty").Funcs(template.FuncMap{ + "faint": style.Faint, + "bold": style.Bold, + "purple": style.Fg(color.Purple), + "blue": style.Fg(color.Blue), + "cyan": style.Fg(color.Cyan), + "value": func(k string) any { return viper.Get(k) }, + "hl": func(v any) string { + switch value := v.(type) { + case bool: + b := strconv.FormatBool(value) + if value { + return style.Fg(color.Green)(b) + } + + return style.Fg(color.Red)(b) + case string: + return style.Fg(color.Yellow)(value) + default: + return fmt.Sprint(value) + } + }, + "typename": func(v any) string { return reflect.TypeOf(v).String() }, +}).Parse(`{{ faint .Description }} +{{ blue "Key:" }} {{ purple .Key }} +{{ blue "Env:" }} {{ .Env }} +{{ blue "Value:" }} {{ hl (value .Key) }} +{{ blue "Default:" }} {{ hl (.Value) }} +{{ blue "Type:" }} {{ typename .Value }}`)) + func (f *Field) Pretty() string { - return fmt.Sprintf( - `%s -%s: %s = %s -`, - style.Faint(f.Description), - style.Fg(color.Purple)(f.Key), - style.Fg(color.Yellow)(f.typeName()), - style.Fg(color.Cyan)(fmt.Sprintf("%v", viper.Get(f.Key))), - ) + var b strings.Builder + + lo.Must0(prettyTemplate.Execute(&b, f)) + + return b.String() } +func (f *Field) Env() string { + env := strings.ToUpper(EnvKeyReplacer.Replace(f.Key)) + appPrefix := strings.ToUpper(constant.Mangal + "_") + + if strings.HasPrefix(env, appPrefix) { + return env + } + + return appPrefix + env +} + +// Pretty format field as string for further cli output +//func (f *Field) Pretty() string { +// return fmt.Sprintf( +// `%s +//%s: %s = %s +//`, +// style.Faint(f.Description), +// style.Fg(color.Purple)(f.Key), +// style.Fg(color.Yellow)(f.typeName()), +// style.Fg(color.Cyan)(fmt.Sprintf("%v", viper.Get(f.Key))), +// ) +//} + // defaults contains all default values for the config. // It must contain all fields defined in the constant package. -var defaults = [constant.DefinedFieldsCount]Field{ +var defaults = [key.DefinedFieldsCount]Field{ { - constant.DownloaderPath, + key.DownloaderPath, ".", `Where to download manga Absolute or relative. @@ -80,7 +135,7 @@ You can also use tilde (~) to refer to your home directory or use env variables. Examples: ~/... or $HOME/... or ${MANGA_PATH}-mangal`, }, { - constant.DownloaderChapterNameTemplate, + key.DownloaderChapterNameTemplate, "[{padded-index}] {chapter}", `Key template of the downloaded chapters Path forbidden symbols will be replaced with "_" @@ -94,261 +149,266 @@ Available variables: {source} - name of the source`, }, { - constant.DownloaderAsync, + key.DownloaderAsync, true, `Use asynchronous downloader (faster) Do no turn it off unless you have some issues`, }, { - constant.DownloaderCreateMangaDir, + key.DownloaderCreateMangaDir, true, `Create a subdirectory for each manga`, }, { - constant.DownloaderCreateVolumeDir, + key.DownloaderCreateVolumeDir, false, `Create a subdirectory for each volume`, }, { - constant.DownloaderReadDownloaded, + key.DownloaderReadDownloaded, true, "If chapter is already downloaded, read it instead of downloading it to temp", }, { - constant.DownloaderRedownloadExisting, + key.DownloaderRedownloadExisting, false, `Redownload chapters that already exist`, }, { - constant.DownloaderDefaultSources, + key.DownloaderDefaultSources, []string{}, `Default sources to use. Will prompt if not set. Type "mangal sources list" to show available sources`, }, { - constant.DownloaderStopOnError, + key.DownloaderStopOnError, false, `Stop downloading other chapters on error`, }, { - constant.DownloaderDownloadCover, + key.DownloaderDownloadCover, true, `Whether to download manga cover or not`, }, { - constant.FormatsUse, + key.FormatsUse, "pdf", `Default format to export chapters Available options are: pdf, zip, cbz, plain`, }, { - constant.FormatsSkipUnsupportedImages, + key.FormatsSkipUnsupportedImages, true, `Will skip images that can't be converted to the specified format Example: if you want to export to pdf, but some images are gifs, they will be skipped`, }, { - constant.MetadataFetchAnilist, + key.MetadataFetchAnilist, true, `Fetch metadata from Anilist It will also cache the results to not spam the API`, }, { - constant.MetadataComicInfoXML, + key.MetadataComicInfoXML, true, `Generate ComicInfo.xml file for each chapter`, }, { - constant.MetadataComicInfoXMLAddDate, + key.MetadataComicInfoXMLAddDate, true, `Add series release date to each chapter in ComicInfo.xml file`, }, { - constant.MetadataComicInfoXMLAlternativeDate, + key.MetadataComicInfoXMLAlternativeDate, false, "Use download date instead of series release date in ComicInfo.xml file", }, { - constant.MetadataComicInfoXMLTagRelevanceThreshold, + key.MetadataComicInfoXMLTagRelevanceThreshold, 60, "Minimum relevance of a tag to be added to ComicInfo.xml file. From 0 to 100", }, { - constant.MetadataSeriesJSON, + key.MetadataSeriesJSON, true, `Generate series.json file for each manga`, }, { - constant.MiniSearchLimit, + key.MiniSearchLimit, 20, `Limit of search results to show`, }, { - constant.IconsVariant, + key.IconsVariant, "plain", `Icons variant. Available options are: emoji, kaomoji, plain, squares, nerd (nerd-font required)`, }, { - constant.ReaderPDF, + key.ReaderPDF, "", "What app to use to open pdf files", }, { - constant.ReaderCBZ, + key.ReaderCBZ, "", "What app to use to open cbz files", }, { - constant.ReaderZIP, + key.ReaderZIP, "", "What app to use to open zip files", }, { - constant.RaderPlain, + key.RaderPlain, "", "What app to use to open folders", }, { - constant.ReaderBrowser, + key.ReaderBrowser, "", "What browser to use to open webpages", }, { - constant.ReaderFolder, + key.ReaderFolder, "", "What app to use to open folders", }, { - constant.ReaderReadInBrowser, + key.ReaderReadInBrowser, false, "Open chapter url in browser instead of downloading it", }, { - constant.HistorySaveOnRead, + key.HistorySaveOnRead, true, "Save history on chapter read", }, { - constant.HistorySaveOnDownload, + key.HistorySaveOnDownload, false, "Save history on chapter download", }, { - constant.SearchShowQuerySuggestions, + key.SearchShowQuerySuggestions, true, "Show query suggestions in when searching", }, { - constant.MangadexLanguage, + key.MangadexLanguage, "en", `Preferred language for mangadex Use "any" to show all languages`, }, { - constant.MangadexNSFW, + key.MangadexNSFW, false, "Show NSFW content", }, { - constant.MangadexShowUnavailableChapters, + key.MangadexShowUnavailableChapters, false, "Show chapters that cannot be downloaded", }, { - constant.InstallerUser, + key.InstallerUser, "metafates", "Custom scrapers repository owner", }, { - constant.InstallerRepo, + key.InstallerRepo, "mangal-scrapers", "Custom scrapers repository name", }, { - constant.InstallerBranch, + key.InstallerBranch, "main", "Custom scrapers repository branch", }, { - constant.GenAuthor, + key.GenAuthor, "", "Key to use in generated scrapers as author", }, { - constant.LogsWrite, + key.LogsWrite, false, "Write logs", }, { - constant.LogsLevel, + key.LogsLevel, "info", `Available options are: (from less to most verbose) panic, fatal, error, warn, info, debug, trace`, }, { - constant.LogsJson, + key.LogsJson, false, "Use json format for logs", }, { - constant.AnilistEnable, + key.AnilistEnable, false, "Enable Anilist integration", }, { - constant.AnilistCode, + key.AnilistCode, "", "Anilist code to use for authentication", }, { - constant.AnilistID, + key.AnilistID, "", "Anilist ID to use for authentication", }, { - constant.AnilistSecret, + key.AnilistSecret, "", "Anilist secret to use for authentication", }, { - constant.AnilistLinkOnMangaSelect, + key.AnilistLinkOnMangaSelect, true, "Show link to Anilist on manga select", }, { - constant.TUIItemSpacing, + key.TUIItemSpacing, 1, "Spacing between items in the TUI", }, { - constant.TUIReadOnEnter, + key.TUIReadOnEnter, true, "Read chapter on enter if other chapters aren't selected", }, { - constant.TUISearchPromptString, + key.TUISearchPromptString, "> ", "Search prompt string to use", }, { - constant.TUIShowURLs, + key.TUIShowURLs, true, "Show URLs under list items", }, { - constant.TUIReverseChapters, + key.TUIReverseChapters, false, "Reverse chapters order", }, { - constant.TUIShowDownloadedPath, + key.TUIShowDownloadedPath, true, "Show path where chapters were downloaded", }, + { + key.CliColored, + true, + "Use colors in CLI help page", + }, } func init() { @@ -364,9 +424,9 @@ func init() { count++ } - if count != constant.DefinedFieldsCount { - panic(fmt.Sprintf("Expected %d default values, got %d", constant.DefinedFieldsCount, count)) + if count != key.DefinedFieldsCount { + panic(fmt.Sprintf("Expected %d default values, got %d", key.DefinedFieldsCount, count)) } } -var Default = make(map[string]Field, constant.DefinedFieldsCount) +var Default = make(map[string]Field, key.DefinedFieldsCount) diff --git a/constant/meta.go b/constant/meta.go index 233a338e..3146c864 100644 --- a/constant/meta.go +++ b/constant/meta.go @@ -2,6 +2,6 @@ package constant const ( Mangal = "mangal" - Version = "4.0.4" + Version = "4.0.5" UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36" ) diff --git a/constant/template.go b/constant/template.go index b10bc17f..31d243ac 100644 --- a/constant/template.go +++ b/constant/template.go @@ -14,6 +14,9 @@ const SourceTemplate = `{{ $divider := repeat "-" (plus (max (len .URL) (len .Na {{ $divider }} +---@alias manga { name: string, url: string, author: string|nil, genres: string|nil, summary: string|nil } +---@alias chapter { name: string, url: string, volume: string|nil, manga_summary: string|nil, manga_author: string|nil, manga_genres: string|nil } +---@alias page { url: string, index: number } ----- IMPORTS ----- @@ -30,46 +33,24 @@ const SourceTemplate = `{{ $divider := repeat "-" (plus (max (len .URL) (len .Na ----- MAIN ----- --- Searches for manga with given query. ---[[ -Manga fields: - name - string, required - url - string, required - author - string, optional - genres - string (multiple genres are divided by comma ','), optional - summary - string, optional ---]] --- @param query Query to search for --- @return Table of mangas +-- @param query string Query to search for +-- @return manga[] Table of mangas function {{ .SearchMangaFn }}(query) return {} end --- Gets the list of all manga chapters. ---[[ -Chapter fields: - name - string, required - url - string, required - volume - string, optional - manga_summary - string, optional (in case you can't get it from search page) - manga_author - string, optional - manga_genres - string (multiple genres are divided by comma ','), optional ---]] --- @param mangaURL URL of the manga --- @return Table of chapters +-- @param mangaURL string URL of the manga +-- @return chapter[] Table of chapters function {{ .MangaChaptersFn }}(mangaURL) return {} end --- Gets the list of all pages of a chapter. ---[[ -Page fields: - url - string, required - index - uint, required ---]] --- @param chapterURL URL of the chapter --- @return Table of pages +-- @param chapterURL string URL of the chapter +-- @return page[] function {{ .ChapterPagesFn }}(chapterURL) return {} end diff --git a/converter/cbz/cbz.go b/converter/cbz/cbz.go index dbb6390d..6fe479da 100644 --- a/converter/cbz/cbz.go +++ b/converter/cbz/cbz.go @@ -4,8 +4,8 @@ import ( "archive/zip" "bytes" "encoding/xml" - "github.com/metafates/mangal/constant" "github.com/metafates/mangal/filesystem" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/source" "github.com/metafates/mangal/util" "github.com/spf13/viper" @@ -57,7 +57,7 @@ func SaveTo(chapter *source.Chapter, to string) error { } } - if viper.GetBool(constant.MetadataComicInfoXML) { + if viper.GetBool(key.MetadataComicInfoXML) { comicInfo := chapter.ComicInfo() marshalled, err := xml.MarshalIndent(comicInfo, "", " ") if err == nil { diff --git a/converter/cbz/cbz_test.go b/converter/cbz/cbz_test.go index 9525e585..7f4feb59 100644 --- a/converter/cbz/cbz_test.go +++ b/converter/cbz/cbz_test.go @@ -6,6 +6,7 @@ import ( "github.com/metafates/mangal/config" "github.com/metafates/mangal/constant" "github.com/metafates/mangal/filesystem" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/source" "github.com/samber/lo" . "github.com/smartystreets/goconvey/convey" @@ -18,7 +19,7 @@ import ( func init() { filesystem.SetMemMapFs() lo.Must0(config.Setup()) - viper.Set(constant.FormatsUse, constant.FormatCBZ) + viper.Set(key.FormatsUse, constant.FormatCBZ) } func TestCBZ(t *testing.T) { diff --git a/converter/pdf/pdf.go b/converter/pdf/pdf.go index 0123a9ab..18d98028 100644 --- a/converter/pdf/pdf.go +++ b/converter/pdf/pdf.go @@ -1,8 +1,8 @@ package pdf import ( - "github.com/metafates/mangal/constant" "github.com/metafates/mangal/filesystem" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/source" "github.com/metafates/mangal/util" "github.com/pdfcpu/pdfcpu/pkg/api" @@ -74,7 +74,7 @@ func pagesToPDF(w io.Writer, pages []*source.Page) error { indRef, err := pdfcpu.NewPageForImage(ctx.XRefTable, r, pagesIndRef, imp) if err != nil { - if viper.GetBool(constant.FormatsSkipUnsupportedImages) { + if viper.GetBool(key.FormatsSkipUnsupportedImages) { continue } diff --git a/converter/pdf/pdf_test.go b/converter/pdf/pdf_test.go index 35410579..87fd8ba2 100644 --- a/converter/pdf/pdf_test.go +++ b/converter/pdf/pdf_test.go @@ -5,6 +5,7 @@ import ( "github.com/metafates/mangal/config" "github.com/metafates/mangal/constant" "github.com/metafates/mangal/filesystem" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/source" "github.com/samber/lo" . "github.com/smartystreets/goconvey/convey" @@ -17,7 +18,7 @@ import ( func init() { filesystem.SetMemMapFs() lo.Must0(config.Setup()) - viper.Set(constant.FormatsUse, constant.FormatPDF) + viper.Set(key.FormatsUse, constant.FormatPDF) } func TestPDF(t *testing.T) { diff --git a/converter/zip/zip_test.go b/converter/zip/zip_test.go index b3b38b29..22e78959 100644 --- a/converter/zip/zip_test.go +++ b/converter/zip/zip_test.go @@ -6,6 +6,7 @@ import ( "github.com/metafates/mangal/config" "github.com/metafates/mangal/constant" "github.com/metafates/mangal/filesystem" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/source" "github.com/samber/lo" . "github.com/smartystreets/goconvey/convey" @@ -18,7 +19,7 @@ import ( func init() { filesystem.SetMemMapFs() lo.Must0(config.Setup()) - viper.Set(constant.FormatsUse, constant.FormatZIP) + viper.Set(key.FormatsUse, constant.FormatZIP) } func TestCBZ(t *testing.T) { diff --git a/downloader/download.go b/downloader/download.go index 84fcd41a..0d597b05 100644 --- a/downloader/download.go +++ b/downloader/download.go @@ -4,10 +4,10 @@ import ( "encoding/json" "fmt" "github.com/metafates/mangal/color" - "github.com/metafates/mangal/constant" "github.com/metafates/mangal/converter" "github.com/metafates/mangal/filesystem" "github.com/metafates/mangal/history" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/log" "github.com/metafates/mangal/source" "github.com/metafates/mangal/style" @@ -25,7 +25,7 @@ func Download(chapter *source.Chapter, progress func(string)) (string, error) { return "", err } - if viper.GetBool(constant.DownloaderRedownloadExisting) { + if viper.GetBool(key.DownloaderRedownloadExisting) { log.Info("chapter already downloaded, deleting and redownloading") err = filesystem.Api().Remove(path) if err != nil { @@ -53,14 +53,14 @@ func Download(chapter *source.Chapter, progress func(string)) (string, error) { return "", err } - if viper.GetBool(constant.MetadataFetchAnilist) { + if viper.GetBool(key.MetadataFetchAnilist) { err := chapter.Manga.PopulateMetadata(progress) if err != nil { log.Warn(err) } } - if viper.GetBool(constant.MetadataSeriesJSON) { + if viper.GetBool(key.MetadataSeriesJSON) { path, err := chapter.Manga.Path(false) if err != nil { log.Warn(err) @@ -80,34 +80,34 @@ func Download(chapter *source.Chapter, progress func(string)) (string, error) { } } - if viper.GetBool(constant.DownloaderDownloadCover) { + if viper.GetBool(key.DownloaderDownloadCover) { coverDir, err := chapter.Manga.Path(false) if err == nil { _ = chapter.Manga.DownloadCover(false, coverDir, progress) } } - log.Info("getting " + viper.GetString(constant.FormatsUse) + " converter") + log.Info("getting " + viper.GetString(key.FormatsUse) + " converter") progress(fmt.Sprintf( "Converting %d pages to %s %s", len(pages), - style.Fg(color.Yellow)(viper.GetString(constant.FormatsUse)), + style.Fg(color.Yellow)(viper.GetString(key.FormatsUse)), style.Faint(chapter.SizeHuman())), ) - conv, err := converter.Get(viper.GetString(constant.FormatsUse)) + conv, err := converter.Get(viper.GetString(key.FormatsUse)) if err != nil { log.Error(err) return "", err } - log.Info("converting " + viper.GetString(constant.FormatsUse)) + log.Info("converting " + viper.GetString(key.FormatsUse)) path, err = conv.Save(chapter) if err != nil { log.Error(err) return "", err } - if viper.GetBool(constant.HistorySaveOnDownload) { + if viper.GetBool(key.HistorySaveOnDownload) { go func() { err = history.Save(chapter) if err != nil { diff --git a/downloader/read.go b/downloader/read.go index 8f954bb7..3a8223f6 100644 --- a/downloader/read.go +++ b/downloader/read.go @@ -6,6 +6,7 @@ import ( "github.com/metafates/mangal/constant" "github.com/metafates/mangal/converter" "github.com/metafates/mangal/history" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/log" "github.com/metafates/mangal/open" "github.com/metafates/mangal/source" @@ -16,14 +17,14 @@ import ( // Read the chapter by downloading it with the given source // and opening it with the configured reader. func Read(chapter *source.Chapter, progress func(string)) error { - if viper.GetBool(constant.ReaderReadInBrowser) { + if viper.GetBool(key.ReaderReadInBrowser) { return open.StartWith( chapter.URL, - viper.GetString(constant.ReaderBrowser), + viper.GetString(key.ReaderBrowser), ) } - if viper.GetBool(constant.DownloaderReadDownloaded) && chapter.IsDownloaded() { + if viper.GetBool(key.DownloaderReadDownloaded) && chapter.IsDownloaded() { path, err := chapter.Path(false) if err == nil { return openRead(path, chapter, progress) @@ -45,18 +46,18 @@ func Read(chapter *source.Chapter, progress func(string)) error { return err } - log.Info("getting " + viper.GetString(constant.FormatsUse) + " converter") - conv, err := converter.Get(viper.GetString(constant.FormatsUse)) + log.Info("getting " + viper.GetString(key.FormatsUse) + " converter") + conv, err := converter.Get(viper.GetString(key.FormatsUse)) if err != nil { log.Error(err) return err } - log.Info("converting " + viper.GetString(constant.FormatsUse)) + log.Info("converting " + viper.GetString(key.FormatsUse)) progress(fmt.Sprintf( "Converting %d pages to %s %s", len(pages), - style.Fg(color.Yellow)(viper.GetString(constant.FormatsUse)), + style.Fg(color.Yellow)(viper.GetString(key.FormatsUse)), style.Faint(chapter.SizeHuman())), ) path, err := conv.SaveTemp(chapter) @@ -76,7 +77,7 @@ func Read(chapter *source.Chapter, progress func(string)) error { } func openRead(path string, chapter *source.Chapter, progress func(string)) error { - if viper.GetBool(constant.HistorySaveOnRead) { + if viper.GetBool(key.HistorySaveOnRead) { go func() { err := history.Save(chapter) if err != nil { @@ -92,15 +93,15 @@ func openRead(path string, chapter *source.Chapter, progress func(string)) error err error ) - switch viper.GetString(constant.FormatsUse) { + switch viper.GetString(key.FormatsUse) { case constant.FormatPDF: - reader = viper.GetString(constant.ReaderPDF) + reader = viper.GetString(key.ReaderPDF) case constant.FormatCBZ: - reader = viper.GetString(constant.ReaderCBZ) + reader = viper.GetString(key.ReaderCBZ) case constant.FormatZIP: - reader = viper.GetString(constant.ReaderZIP) + reader = viper.GetString(key.ReaderZIP) case constant.FormatPlain: - reader = viper.GetString(constant.RaderPlain) + reader = viper.GetString(key.RaderPlain) } if reader != "" { diff --git a/go.mod b/go.mod index 31d73120..64172521 100644 --- a/go.mod +++ b/go.mod @@ -6,12 +6,12 @@ require ( github.com/AlecAivazis/survey/v2 v2.3.6 github.com/PuerkitoBio/goquery v1.8.0 github.com/charmbracelet/bubbles v0.14.0 - github.com/charmbracelet/bubbletea v0.22.1 + github.com/charmbracelet/bubbletea v0.23.1 github.com/charmbracelet/lipgloss v0.6.0 github.com/darylhjd/mangodex v0.0.0-20211231093527-e4a91c518fa0 github.com/dustin/go-humanize v1.0.0 github.com/gocolly/colly/v2 v2.1.0 - github.com/invopop/jsonschema v0.6.0 + github.com/invopop/jsonschema v0.7.0 github.com/ivanpirog/coloredcobra v1.0.1 github.com/ka-weihe/fast-levenshtein v0.0.0-20201227151214-4c99ee36a1ba github.com/lithammer/fuzzysearch v1.1.5 @@ -19,23 +19,23 @@ require ( github.com/metafates/mangal-lua-libs v0.4.2 github.com/muesli/reflow v0.3.0 github.com/pdfcpu/pdfcpu v0.3.13 - github.com/samber/lo v1.33.0 - github.com/samber/mo v1.5.1 + github.com/samber/lo v1.37.0 + github.com/samber/mo v1.7.0 github.com/sirupsen/logrus v1.9.0 github.com/smartystreets/goconvey v1.7.2 - github.com/spf13/afero v1.9.2 + github.com/spf13/afero v1.9.3 github.com/spf13/cobra v1.6.1 - github.com/spf13/viper v1.13.0 - github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 - golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 - golang.org/x/term v0.1.0 + github.com/spf13/viper v1.14.0 + github.com/yuin/gopher-lua v0.0.0-20221210110428-332342483e3f + golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15 + golang.org/x/term v0.3.0 ) require ( github.com/alessio/shellescape v1.4.1 // indirect github.com/andybalholm/cascadia v1.3.1 // indirect github.com/antchfx/htmlquery v1.2.5 // indirect - github.com/antchfx/xmlquery v1.3.12 // indirect + github.com/antchfx/xmlquery v1.3.13 // indirect github.com/antchfx/xpath v1.2.1 // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52 v1.2.1 // indirect @@ -44,7 +44,7 @@ require ( github.com/containerd/console v1.0.3 // indirect github.com/fatih/color v1.13.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-rod/rod v0.112.0 // indirect + github.com/go-rod/rod v0.112.2 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect @@ -53,12 +53,12 @@ require ( github.com/hhrutter/lzw v0.0.0-20190829144645-6f07a24e8650 // indirect github.com/hhrutter/tiff v0.0.0-20190829141212-736cae8d0bc7 // indirect github.com/iancoleman/orderedmap v0.2.0 // indirect - github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kennygrant/sanitize v1.2.4 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/magiconair/properties v1.8.6 // 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.16 // indirect github.com/mattn/go-localereader v0.0.1 // indirect @@ -66,13 +66,13 @@ require ( github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/montanaflynn/stats v0.6.6 // indirect - github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect + github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.13.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.5 // indirect + github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/rivo/uniseg v0.4.2 // indirect + github.com/rivo/uniseg v0.4.3 // indirect github.com/sahilm/fuzzy v0.1.0 // indirect github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect github.com/smartystreets/assertions v1.2.0 // indirect @@ -82,13 +82,13 @@ require ( github.com/subosito/gotenv v1.4.1 // indirect github.com/temoto/robotstxt v1.1.2 // indirect github.com/ysmood/goob v0.4.0 // indirect - github.com/ysmood/gson v0.7.2 // indirect + github.com/ysmood/gson v0.7.3 // indirect github.com/ysmood/leakless v0.8.0 // indirect github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7 // indirect - golang.org/x/image v0.1.0 // indirect - golang.org/x/net v0.1.0 // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/text v0.4.0 // indirect + golang.org/x/image v0.2.0 // indirect + golang.org/x/net v0.4.0 // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/text v0.5.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect diff --git a/go.sum b/go.sum index ad098733..3f0ff953 100644 --- a/go.sum +++ b/go.sum @@ -59,6 +59,8 @@ github.com/antchfx/htmlquery v1.2.5/go.mod h1:2MCVBzYVafPBmKbrmwB9F5xdd+IEgRY61c github.com/antchfx/xmlquery v1.2.4/go.mod h1:KQQuESaxSlqugE2ZBcM/qn+ebIpt+d+4Xx7YcSGAIrM= github.com/antchfx/xmlquery v1.3.12 h1:6TMGpdjpO/P8VhjnaYPXuqT3qyJ/VsqoyNTmJzNBTQ4= github.com/antchfx/xmlquery v1.3.12/go.mod h1:3w2RvQvTz+DaT5fSgsELkSJcdNgkmg6vuXDEuhdwsPQ= +github.com/antchfx/xmlquery v1.3.13 h1:wqhTv2BN5MzYg9rnPVtZb3IWP8kW6WV/ebAY0FCTI7Y= +github.com/antchfx/xmlquery v1.3.13/go.mod h1:3w2RvQvTz+DaT5fSgsELkSJcdNgkmg6vuXDEuhdwsPQ= github.com/antchfx/xpath v1.1.6/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk= github.com/antchfx/xpath v1.1.8/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk= github.com/antchfx/xpath v1.2.1 h1:qhp4EW6aCOVr5XIkT+l6LJ9ck/JsUH/yyauNgTQkBF8= @@ -78,6 +80,8 @@ github.com/charmbracelet/bubbles v0.14.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWo github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4= github.com/charmbracelet/bubbletea v0.22.1 h1:z66q0LWdJNOWEH9zadiAIXp2GN1AWrwNXU8obVY9X24= github.com/charmbracelet/bubbletea v0.22.1/go.mod h1:8/7hVvbPN6ZZPkczLiB8YpLkLJ0n7DMho5Wvfd2X1C0= +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 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= @@ -122,6 +126,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-rod/rod v0.112.0 h1:U9Yc+quw4hxZ6GrdbWFBeylvaYElEKM9ijFW2LYkGlA= github.com/go-rod/rod v0.112.0/go.mod h1:GZDtmEs6RpF6kBRYpGCZXxXlKNneKVPiKOjaMbmVVjE= +github.com/go-rod/rod v0.112.2 h1:dwauKYC/H2em8/BcGk3gC0LTzZHf5MIDKf2DVM4z9gU= +github.com/go-rod/rod v0.112.2/go.mod h1:ElViL9ABbcshNQw93+11FrYRH92RRhMKleuILo6+5V0= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gocolly/colly v1.2.0 h1:qRz9YAn8FIH0qzgNUw+HT9UN7wm1oF9OBAilwEWpyrI= @@ -210,8 +216,12 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1: github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/invopop/jsonschema v0.6.0 h1:8e+xY8ZEn8gDHUYylSlLHy22P+SLeIRIHv3nM3hCbmY= github.com/invopop/jsonschema v0.6.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0= +github.com/invopop/jsonschema v0.7.0 h1:2vgQcBz1n256N+FpX3Jq7Y17AjYt46Ig3zIWyy770So= +github.com/invopop/jsonschema v0.7.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0= github.com/ivanpirog/coloredcobra v1.0.1 h1:aURSdEmlR90/tSiWS0dMjdwOvCVUeYLfltLfbgNxrN4= github.com/ivanpirog/coloredcobra v1.0.1/go.mod h1:iho4nEKcnwZFiniGSdcgdvRgZNjxm+h20acv8vqmN6Q= github.com/jawher/mow.cli v1.1.0/go.mod h1:aNaQlc7ozF3vw6IJ2dHjp2ZFiA4ozMIYY6PyuRJwlUg= @@ -241,6 +251,8 @@ 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/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +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.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -273,6 +285,8 @@ github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 h1:kMlmsLSbjkikxQJ1IPwaM+7LJ9ltFu/fi8CRzvSnQmA= github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= +github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a h1:jlDOeO5TU0pYlbc/y6PFguab5IjANI0Knrpg3u/ton4= +github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= 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= @@ -289,6 +303,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= +github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= @@ -299,6 +315,8 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= +github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -308,8 +326,16 @@ github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxT github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= github.com/samber/lo v1.33.0 h1:2aKucr+rQV6gHpY3bpeZu69uYoQOzVhGT3J22Op6Cjk= github.com/samber/lo v1.33.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8= +github.com/samber/lo v1.36.0 h1:4LaOxH1mHnbDGhTVE0i1z8v/lWaQW8AIfOD3HU4mSaw= +github.com/samber/lo v1.36.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8= +github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw= +github.com/samber/lo v1.37.0/go.mod h1:9vaz2O4o8oOnK23pd2TrXufcbdbJIa3b6cstBWKpopA= github.com/samber/mo v1.5.1 h1:5dRSevAB33Q/OrYwTmtksHHxquuf2urnRSUTsdTFysY= github.com/samber/mo v1.5.1/go.mod h1:pDuQgWscOVGGoEz+NAeth/Xq+MPAcXxCeph1XIAm/DU= +github.com/samber/mo v1.5.2 h1:MINtAxMiorTmlWWPmd8LBms0OoHZJsEEBY5yI/DXFa8= +github.com/samber/mo v1.5.2/go.mod h1:pDuQgWscOVGGoEz+NAeth/Xq+MPAcXxCeph1XIAm/DU= +github.com/samber/mo v1.7.0 h1:wYI97e2+CHUvhkRGK1dl5FWpv/XDieaEYIAEJ9XVu2o= +github.com/samber/mo v1.7.0/go.mod h1:gELW3aXN9Utq0gz969NbLMeZo6dkUW8QTohmafdFEEA= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= @@ -318,6 +344,8 @@ github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hg github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= +github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= @@ -329,9 +357,12 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU= github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= +github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU= +github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -342,6 +373,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/temoto/robotstxt v1.1.1/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo= @@ -352,11 +384,15 @@ github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ= github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18= github.com/ysmood/got v0.31.3 h1:UvvF+TDVsZLO7MSzm/Bd/H4HVp+7S5YwsxgdwaKq8uA= github.com/ysmood/got v0.31.3/go.mod h1:pE1l4LOwOBhQg6A/8IAatkGp7uZjnalzrZolnlhhMgY= +github.com/ysmood/got v0.32.0 h1:aAHdQgfgMb/lo4v+OekM+SSqEJYFI035h5YYvLXsVyU= +github.com/ysmood/got v0.32.0/go.mod h1:pE1l4LOwOBhQg6A/8IAatkGp7uZjnalzrZolnlhhMgY= github.com/ysmood/gotrace v0.6.0 h1:SyI1d4jclswLhg7SWTL6os3L1WOKeNn/ZtzVQF8QmdY= github.com/ysmood/gotrace v0.6.0/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM= github.com/ysmood/gson v0.7.1/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= github.com/ysmood/gson v0.7.2 h1:1iWUvpi5DPvd2j59W7ifRPR9DiAZ3Ga+fmMl1mJrRbM= github.com/ysmood/gson v0.7.2/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= +github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE= +github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= github.com/ysmood/leakless v0.8.0 h1:BzLrVoiwxikpgEQR0Lk8NyBN5Cit2b1z+u0mgL4ZJak= github.com/ysmood/leakless v0.8.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7 h1:noHsffKZsNfU38DwcXWEPldrTjIZ8FPNKx8mYMGnqjs= @@ -368,6 +404,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 h1:5mLPGnFdSsevFRFc9q3yYbBkB6tsm4aCwwQV/j1JQAQ= github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= +github.com/yuin/gopher-lua v0.0.0-20221210110428-332342483e3f h1:wihIB0V/mGpVYrL8I7n/WxVqWnP07CBXZ5uCgxUP1tI= +github.com/yuin/gopher-lua v0.0.0-20221210110428-332342483e3f/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -396,11 +434,19 @@ golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f h1:Al51T6tzvuh3oiwX11vex3QgJ golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 h1:QfTh0HpN6hlw6D3vu8DAwC8pBIwikq0AI1evdm+FksE= golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9 h1:yZNXmy+j/JpX19vZkVktWqAo7Gny4PBWYYK3zskGpx4= +golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20221208044002-44028be4359e h1:lTjJJUAuWTLRn0pXoNLiVZIFYOIpvmg3MxmZxgO09bM= +golang.org/x/exp v0.0.0-20221208044002-44028be4359e/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15 h1:5oN1Pz/eDhCpbMbLstvIPa0b/BEQo6g6nwV3pLjfM6w= +golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190823064033-3a9bac650e44/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.1.0 h1:r8Oj8ZA2Xy12/b5KZYj3tuv7NG/fBz3TwQVvpJ9l8Rk= golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c= +golang.org/x/image v0.2.0 h1:/DcQ0w3VHKCC5p0/P2B0JpAZ9Z++V2KOo2fyU89CXBQ= +golang.org/x/image v0.2.0/go.mod h1:la7oBXb9w3YFjBqaAwtynVioc1ZvOnNteUNrifGNmAI= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -462,6 +508,10 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -532,11 +582,19 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -547,6 +605,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/history/history.go b/history/history.go index 4c0d0c66..235f3397 100644 --- a/history/history.go +++ b/history/history.go @@ -2,9 +2,9 @@ package history import ( "github.com/metafates/gache" - "github.com/metafates/mangal/constant" "github.com/metafates/mangal/filesystem" "github.com/metafates/mangal/integration" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/log" "github.com/metafates/mangal/source" "github.com/metafates/mangal/where" @@ -35,7 +35,7 @@ func Get() (chapters map[string]*SavedChapter, err error) { // Save saves the chapter to the history file func Save(chapter *source.Chapter) error { - if viper.GetBool(constant.AnilistEnable) { + if viper.GetBool(key.AnilistEnable) { go func() { log.Info("Saving chapter to anilist") err := integration.Anilist.MarkRead(chapter) diff --git a/icon/icon.go b/icon/icon.go index 89b181d4..08f6b736 100644 --- a/icon/icon.go +++ b/icon/icon.go @@ -1,7 +1,7 @@ package icon import ( - "github.com/metafates/mangal/constant" + "github.com/metafates/mangal/key" "github.com/spf13/viper" ) @@ -26,7 +26,7 @@ type iconDef struct { } func (i *iconDef) Get() string { - switch viper.GetString(constant.IconsVariant) { + switch viper.GetString(key.IconsVariant) { case emoji: return i.emoji case nerd: diff --git a/icon/icon_test.go b/icon/icon_test.go index 3ef3fb69..78d54dd4 100644 --- a/icon/icon_test.go +++ b/icon/icon_test.go @@ -1,7 +1,7 @@ package icon import ( - "github.com/metafates/mangal/constant" + "github.com/metafates/mangal/key" . "github.com/smartystreets/goconvey/convey" "github.com/spf13/viper" "testing" @@ -11,7 +11,7 @@ func TestGet(t *testing.T) { Convey("Given a icon", t, func() { i := Lua Convey("When getting the icon with emoji setting", func() { - viper.Set(constant.IconsVariant, emoji) + viper.Set(key.IconsVariant, emoji) result := Get(i) Convey("Then the result should be emoji icon", func() { So(result, ShouldEqual, icons[i].emoji) @@ -19,7 +19,7 @@ func TestGet(t *testing.T) { }) Convey("When getting the icon with nerd setting", func() { - viper.Set(constant.IconsVariant, nerd) + viper.Set(key.IconsVariant, nerd) result := Get(i) Convey("Then the result should be nerd icon", func() { So(result, ShouldEqual, icons[i].nerd) @@ -27,7 +27,7 @@ func TestGet(t *testing.T) { }) Convey("When getting the icon with plain setting", func() { - viper.Set(constant.IconsVariant, plain) + viper.Set(key.IconsVariant, plain) result := Get(i) Convey("Then the result should be plain icon", func() { So(result, ShouldEqual, icons[i].plain) @@ -35,7 +35,7 @@ func TestGet(t *testing.T) { }) Convey("When getting the icon with kaomoji setting", func() { - viper.Set(constant.IconsVariant, kaomoji) + viper.Set(key.IconsVariant, kaomoji) result := Get(i) Convey("Then the result should be kaomoji icon", func() { So(result, ShouldEqual, icons[i].kaomoji) @@ -43,7 +43,7 @@ func TestGet(t *testing.T) { }) Convey("When getting the icon with no setting", func() { - viper.Set(constant.IconsVariant, "") + viper.Set(key.IconsVariant, "") result := Get(i) Convey("Then the result should be empty icon", func() { So(result, ShouldBeEmpty) diff --git a/inline/inline.go b/inline/inline.go index 44d2ff22..26ad0c6a 100644 --- a/inline/inline.go +++ b/inline/inline.go @@ -1,8 +1,8 @@ package inline import ( - "github.com/metafates/mangal/constant" "github.com/metafates/mangal/downloader" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/log" "github.com/metafates/mangal/source" "github.com/spf13/viper" @@ -25,7 +25,7 @@ func Run(options *Options) (err error) { } if options.MangaPicker.IsAbsent() && options.ChaptersFilter.IsAbsent() { - if viper.GetBool(constant.MetadataFetchAnilist) { + if viper.GetBool(key.MetadataFetchAnilist) { for _, manga := range mangas { _ = manga.PopulateMetadata(func(string) {}) } @@ -119,8 +119,12 @@ func Run(options *Options) (err error) { for _, chapter := range chapters { if options.Download { path, err := downloader.Download(chapter, func(string) {}) - if err != nil && viper.GetBool(constant.DownloaderStopOnError) { - return err + if err != nil { + if viper.GetBool(key.DownloaderStopOnError) { + return err + } + + continue } _, err = options.Out.Write([]byte(path + "\n")) diff --git a/inline/json.go b/inline/json.go index 42c9b6ff..96a41e38 100644 --- a/inline/json.go +++ b/inline/json.go @@ -3,7 +3,7 @@ package inline import ( "encoding/json" "github.com/metafates/mangal/anilist" - "github.com/metafates/mangal/constant" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/source" "github.com/spf13/viper" ) @@ -79,7 +79,7 @@ func prepareManga(manga *source.Manga, options *Options) error { manga.Chapters = make([]*source.Chapter, 0) } - if viper.GetBool(constant.MetadataFetchAnilist) { + if viper.GetBool(key.MetadataFetchAnilist) { _ = manga.PopulateMetadata(func(string) {}) } diff --git a/installer/collector.go b/installer/collector.go index d2a3c96d..e0becec1 100644 --- a/installer/collector.go +++ b/installer/collector.go @@ -1,7 +1,7 @@ package installer import ( - "github.com/metafates/mangal/constant" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/util" "github.com/samber/lo" "github.com/spf13/viper" @@ -36,8 +36,8 @@ func Scrapers() ([]*Scraper, error) { func setupCollector() { collector = &githubFilesCollector{ - user: viper.GetString(constant.InstallerUser), - repo: viper.GetString(constant.InstallerRepo), - branch: viper.GetString(constant.InstallerBranch), + user: viper.GetString(key.InstallerUser), + repo: viper.GetString(key.InstallerRepo), + branch: viper.GetString(key.InstallerBranch), } } diff --git a/integration/anilist/anilist.go b/integration/anilist/anilist.go index 078c1933..2238a782 100644 --- a/integration/anilist/anilist.go +++ b/integration/anilist/anilist.go @@ -1,7 +1,7 @@ package anilist import ( - "github.com/metafates/mangal/constant" + "github.com/metafates/mangal/key" "github.com/spf13/viper" ) @@ -15,15 +15,15 @@ func New() *Anilist { } func (a *Anilist) id() string { - return viper.GetString(constant.AnilistID) + return viper.GetString(key.AnilistID) } func (a *Anilist) secret() string { - return viper.GetString(constant.AnilistSecret) + return viper.GetString(key.AnilistSecret) } func (a *Anilist) code() string { - return viper.GetString(constant.AnilistCode) + return viper.GetString(key.AnilistCode) } // AuthURL returns the URL to authenticate with Anilist diff --git a/constant/config.go b/key/keys.go similarity index 97% rename from constant/config.go rename to key/keys.go index 6add2886..2b139c16 100644 --- a/constant/config.go +++ b/key/keys.go @@ -1,9 +1,9 @@ -package constant +package key // DefinedFieldsCount is the number of fields defined in this package. // You have to manually update this number when you add a new field // to check later if every field has a defined default value -const DefinedFieldsCount = 51 +const DefinedFieldsCount = 52 const ( DownloaderPath = "downloader.path" @@ -97,3 +97,7 @@ const ( LogsLevel = "logs.level" LogsJson = "logs.json" ) + +const ( + CliColored = "cli.colored" +) diff --git a/log/log.go b/log/log.go index 61612cb5..54b63683 100644 --- a/log/log.go +++ b/log/log.go @@ -3,8 +3,8 @@ package log import ( "errors" "fmt" - "github.com/metafates/mangal/constant" "github.com/metafates/mangal/filesystem" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/where" "github.com/samber/lo" log "github.com/sirupsen/logrus" @@ -17,7 +17,7 @@ import ( var writeLogs bool func Setup() error { - writeLogs = viper.GetBool(constant.LogsWrite) + writeLogs = viper.GetBool(key.LogsWrite) if !writeLogs { return nil @@ -41,13 +41,13 @@ func Setup() error { log.SetOutput(logFile) - if viper.GetBool(constant.LogsJson) { + if viper.GetBool(key.LogsJson) { log.SetFormatter(&log.JSONFormatter{PrettyPrint: true}) } else { log.SetFormatter(&log.TextFormatter{}) } - switch viper.GetString(constant.LogsLevel) { + switch viper.GetString(key.LogsLevel) { case "panic": log.SetLevel(log.PanicLevel) case "fatal": diff --git a/mini/states.go b/mini/states.go index a39db722..7028f5a8 100644 --- a/mini/states.go +++ b/mini/states.go @@ -2,9 +2,9 @@ package mini import ( "fmt" - "github.com/metafates/mangal/constant" "github.com/metafates/mangal/downloader" "github.com/metafates/mangal/history" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/provider" "github.com/metafates/mangal/source" "github.com/metafates/mangal/util" @@ -32,7 +32,7 @@ const ( func (m *mini) handleSourceSelectState() error { var err error - if name := viper.GetString(constant.DownloaderDefaultSources); name != "" { + if name := viper.GetString(key.DownloaderDefaultSources); name != "" { p, ok := provider.Get(name) if !ok { return fmt.Errorf("unknown source \"%s\"", name) @@ -91,7 +91,7 @@ func (m *mini) handleMangaSearchState() error { erase := progress("Searching Query..") m.cachedMangas[query], err = m.selectedSource.Search(query) - max := lo.Min([]int{len(m.cachedMangas[query]), viper.GetInt(constant.MiniSearchLimit)}) + max := lo.Min([]int{len(m.cachedMangas[query]), viper.GetInt(key.MiniSearchLimit)}) m.cachedMangas[query] = m.cachedMangas[query][:max] erase() @@ -324,7 +324,7 @@ func (m *mini) handleChaptersDownloadState() error { erase() - if err != nil && viper.GetBool(constant.DownloaderStopOnError) { + if err != nil && viper.GetBool(key.DownloaderStopOnError) { return err } diff --git a/provider/mangadex/chapters.go b/provider/mangadex/chapters.go index 35985398..5255ea36 100644 --- a/provider/mangadex/chapters.go +++ b/provider/mangadex/chapters.go @@ -3,7 +3,7 @@ package mangadex import ( "fmt" "github.com/darylhjd/mangodex" - "github.com/metafates/mangal/constant" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/source" "github.com/spf13/viper" "golang.org/x/exp/slices" @@ -27,7 +27,7 @@ func (m *Mangadex) ChaptersOf(manga *source.Manga) ([]*source.Chapter, error) { params.Add("contentRating[]", rating) } - if viper.GetBool(constant.MangadexNSFW) { + if viper.GetBool(key.MangadexNSFW) { params.Add("contentRating[]", mangodex.Porn) params.Add("contentRating[]", mangodex.Erotica) } @@ -41,7 +41,7 @@ func (m *Mangadex) ChaptersOf(manga *source.Manga) ([]*source.Chapter, error) { currOffset = 0 ) - language := viper.GetString(constant.MangadexLanguage) + language := viper.GetString(key.MangadexLanguage) for { params.Set("offset", strconv.Itoa(currOffset)) @@ -52,7 +52,7 @@ func (m *Mangadex) ChaptersOf(manga *source.Manga) ([]*source.Chapter, error) { for i, chapter := range list.Data { // Skip external chapters. Their pages cannot be downloaded. - if chapter.Attributes.ExternalURL != nil && !viper.GetBool(constant.MangadexShowUnavailableChapters) { + if chapter.Attributes.ExternalURL != nil && !viper.GetBool(key.MangadexShowUnavailableChapters) { continue } diff --git a/provider/mangadex/search.go b/provider/mangadex/search.go index b3c839c0..a589d72e 100644 --- a/provider/mangadex/search.go +++ b/provider/mangadex/search.go @@ -3,7 +3,7 @@ package mangadex import ( "fmt" "github.com/darylhjd/mangodex" - "github.com/metafates/mangal/constant" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/source" "github.com/spf13/viper" "log" @@ -29,7 +29,7 @@ func (m *Mangadex) Search(query string) ([]*source.Manga, error) { params.Add("contentRating[]", rating) } - if viper.GetBool(constant.MangadexNSFW) { + if viper.GetBool(key.MangadexNSFW) { params.Add("contentRating[]", mangodex.Porn) params.Add("contentRating[]", mangodex.Erotica) } @@ -47,7 +47,7 @@ func (m *Mangadex) Search(query string) ([]*source.Manga, error) { for i, manga := range mangaList.Data { m := source.Manga{ - Name: manga.GetTitle(viper.GetString(constant.MangadexLanguage)), + Name: manga.GetTitle(viper.GetString(key.MangadexLanguage)), URL: fmt.Sprintf("https://mangadex.org/title/%s", manga.ID), Index: uint16(i), ID: manga.ID, diff --git a/provider/provider.go b/provider/provider.go index 505a5e2c..42cdb6f6 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -51,6 +51,7 @@ func Customs() []*Provider { []byte("require(\"headless\")"), []byte("require('headless')"), []byte("require(headless)"), + []byte("require'headless'"), }) name := util.FileStem(path) diff --git a/query/suggest.go b/query/suggest.go index 0459186d..16bc302f 100644 --- a/query/suggest.go +++ b/query/suggest.go @@ -2,7 +2,7 @@ package query import ( "github.com/lithammer/fuzzysearch/fuzzy" - "github.com/metafates/mangal/constant" + "github.com/metafates/mangal/key" "github.com/samber/lo" "github.com/samber/mo" "github.com/spf13/viper" @@ -14,7 +14,7 @@ var ( ) func SuggestMany(query string) []string { - if !viper.GetBool(constant.SearchShowQuerySuggestions) { + if !viper.GetBool(key.SearchShowQuerySuggestions) { return []string{} } diff --git a/source/chapter.go b/source/chapter.go index 2dc3a816..2db74035 100644 --- a/source/chapter.go +++ b/source/chapter.go @@ -5,6 +5,7 @@ import ( "github.com/dustin/go-humanize" "github.com/metafates/mangal/constant" "github.com/metafates/mangal/filesystem" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/style" "github.com/metafates/mangal/util" "github.com/samber/mo" @@ -58,6 +59,10 @@ func (c *Chapter) DownloadPages(temp bool, progress func(string)) (err error) { wg.Add(len(c.Pages)) for _, page := range c.Pages { + if page == nil { + return fmt.Errorf("page #%d is empty, aborting download", page.Index) + } + d := func(page *Page) { defer wg.Done() @@ -71,7 +76,7 @@ func (c *Chapter) DownloadPages(temp bool, progress func(string)) (err error) { progress(status()) } - if viper.GetBool(constant.DownloaderAsync) { + if viper.GetBool(key.DownloaderAsync) { go d(page) } else { d(page) @@ -79,13 +84,19 @@ func (c *Chapter) DownloadPages(temp bool, progress func(string)) (err error) { } wg.Wait() - c.isDownloaded = mo.Some(!temp && err == nil) + + if err != nil { + c.isDownloaded = mo.Some(false) + return err + } + + c.isDownloaded = mo.Some(!temp) return } // formattedName of the chapter according to the template in the config. func (c *Chapter) formattedName() (name string) { - name = viper.GetString(constant.DownloaderChapterNameTemplate) + name = viper.GetString(key.DownloaderChapterNameTemplate) var sourceName string if c.Source() != nil { @@ -117,7 +128,7 @@ func (c *Chapter) Filename() (filename string) { // plain format assumes that chapter is a directory with images // rather than a single file. So no need to add extension to it - if f := viper.GetString(constant.FormatsUse); f != constant.FormatPlain { + if f := viper.GetString(key.FormatsUse); f != constant.FormatPlain { return filename + "." + f } @@ -155,7 +166,7 @@ func (c *Chapter) Path(temp bool) (path string, err error) { return } - return c.path(manga, c.Volume != "" && viper.GetBool(constant.DownloaderCreateVolumeDir)) + return c.path(manga, c.Volume != "" && viper.GetBool(key.DownloaderCreateVolumeDir)) } func (c *Chapter) Source() Source { @@ -167,8 +178,8 @@ func (c *Chapter) ComicInfo() *ComicInfo { day, month, year int ) - if viper.GetBool(constant.MetadataComicInfoXMLAddDate) { - if viper.GetBool(constant.MetadataComicInfoXMLAlternativeDate) { + if viper.GetBool(key.MetadataComicInfoXMLAddDate) { + if viper.GetBool(key.MetadataComicInfoXMLAlternativeDate) { // get current date t := time.Now() day = t.Day() diff --git a/source/chapter_test.go b/source/chapter_test.go index 92dc539b..ae85bd3c 100644 --- a/source/chapter_test.go +++ b/source/chapter_test.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/metafates/mangal/constant" "github.com/metafates/mangal/filesystem" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/util" . "github.com/smartystreets/goconvey/convey" "github.com/spf13/viper" @@ -12,7 +13,7 @@ import ( func init() { filesystem.SetMemMapFs() - viper.Set(constant.FormatsUse, constant.FormatPDF) + viper.Set(key.FormatsUse, constant.FormatPDF) } var testChapter = Chapter{ @@ -30,7 +31,7 @@ func TestChapter_Filename(t *testing.T) { Convey("When Filename is called", func() { Convey("It should return a sanitized filename", func() { const template = "&{index}! {chapter}// {volume} 28922@ {manga}" - viper.Set(constant.DownloaderChapterNameTemplate, template) + viper.Set(key.DownloaderChapterNameTemplate, template) filename := testChapter.Filename() Convey("It should match the given template", func() { diff --git a/source/manga.go b/source/manga.go index d3e4a501..9ad0ccb3 100644 --- a/source/manga.go +++ b/source/manga.go @@ -3,8 +3,8 @@ package source import ( "fmt" "github.com/metafates/mangal/anilist" - "github.com/metafates/mangal/constant" "github.com/metafates/mangal/filesystem" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/log" "github.com/metafates/mangal/util" "github.com/metafates/mangal/where" @@ -103,7 +103,7 @@ func (m *Manga) Dirname() string { func (m *Manga) peekPath() string { path := where.Downloads() - if viper.GetBool(constant.DownloaderCreateMangaDir) { + if viper.GetBool(key.DownloaderCreateMangaDir) { path = filepath.Join(path, m.Dirname()) } @@ -263,7 +263,7 @@ func (m *Manga) PopulateMetadata(progress func(string)) error { var tags = make([]string, 0) for _, tag := range manga.Tags { - if tag.Rank >= viper.GetInt(constant.MetadataComicInfoXMLTagRelevanceThreshold) { + if tag.Rank >= viper.GetInt(key.MetadataComicInfoXMLTagRelevanceThreshold) { tags = append(tags, tag.Name) } } diff --git a/tui/bubble.go b/tui/bubble.go index 0ac9a827..6054424a 100644 --- a/tui/bubble.go +++ b/tui/bubble.go @@ -11,9 +11,9 @@ import ( "github.com/charmbracelet/lipgloss" "github.com/metafates/mangal/anilist" "github.com/metafates/mangal/color" - "github.com/metafates/mangal/constant" "github.com/metafates/mangal/history" "github.com/metafates/mangal/installer" + key2 "github.com/metafates/mangal/key" "github.com/metafates/mangal/provider" "github.com/metafates/mangal/source" "github.com/metafates/mangal/style" @@ -196,7 +196,7 @@ func newBubble() *statefulBubble { makeList := func(title string, description bool, options *listOptions) list.Model { delegate := list.NewDefaultDelegate() - delegate.SetSpacing(viper.GetInt(constant.TUIItemSpacing)) + delegate.SetSpacing(viper.GetInt(key2.TUIItemSpacing)) delegate.ShowDescription = description delegate.Styles.SelectedTitle = lipgloss.NewStyle(). Border(lipgloss.ThickBorder(), false, false, false, true). @@ -234,7 +234,7 @@ func newBubble() *statefulBubble { bubble.inputC = textinput.New() bubble.inputC.Placeholder = "Search" bubble.inputC.CharLimit = 60 - bubble.inputC.Prompt = viper.GetString(constant.TUISearchPromptString) + bubble.inputC.Prompt = viper.GetString(key2.TUISearchPromptString) bubble.progressC = progress.New(progress.WithDefaultGradient()) @@ -255,7 +255,7 @@ func newBubble() *statefulBubble { }) bubble.sourcesC.SetStatusBarItemName("source", "sources") - showURLs := viper.GetBool(constant.TUIShowURLs) + showURLs := viper.GetBool(key2.TUIShowURLs) bubble.mangasC = makeList("Mangas", showURLs, &listOptions{ TitleStyle: mo.Some( style.NewColored("#f2e8cf", "#386641").Padding(0, 1), diff --git a/tui/handlers.go b/tui/handlers.go index 5c98a94a..db6cbe21 100644 --- a/tui/handlers.go +++ b/tui/handlers.go @@ -6,9 +6,9 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/metafates/mangal/anilist" "github.com/metafates/mangal/color" - "github.com/metafates/mangal/constant" "github.com/metafates/mangal/downloader" "github.com/metafates/mangal/installer" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/log" "github.com/metafates/mangal/provider" "github.com/metafates/mangal/source" @@ -252,7 +252,7 @@ func (b *statefulBubble) downloadChapter(chapter *source.Chapter) tea.Cmd { }) if err != nil { - if viper.GetBool(constant.DownloaderStopOnError) { + if viper.GetBool(key.DownloaderStopOnError) { b.errorChannel <- err } else { b.failedChapters = append(b.failedChapters, chapter) diff --git a/tui/init.go b/tui/init.go index af47c4dc..e44270e3 100644 --- a/tui/init.go +++ b/tui/init.go @@ -4,13 +4,13 @@ import ( "fmt" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" - "github.com/metafates/mangal/constant" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/provider" "github.com/spf13/viper" ) func (b *statefulBubble) Init() tea.Cmd { - if names := viper.GetStringSlice(constant.DownloaderDefaultSources); b.state != historyState && len(names) != 0 { + if names := viper.GetStringSlice(key.DownloaderDefaultSources); b.state != historyState && len(names) != 0 { var providers []*provider.Provider for _, name := range names { diff --git a/tui/keymap.go b/tui/keymap.go index 292b0fb2..8186e25d 100644 --- a/tui/keymap.go +++ b/tui/keymap.go @@ -133,7 +133,6 @@ func newStatefulKeymap() *statefulKeymap { } // help returns short and full help for the state -// TODO: add more information for full help func (k *statefulKeymap) help() ([]key.Binding, []key.Binding) { h := func(bindings ...key.Binding) []key.Binding { return bindings diff --git a/tui/update.go b/tui/update.go index afd9b28d..5baa8375 100644 --- a/tui/update.go +++ b/tui/update.go @@ -8,9 +8,9 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/metafates/mangal/anilist" "github.com/metafates/mangal/color" - "github.com/metafates/mangal/constant" "github.com/metafates/mangal/history" "github.com/metafates/mangal/installer" + key2 "github.com/metafates/mangal/key" "github.com/metafates/mangal/open" "github.com/metafates/mangal/provider" "github.com/metafates/mangal/query" @@ -450,7 +450,7 @@ func (b *statefulBubble) updateMangas(msg tea.Msg) (tea.Model, tea.Cmd) { case []*source.Chapter: items := make([]list.Item, len(msg)) - if viper.GetBool(constant.TUIReverseChapters) { + if viper.GetBool(key2.TUIReverseChapters) { for i, c := range msg { items[len(msg)-i-1] = &listItem{internal: c} } @@ -464,7 +464,7 @@ func (b *statefulBubble) updateMangas(msg tea.Msg) (tea.Model, tea.Cmd) { b.newState(chaptersState) b.stopLoading() - if viper.GetBool(constant.AnilistLinkOnMangaSelect) { + if viper.GetBool(key2.AnilistLinkOnMangaSelect) { return b, tea.Batch(cmd, b.fetchAndSetAnilist(b.selectedManga), b.waitForAnilistFetchAndSet()) } @@ -568,7 +568,7 @@ func (b *statefulBubble) updateChapters(msg tea.Msg) (tea.Model, tea.Cmd) { case key.Matches(msg, b.keymap.confirm): if len(b.selectedChapters) != 0 { b.newState(confirmState) - } else if viper.GetBool(constant.TUIReadOnEnter) { + } else if viper.GetBool(key2.TUIReadOnEnter) { if b.chaptersC.SelectedItem() == nil { break } @@ -704,7 +704,7 @@ func (b *statefulBubble) updateDownloadDone(msg tea.Msg) (tea.Model, tea.Cmd) { case key.Matches(msg, b.keymap.openFolder): err := open.StartWith( lo.Must(b.currentDownloadingChapter.Manga.Path(false)), - viper.GetString(constant.ReaderFolder), + viper.GetString(key2.ReaderFolder), ) if err != nil { diff --git a/tui/view.go b/tui/view.go index 80248479..688cf06d 100644 --- a/tui/view.go +++ b/tui/view.go @@ -4,8 +4,8 @@ import ( "fmt" "github.com/charmbracelet/lipgloss" "github.com/metafates/mangal/color" - "github.com/metafates/mangal/constant" "github.com/metafates/mangal/icon" + "github.com/metafates/mangal/key" "github.com/metafates/mangal/style" "github.com/metafates/mangal/util" "github.com/muesli/reflow/wrap" @@ -125,7 +125,7 @@ func (b *statefulBubble) downloadingChapterMetainfo() string { metainfo.WriteString(" as ") } - metainfo.WriteString(style.Fg(color.Purple)(viper.GetString(constant.FormatsUse))) + metainfo.WriteString(style.Fg(color.Purple)(viper.GetString(key.FormatsUse))) return metainfo.String() } @@ -196,7 +196,7 @@ func (b *statefulBubble) viewDownloadDone() string { msg, } - if succeded > 0 && viper.GetBool(constant.TUIShowDownloadedPath) { + if succeded > 0 && viper.GetBool(key.TUIShowDownloadedPath) { path, err := b.selectedManga.Path(false) if err == nil { lines = append(lines, "") diff --git a/vendor/github.com/antchfx/xmlquery/README.md b/vendor/github.com/antchfx/xmlquery/README.md index f9f7a3fd..ac65ddcc 100644 --- a/vendor/github.com/antchfx/xmlquery/README.md +++ b/vendor/github.com/antchfx/xmlquery/README.md @@ -235,6 +235,9 @@ title.FirstChild = title_text channel.FirstChild = title fmt.Println(doc.OutputXML(true)) // W3Schools Home Page + +fmt.Println(doc.OutputXMLWithOptions(WithOutputSelf())) +// W3Schools Home Page ``` Questions diff --git a/vendor/github.com/antchfx/xmlquery/node.go b/vendor/github.com/antchfx/xmlquery/node.go index 46436957..e49358cf 100644 --- a/vendor/github.com/antchfx/xmlquery/node.go +++ b/vendor/github.com/antchfx/xmlquery/node.go @@ -50,6 +50,37 @@ type Node struct { level int // node level in the tree } +type outputConfiguration struct { + printSelf bool + preserveSpaces bool + emptyElementTagSupport bool + skipComments bool +} + +type OutputOption func(*outputConfiguration) + +// WithOutputSelf configures the Node to print the root node itself +func WithOutputSelf() OutputOption { + return func(oc *outputConfiguration) { + oc.printSelf = true + } +} + +// WithEmptyTagSupport empty tags should be written as and +// not as +func WithEmptyTagSupport() OutputOption { + return func(oc *outputConfiguration) { + oc.emptyElementTagSupport = true + } +} + +// WithoutComments will skip comments in output +func WithoutComments() OutputOption { + return func(oc *outputConfiguration) { + oc.skipComments = true + } +} + // InnerText returns the text between the start and end tags of the object. func (n *Node) InnerText() string { var output func(*bytes.Buffer, *Node) @@ -86,7 +117,7 @@ func calculatePreserveSpaces(n *Node, pastValue bool) bool { return pastValue } -func outputXML(buf *bytes.Buffer, n *Node, preserveSpaces bool) { +func outputXML(buf *bytes.Buffer, n *Node, preserveSpaces bool, config *outputConfiguration) { preserveSpaces = calculatePreserveSpaces(n, preserveSpaces) switch n.Type { case TextNode: @@ -98,9 +129,11 @@ func outputXML(buf *bytes.Buffer, n *Node, preserveSpaces bool) { buf.WriteString("]]>") return case CommentNode: - buf.WriteString("") + if !config.skipComments { + buf.WriteString("") + } return case DeclarationNode: buf.WriteString("") } else { - buf.WriteString(">") + if n.FirstChild != nil || !config.emptyElementTagSupport { + buf.WriteString(">") + } else { + buf.WriteString("/>") + return + } } for child := n.FirstChild; child != nil; child = child.NextSibling { - outputXML(buf, child, preserveSpaces) + outputXML(buf, child, preserveSpaces, config) } if n.Type != DeclarationNode { if n.Prefix == "" { @@ -141,13 +179,40 @@ func outputXML(buf *bytes.Buffer, n *Node, preserveSpaces bool) { // OutputXML returns the text that including tags name. func (n *Node) OutputXML(self bool) string { + + config := &outputConfiguration{ + printSelf: true, + emptyElementTagSupport: false, + } preserveSpaces := calculatePreserveSpaces(n, false) var buf bytes.Buffer if self && n.Type != DocumentNode { - outputXML(&buf, n, preserveSpaces) + outputXML(&buf, n, preserveSpaces, config) } else { for n := n.FirstChild; n != nil; n = n.NextSibling { - outputXML(&buf, n, preserveSpaces) + outputXML(&buf, n, preserveSpaces, config) + } + } + + return buf.String() +} + +// OutputXMLWithOptions returns the text that including tags name. +func (n *Node) OutputXMLWithOptions(opts ...OutputOption) string { + + config := &outputConfiguration{} + // Set the options + for _, opt := range opts { + opt(config) + } + + preserveSpaces := calculatePreserveSpaces(n, false) + var buf bytes.Buffer + if config.printSelf && n.Type != DocumentNode { + outputXML(&buf, n, preserveSpaces, config) + } else { + for n := n.FirstChild; n != nil; n = n.NextSibling { + outputXML(&buf, n, preserveSpaces, config) } } @@ -172,6 +237,55 @@ func AddAttr(n *Node, key, val string) { n.Attr = append(n.Attr, attr) } +// SetAttr allows an attribute value with the specified name to be changed. +// If the attribute did not previously exist, it will be created. +func (n *Node) SetAttr(key, value string) { + if i := strings.Index(key, ":"); i > 0 { + space := key[:i] + local := key[i+1:] + for idx := 0; idx < len(n.Attr); idx++ { + if n.Attr[idx].Name.Space == space && n.Attr[idx].Name.Local == local { + n.Attr[idx].Value = value + return + } + } + + AddAttr(n, key, value) + } else { + for idx := 0; idx < len(n.Attr); idx++ { + if n.Attr[idx].Name.Local == key { + n.Attr[idx].Value = value + return + } + } + + AddAttr(n, key, value) + } +} + +// RemoveAttr removes the attribute with the specified name. +func (n *Node) RemoveAttr(key string) { + removeIdx := -1 + if i := strings.Index(key, ":"); i > 0 { + space := key[:i] + local := key[i+1:] + for idx := 0; idx < len(n.Attr); idx++ { + if n.Attr[idx].Name.Space == space && n.Attr[idx].Name.Local == local { + removeIdx = idx + } + } + } else { + for idx := 0; idx < len(n.Attr); idx++ { + if n.Attr[idx].Name.Local == key { + removeIdx = idx + } + } + } + if removeIdx != -1 { + n.Attr = append(n.Attr[:removeIdx], n.Attr[removeIdx+1:]...) + } +} + // AddChild adds a new node 'n' to a node 'parent' as its last child. func AddChild(parent, n *Node) { n.Parent = parent diff --git a/vendor/github.com/antchfx/xmlquery/parse.go b/vendor/github.com/antchfx/xmlquery/parse.go index 1e8f6a4e..76f49aad 100644 --- a/vendor/github.com/antchfx/xmlquery/parse.go +++ b/vendor/github.com/antchfx/xmlquery/parse.go @@ -3,7 +3,6 @@ package xmlquery import ( "bufio" "encoding/xml" - "errors" "fmt" "io" "net/http" @@ -92,7 +91,15 @@ func (p *parser) parse() (*Node, error) { case xml.StartElement: if p.level == 0 { // mising XML declaration - node := &Node{Type: DeclarationNode, Data: "xml", level: 1} + attributes := make([]Attr, 1) + attributes[0].Name = xml.Name{Local: "version"} + attributes[0].Value = "1.0" + node := &Node{ + Type: DeclarationNode, + Data: "xml", + Attr: attributes, + level: 1, + } AddChild(p.prev, node) p.level = 1 p.prev = node @@ -106,9 +113,9 @@ func (p *parser) parse() (*Node, error) { } } - if tok.Name.Space != "" { - if _, found := p.space2prefix[tok.Name.Space]; !found { - return nil, errors.New("xmlquery: invalid XML document, namespace is missing") + if space := tok.Name.Space; space != "" { + if _, found := p.space2prefix[space]; !found && p.decoder.Strict { + return nil, fmt.Errorf("xmlquery: invalid XML document, namespace %s is missing", space) } } diff --git a/vendor/github.com/charmbracelet/bubbletea/.gitignore b/vendor/github.com/charmbracelet/bubbletea/.gitignore index 59194486..9cc52352 100644 --- a/vendor/github.com/charmbracelet/bubbletea/.gitignore +++ b/vendor/github.com/charmbracelet/bubbletea/.gitignore @@ -20,4 +20,3 @@ tutorials/basics/basics tutorials/commands/commands .idea coverage.txt -README.md.* diff --git a/vendor/github.com/charmbracelet/bubbletea/README.md b/vendor/github.com/charmbracelet/bubbletea/README.md index 4889573d..fe1b5cf5 100644 --- a/vendor/github.com/charmbracelet/bubbletea/README.md +++ b/vendor/github.com/charmbracelet/bubbletea/README.md @@ -1,5 +1,4 @@ -Bubble Tea -========== +# Bubble Tea

Bubble Tea Title Treatment
@@ -13,7 +12,7 @@ based on [The Elm Architecture][elm]. Bubble Tea is well-suited for simple and complex terminal applications, either inline, full-window, or a mix of both.

- Bubble Tea Example + Bubble Tea Example

Bubble Tea is in use in production and includes a number of features and @@ -21,29 +20,243 @@ performance optimizations we’ve added along the way. Among those is a standard framerate-based renderer, a renderer for high-performance scrollable regions which works alongside the main renderer, and mouse support. -## Getting Started +To get started, see the tutorial below, the [examples][examples], the +[docs][docs], the [video tutorials][youtube] and some common [resources](#libraries-we-use-with-bubble-tea). -We recommend starting with the [basics tutorial][basics] followed by the -[commands tutorial][commands], both of which should give you a good -understanding of how things work. +[youtube]: https://charm.sh/yt -There are a bunch of [examples][examples], too! +## By the way -[basics]: https://github.com/charmbracelet/bubbletea/tree/master/tutorials/basics -[commands]: https://github.com/charmbracelet/bubbletea/tree/master/tutorials/commands -[documentation]: https://github.com/charmbracelet/bubbletea/tree/master/docs -[examples]: https://github.com/charmbracelet/bubbletea/tree/master/examples - -## Components - -For a bunch of basic user interface components check out [Bubbles][bubbles], -the official Bubble Tea component library. +Be sure to check out [Bubbles][bubbles], a library of common UI components for Bubble Tea.

Bubbles Badge   Text Input Example from Bubbles

+*** + +## Tutorial + +Bubble Tea is based on the functional design paradigms of [The Elm +Architecture][elm], which happens to work nicely with Go. It's a delightful way +to build applications. + +This tutorial assumes you have a working knowledge of Go. + +By the way, the non-annotated source code for this program is available +[on GitHub][tut-source]. + +[elm]: https://guide.elm-lang.org/architecture/ +[tut-source]:https://github.com/charmbracelet/bubbletea/tree/master/tutorials/basics + +### Enough! Let's get to it. + +For this tutorial, we're making a shopping list. + +To start we'll define our package and import some libraries. Our only external +import will be the Bubble Tea library, which we'll call `tea` for short. + +```go +package main + +import ( + "fmt" + "os" + + tea "github.com/charmbracelet/bubbletea" +) +``` + +Bubble Tea programs are comprised of a **model** that describes the application +state and three simple methods on that model: + +* **Init**, a function that returns an initial command for the application to run. +* **Update**, a function that handles incoming events and updates the model accordingly. +* **View**, a function that renders the UI based on the data in the model. + +### The Model + +So let's start by defining our model which will store our application's state. +It can be any type, but a `struct` usually makes the most sense. + +```go +type model struct { + choices []string // items on the to-do list + cursor int // which to-do list item our cursor is pointing at + selected map[int]struct{} // which to-do items are selected +} +``` + +### Initialization + +Next, we’ll define our application’s initial state. In this case, we’re defining +a function to return our initial model, however, we could just as easily define +the initial model as a variable elsewhere, too. + +```go +func initialModel() model { + return model{ + // Our to-do list is a grocery list + choices: []string{"Buy carrots", "Buy celery", "Buy kohlrabi"}, + + // A map which indicates which choices are selected. We're using + // the map like a mathematical set. The keys refer to the indexes + // of the `choices` slice, above. + selected: make(map[int]struct{}), + } +} +``` + +Next, we define the `Init` method. `Init` can return a `Cmd` that could perform +some initial I/O. For now, we don't need to do any I/O, so for the command, +we'll just return `nil`, which translates to "no command." + +```go +func (m model) Init() tea.Cmd { + // Just return `nil`, which means "no I/O right now, please." + return nil +} +``` + +### The Update Method + +Next up is the update method. The update function is called when ”things +happen.” Its job is to look at what has happened and return an updated model in +response. It can also return a `Cmd` to make more things happen, but for now +don't worry about that part. + +In our case, when a user presses the down arrow, `Update`’s job is to notice +that the down arrow was pressed and move the cursor accordingly (or not). + +The “something happened” comes in the form of a `Msg`, which can be any type. +Messages are the result of some I/O that took place, such as a keypress, timer +tick, or a response from a server. + +We usually figure out which type of `Msg` we received with a type switch, but +you could also use a type assertion. + +For now, we'll just deal with `tea.KeyMsg` messages, which are automatically +sent to the update function when keys are pressed. + +```go +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + + // Is it a key press? + case tea.KeyMsg: + + // Cool, what was the actual key pressed? + switch msg.String() { + + // These keys should exit the program. + case "ctrl+c", "q": + return m, tea.Quit + + // The "up" and "k" keys move the cursor up + case "up", "k": + if m.cursor > 0 { + m.cursor-- + } + + // The "down" and "j" keys move the cursor down + case "down", "j": + if m.cursor < len(m.choices)-1 { + m.cursor++ + } + + // The "enter" key and the spacebar (a literal space) toggle + // the selected state for the item that the cursor is pointing at. + case "enter", " ": + _, ok := m.selected[m.cursor] + if ok { + delete(m.selected, m.cursor) + } else { + m.selected[m.cursor] = struct{}{} + } + } + } + + // Return the updated model to the Bubble Tea runtime for processing. + // Note that we're not returning a command. + return m, nil +} +``` + +You may have noticed that ctrl+c and q above return +a `tea.Quit` command with the model. That’s a special command which instructs +the Bubble Tea runtime to quit, exiting the program. + +### The View Method + +At last, it’s time to render our UI. Of all the methods, the view is the +simplest. We look at the model in its current state and use it to return +a `string`. That string is our UI! + +Because the view describes the entire UI of your application, you don’t have to +worry about redrawing logic and stuff like that. Bubble Tea takes care of it +for you. + +```go +func (m model) View() string { + // The header + s := "What should we buy at the market?\n\n" + + // Iterate over our choices + for i, choice := range m.choices { + + // Is the cursor pointing at this choice? + cursor := " " // no cursor + if m.cursor == i { + cursor = ">" // cursor! + } + + // Is this choice selected? + checked := " " // not selected + if _, ok := m.selected[i]; ok { + checked = "x" // selected! + } + + // Render the row + s += fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice) + } + + // The footer + s += "\nPress q to quit.\n" + + // Send the UI for rendering + return s +} +``` + +### All Together Now + +The last step is to simply run our program. We pass our initial model to +`tea.NewProgram` and let it rip: + +```go +func main() { + p := tea.NewProgram(initialModel()) + if _, err := p.Run(); err != nil { + fmt.Printf("Alas, there's been an error: %v", err) + os.Exit(1) + } +} +``` + +## What’s Next? + +This tutorial covers the basics of building an interactive terminal UI, but +in the real world you'll also need to perform I/O. To learn about that have a +look at the [Command Tutorial][cmd]. It's pretty simple. + +There are also several [Bubble Tea examples][examples] available and, of course, +there are [Go Docs][docs]. + +[cmd]: http://github.com/charmbracelet/bubbletea/tree/master/tutorials/commands/ +[examples]: http://github.com/charmbracelet/bubbletea/tree/master/examples +[docs]: https://pkg.go.dev/github.com/charmbracelet/bubbletea?tab=doc + ## Debugging ### Debugging with Delve @@ -65,8 +278,9 @@ actually watch out what address the first `dlv` run tells you to connect to. ### Logging Stuff -You can log to a debug file to print debug Bubble Tea applications. To do so, -include something like… +You can’t really log to stdout with Bubble Tea because your TUI is busy +occupying that! You can, however, log to a file by including something like +the following prior to starting your Bubble Tea program: ```go if len(os.Getenv("DEBUG")) > 0 { @@ -79,8 +293,8 @@ if len(os.Getenv("DEBUG")) > 0 { } ``` -…before you start your Bubble Tea program. To see what’s printed in real time, -run `tail -f debug.log` while you run your program in another window. +To see what’s being logged in real time, run `tail -f debug.log` while you run +your program in another window. ## Libraries we use with Bubble Tea @@ -106,14 +320,15 @@ For some Bubble Tea programs in production, see: * [Aztify](https://github.com/Azure/aztfy): bring Microsoft Azure resources under Terraform * [Canard](https://github.com/mrusme/canard): an RSS client * [charm](https://github.com/charmbracelet/charm): the official Charm user account manager +* [chezmoi](https://github.com/twpayne/chezmoi): manage your dotfiles across multiple machines, securely * [circumflex](https://github.com/bensadeh/circumflex): read Hacker News in your terminal * [clidle](https://github.com/ajeetdsouza/clidle): a Wordle clone for your terminal * [container-canary](https://github.com/NVIDIA/container-canary): a container validator * [dns53](https://github.com/purpleclay/dns53): dynamic DNS with Amazon Route53. Expose your EC2 quickly, securely and privately -* [fm](https://github.com/knipferrc/fm): a terminal-based file manager * [flapioca](https://github.com/kbrgl/flapioca): Flappy Bird on the CLI! -* [fztea](https://github.com/jon4hz/fztea): connect to your Flipper's UI over serial or make it accessible via SSH +* [fm](https://github.com/knipferrc/fm): a terminal-based file manager * [fork-cleaner](https://github.com/caarlos0/fork-cleaner): cleans up old and inactive forks in your GitHub account +* [fztea](https://github.com/jon4hz/fztea): connect to your Flipper's UI over serial or make it accessible via SSH * [gambit](https://github.com/maaslalani/gambit): play chess in the terminal * [gembro](https://git.sr.ht/~rafael/gembro): a mouse-driven Gemini browser * [gh-b](https://github.com/joaom00/gh-b): GitHub CLI extension to easily manage your branches @@ -121,14 +336,16 @@ For some Bubble Tea programs in production, see: * [gitflow-toolkit](https://github.com/mritd/gitflow-toolkit): a GitFlow submission tool * [Glow](https://github.com/charmbracelet/glow): a markdown reader, browser and online markdown stash * [gocovsh](https://github.com/orlangure/gocovsh): explore Go coverage reports from the CLI +* [got](https://github.com/fedeztk/got): a simple translator and text-to-speech app build on top of simplytranslate's APIs * [httpit](https://github.com/gonetx/httpit): a rapid http(s) benchmark tool * [IDNT](https://github.com/r-darwish/idnt): batch software uninstaller * [kboard](https://github.com/CamiloGarciaLaRotta/kboard): a typing game * [mandelbrot-cli](https://github.com/MicheleFiladelfia/mandelbrot-cli): Multiplatform terminal mandelbrot set explorer -* [mergestat](https://github.com/mergestat/mergestat): run SQL queries on git repositories * [mc](https://github.com/minio/mc): the official [MinIO](https://min.io) client +* [mergestat](https://github.com/mergestat/mergestat): run SQL queries on git repositories +* [Noted](https://github.com/torbratsberg/noted): Note viewer and manager * [pathos](https://github.com/chip/pathos): pathos - CLI for editing a PATH env variable -* [portal][portal]: securely send transfer between computers +* [portal](https://github.com/ZinoKader/portal): securely send transfer between computers * [redis-viewer](https://github.com/SaltFishPr/redis-viewer): browse Redis databases * [sku](https://github.com/fedeztk/sku): a simple TUI for playing sudoku inside the terminal * [Slides](https://github.com/maaslalani/slides): a markdown-based presentation tool @@ -140,21 +357,19 @@ For some Bubble Tea programs in production, see: * [termdbms](https://github.com/mathaou/termdbms): a keyboard and mouse driven database browser * [ticker](https://github.com/achannarasappa/ticker): a terminal stock watcher and stock position tracker * [tran](https://github.com/abdfnx/tran): securely transfer stuff between computers (based on [portal][portal]) +* [Typer](https://github.com/maaslalani/typer): a typing test * [tz](https://github.com/oz/tz): an aid for scheduling across multiple time zones * [ugm](https://github.com/ariasmn/ugm): a unix user and group browser -* [Typer](https://github.com/maaslalani/typer): a typing test +* [wander](https://github.com/robinovitch61/wander): HashiCorp Nomad terminal client * [wishlist](https://github.com/charmbracelet/wishlist): an SSH directory -* [Noted](https://github.com/torbratsberg/noted): Note viewer and manager - -[portal]: https://github.com/ZinoKader/portal ## Feedback -We'd love to hear your thoughts on this tutorial. Feel free to drop us a note! +We'd love to hear your thoughts on this project. Feel free to drop us a note! * [Twitter](https://twitter.com/charmcli) -* [The Fediverse](https://mastodon.technology/@charm) -* [Slack](https://charm.sh/slack) +* [The Fediverse](https://mastodon.social/@charmcli) +* [Discord](https://charm.sh/chat) ## Acknowledgments @@ -177,4 +392,4 @@ Part of [Charm](https://charm.sh). The Charm logo -Charm热爱开源 • Charm loves open source +Charm热爱开源 • Charm loves open source • نحنُ نحب المصادر المفتوحة diff --git a/vendor/github.com/charmbracelet/bubbletea/commands.go b/vendor/github.com/charmbracelet/bubbletea/commands.go index 91116388..7c30a121 100644 --- a/vendor/github.com/charmbracelet/bubbletea/commands.go +++ b/vendor/github.com/charmbracelet/bubbletea/commands.go @@ -1,12 +1,48 @@ package tea -// Convenience commands. Not part of the Bubble Tea core, but potentially -// handy. - import ( "time" ) +// Batch performs a bunch of commands concurrently with no ordering guarantees +// about the results. Use a Batch to return several commands. +// +// Example: +// +// func (m model) Init() Cmd { +// return tea.Batch(someCommand, someOtherCommand) +// } +func Batch(cmds ...Cmd) Cmd { + var validCmds []Cmd + for _, c := range cmds { + if c == nil { + continue + } + validCmds = append(validCmds, c) + } + if len(validCmds) == 0 { + return nil + } + return func() Msg { + return BatchMsg(validCmds) + } +} + +// BatchMsg is a message used to perform a bunch of commands concurrently with +// no ordering guarantees. You can send a BatchMsg with Batch. +type BatchMsg []Cmd + +// Sequence runs the given commands one at a time, in order. Contrast this with +// Batch, which runs commands concurrently. +func Sequence(cmds ...Cmd) Cmd { + return func() Msg { + return sequenceMsg(cmds) + } +} + +// sequenceMsg is used internally to run the given commands in order. +type sequenceMsg []Cmd + // Every is a command that ticks in sync with the system clock. So, if you // wanted to tick with the system clock every second, minute or hour you // could use this. It's also handy for having different things tick in sync. @@ -63,7 +99,7 @@ func Every(duration time.Duration, fn func(time.Time) Msg) Cmd { } // Tick produces a command at an interval independent of the system clock at -// the given duration. That is, the timer begins when precisely when invoked, +// the given duration. That is, the timer begins precisely when invoked, // and runs for its entire duration. // // To produce the command, pass a duration and a function which returns @@ -119,6 +155,8 @@ func Tick(d time.Duration, fn func(time.Time) Msg) Cmd { // } // // cmd := Sequentially(saveStateCmd, Quit) +// +// Deprecated: use Sequence instead. func Sequentially(cmds ...Cmd) Cmd { return func() Msg { for _, cmd := range cmds { diff --git a/vendor/github.com/charmbracelet/bubbletea/exec.go b/vendor/github.com/charmbracelet/bubbletea/exec.go index 587d91a8..fb6d91ed 100644 --- a/vendor/github.com/charmbracelet/bubbletea/exec.go +++ b/vendor/github.com/charmbracelet/bubbletea/exec.go @@ -13,7 +13,7 @@ type execMsg struct { } // Exec is used to perform arbitrary I/O in a blocking fashion, effectively -// pausing the Program while execution is runnning and resuming it when +// pausing the Program while execution is running and resuming it when // execution has completed. // // Most of the time you'll want to use ExecProcess, which runs an exec.Cmd. @@ -39,7 +39,7 @@ func Exec(c ExecCommand, fn ExecCallback) Cmd { // c := exec.Command("vim", "file.txt") // // cmd := ExecProcess(c, func(err error) Msg { -// return VimFinishedMsg{err: error} +// return VimFinishedMsg{err: err} // }) // // Or, if you don't care about errors, you could simply: @@ -109,7 +109,7 @@ func (p *Program) exec(c ExecCommand, fn ExecCallback) { } c.SetStdin(p.input) - c.SetStdout(p.output) + c.SetStdout(p.output.TTY()) c.SetStderr(os.Stderr) // Execute system command. diff --git a/vendor/github.com/charmbracelet/bubbletea/key.go b/vendor/github.com/charmbracelet/bubbletea/key.go index 07bc6519..c2e5e3ab 100644 --- a/vendor/github.com/charmbracelet/bubbletea/key.go +++ b/vendor/github.com/charmbracelet/bubbletea/key.go @@ -2,7 +2,6 @@ package tea import ( "errors" - "fmt" "io" "unicode/utf8" @@ -198,20 +197,29 @@ const ( KeyEnd KeyPgUp KeyPgDown + KeyCtrlPgUp + KeyCtrlPgDown KeyDelete + KeyInsert KeySpace KeyCtrlUp KeyCtrlDown KeyCtrlRight KeyCtrlLeft + KeyCtrlHome + KeyCtrlEnd KeyShiftUp KeyShiftDown KeyShiftRight KeyShiftLeft + KeyShiftHome + KeyShiftEnd KeyCtrlShiftUp KeyCtrlShiftDown KeyCtrlShiftLeft KeyCtrlShiftRight + KeyCtrlShiftHome + KeyCtrlShiftEnd KeyF1 KeyF2 KeyF3 @@ -281,9 +289,18 @@ var keyNames = map[KeyType]string{ KeyShiftTab: "shift+tab", KeyHome: "home", KeyEnd: "end", + KeyCtrlHome: "ctrl+home", + KeyCtrlEnd: "ctrl+end", + KeyShiftHome: "shift+home", + KeyShiftEnd: "shift+end", + KeyCtrlShiftHome: "ctrl+shift+home", + KeyCtrlShiftEnd: "ctrl+shift+end", KeyPgUp: "pgup", KeyPgDown: "pgdown", + KeyCtrlPgUp: "ctrl+pgup", + KeyCtrlPgDown: "ctrl+pgdown", KeyDelete: "delete", + KeyInsert: "insert", KeyCtrlUp: "ctrl+up", KeyCtrlDown: "ctrl+down", KeyCtrlRight: "ctrl+right", @@ -375,100 +392,175 @@ var sequences = map[string]Key{ "\x1b[1;8D": {Type: KeyCtrlShiftLeft, Alt: true}, // Miscellaneous keys - "\x1b[Z": {Type: KeyShiftTab}, + "\x1b[Z": {Type: KeyShiftTab}, + + "\x1b[2~": {Type: KeyInsert}, + "\x1b[3;2~": {Type: KeyInsert, Alt: true}, + "\x1b\x1b[2~": {Type: KeyInsert, Alt: true}, // urxvt + "\x1b[3~": {Type: KeyDelete}, "\x1b[3;3~": {Type: KeyDelete, Alt: true}, - "\x1b[1~": {Type: KeyHome}, - "\x1b[1;3H~": {Type: KeyHome, Alt: true}, - "\x1b[4~": {Type: KeyEnd}, - "\x1b[1;3F~": {Type: KeyEnd, Alt: true}, + "\x1b\x1b[3~": {Type: KeyDelete, Alt: true}, // urxvt + "\x1b[5~": {Type: KeyPgUp}, "\x1b[5;3~": {Type: KeyPgUp, Alt: true}, + "\x1b\x1b[5~": {Type: KeyPgUp, Alt: true}, // urxvt + "\x1b[5;5~": {Type: KeyCtrlPgUp}, + "\x1b[5^": {Type: KeyCtrlPgUp}, // urxvt + "\x1b[5;7~": {Type: KeyCtrlPgUp, Alt: true}, + "\x1b\x1b[5^": {Type: KeyCtrlPgUp, Alt: true}, // urxvt + "\x1b[6~": {Type: KeyPgDown}, "\x1b[6;3~": {Type: KeyPgDown, Alt: true}, - "\x1b[7~": {Type: KeyHome}, // urxvt - "\x1b[8~": {Type: KeyEnd}, // urxvt - "\x1b\x1b[3~": {Type: KeyDelete, Alt: true}, // urxvt - "\x1b\x1b[5~": {Type: KeyPgUp, Alt: true}, // urxvt "\x1b\x1b[6~": {Type: KeyPgDown, Alt: true}, // urxvt - "\x1b\x1b[7~": {Type: KeyHome, Alt: true}, // urxvt - "\x1b\x1b[8~": {Type: KeyEnd, Alt: true}, // urxvt + "\x1b[6;5~": {Type: KeyCtrlPgDown}, + "\x1b[6^": {Type: KeyCtrlPgDown}, // urxvt + "\x1b[6;7~": {Type: KeyCtrlPgDown, Alt: true}, + "\x1b\x1b[6^": {Type: KeyCtrlPgDown, Alt: true}, // urxvt + + "\x1b[1~": {Type: KeyHome}, + "\x1b[H": {Type: KeyHome}, // xterm, lxterm + "\x1b[1;3H": {Type: KeyHome, Alt: true}, // xterm, lxterm + "\x1b[1;5H": {Type: KeyCtrlHome}, // xterm, lxterm + "\x1b[1;7H": {Type: KeyCtrlHome, Alt: true}, // xterm, lxterm + "\x1b[1;2H": {Type: KeyShiftHome}, // xterm, lxterm + "\x1b[1;4H": {Type: KeyShiftHome, Alt: true}, // xterm, lxterm + "\x1b[1;6H": {Type: KeyCtrlShiftHome}, // xterm, lxterm + "\x1b[1;8H": {Type: KeyCtrlShiftHome, Alt: true}, // xterm, lxterm + + "\x1b[4~": {Type: KeyEnd}, + "\x1b[F": {Type: KeyEnd}, // xterm, lxterm + "\x1b[1;3F": {Type: KeyEnd, Alt: true}, // xterm, lxterm + "\x1b[1;5F": {Type: KeyCtrlEnd}, // xterm, lxterm + "\x1b[1;7F": {Type: KeyCtrlEnd, Alt: true}, // xterm, lxterm + "\x1b[1;2F": {Type: KeyShiftEnd}, // xterm, lxterm + "\x1b[1;4F": {Type: KeyShiftEnd, Alt: true}, // xterm, lxterm + "\x1b[1;6F": {Type: KeyCtrlShiftEnd}, // xterm, lxterm + "\x1b[1;8F": {Type: KeyCtrlShiftEnd, Alt: true}, // xterm, lxterm + + "\x1b[7~": {Type: KeyHome}, // urxvt + "\x1b\x1b[7~": {Type: KeyHome, Alt: true}, // urxvt + "\x1b[7^": {Type: KeyCtrlHome}, // urxvt + "\x1b\x1b[7^": {Type: KeyCtrlHome, Alt: true}, // urxvt + "\x1b[7$": {Type: KeyShiftHome}, // urxvt + "\x1b\x1b[7$": {Type: KeyShiftHome, Alt: true}, // urxvt + "\x1b[7@": {Type: KeyCtrlShiftHome}, // urxvt + "\x1b\x1b[7@": {Type: KeyCtrlShiftHome, Alt: true}, // urxvt + + "\x1b[8~": {Type: KeyEnd}, // urxvt + "\x1b\x1b[8~": {Type: KeyEnd, Alt: true}, // urxvt + "\x1b[8^": {Type: KeyCtrlEnd}, // urxvt + "\x1b\x1b[8^": {Type: KeyCtrlEnd, Alt: true}, // urxvt + "\x1b[8$": {Type: KeyShiftEnd}, // urxvt + "\x1b\x1b[8$": {Type: KeyShiftEnd, Alt: true}, // urxvt + "\x1b[8@": {Type: KeyCtrlShiftEnd}, // urxvt + "\x1b\x1b[8@": {Type: KeyCtrlShiftEnd, Alt: true}, // urxvt + + // Function keys, Linux console + "\x1b[[A": {Type: KeyF1}, // linux console + "\x1b[[B": {Type: KeyF2}, // linux console + "\x1b[[C": {Type: KeyF3}, // linux console + "\x1b[[D": {Type: KeyF4}, // linux console + "\x1b[[E": {Type: KeyF5}, // linux console // Function keys, X11 - "\x1bOP": {Type: KeyF1}, // vt100 - "\x1bOQ": {Type: KeyF2}, // vt100 - "\x1bOR": {Type: KeyF3}, // vt100 - "\x1bOS": {Type: KeyF4}, // vt100 - "\x1b[15~": {Type: KeyF5}, // also urxvt - "\x1b[17~": {Type: KeyF6}, // also urxvt - "\x1b[18~": {Type: KeyF7}, // also urxvt - "\x1b[19~": {Type: KeyF8}, // also urxvt - "\x1b[20~": {Type: KeyF9}, // also urxvt - "\x1b[21~": {Type: KeyF10}, // also urxvt - "\x1b[23~": {Type: KeyF11}, // also urxvt - "\x1b[24~": {Type: KeyF12}, // also urxvt - "\x1b[1;2P": {Type: KeyF13}, - "\x1b[1;2Q": {Type: KeyF14}, - "\x1b[1;2R": {Type: KeyF15}, - "\x1b[1;2S": {Type: KeyF16}, + "\x1bOP": {Type: KeyF1}, // vt100, xterm + "\x1bOQ": {Type: KeyF2}, // vt100, xterm + "\x1bOR": {Type: KeyF3}, // vt100, xterm + "\x1bOS": {Type: KeyF4}, // vt100, xterm + + "\x1b[1;3P": {Type: KeyF1, Alt: true}, // vt100, xterm + "\x1b[1;3Q": {Type: KeyF2, Alt: true}, // vt100, xterm + "\x1b[1;3R": {Type: KeyF3, Alt: true}, // vt100, xterm + "\x1b[1;3S": {Type: KeyF4, Alt: true}, // vt100, xterm + + "\x1b[11~": {Type: KeyF1}, // urxvt + "\x1b[12~": {Type: KeyF2}, // urxvt + "\x1b[13~": {Type: KeyF3}, // urxvt + "\x1b[14~": {Type: KeyF4}, // urxvt + + "\x1b\x1b[11~": {Type: KeyF1, Alt: true}, // urxvt + "\x1b\x1b[12~": {Type: KeyF2, Alt: true}, // urxvt + "\x1b\x1b[13~": {Type: KeyF3, Alt: true}, // urxvt + "\x1b\x1b[14~": {Type: KeyF4, Alt: true}, // urxvt + + "\x1b[15~": {Type: KeyF5}, // vt100, xterm, also urxvt + + "\x1b[15;3~": {Type: KeyF5, Alt: true}, // vt100, xterm, also urxvt + + "\x1b\x1b[15~": {Type: KeyF5, Alt: true}, // urxvt + + "\x1b[17~": {Type: KeyF6}, // vt100, xterm, also urxvt + "\x1b[18~": {Type: KeyF7}, // vt100, xterm, also urxvt + "\x1b[19~": {Type: KeyF8}, // vt100, xterm, also urxvt + "\x1b[20~": {Type: KeyF9}, // vt100, xterm, also urxvt + "\x1b[21~": {Type: KeyF10}, // vt100, xterm, also urxvt + + "\x1b\x1b[17~": {Type: KeyF6, Alt: true}, // urxvt + "\x1b\x1b[18~": {Type: KeyF7, Alt: true}, // urxvt + "\x1b\x1b[19~": {Type: KeyF8, Alt: true}, // urxvt + "\x1b\x1b[20~": {Type: KeyF9, Alt: true}, // urxvt + "\x1b\x1b[21~": {Type: KeyF10, Alt: true}, // urxvt + + "\x1b[17;3~": {Type: KeyF6, Alt: true}, // vt100, xterm + "\x1b[18;3~": {Type: KeyF7, Alt: true}, // vt100, xterm + "\x1b[19;3~": {Type: KeyF8, Alt: true}, // vt100, xterm + "\x1b[20;3~": {Type: KeyF9, Alt: true}, // vt100, xterm + "\x1b[21;3~": {Type: KeyF10, Alt: true}, // vt100, xterm + + "\x1b[23~": {Type: KeyF11}, // vt100, xterm, also urxvt + "\x1b[24~": {Type: KeyF12}, // vt100, xterm, also urxvt + + "\x1b[23;3~": {Type: KeyF11, Alt: true}, // vt100, xterm + "\x1b[24;3~": {Type: KeyF12, Alt: true}, // vt100, xterm + + "\x1b\x1b[23~": {Type: KeyF11, Alt: true}, // urxvt + "\x1b\x1b[24~": {Type: KeyF12, Alt: true}, // urxvt + + "\x1b[1;2P": {Type: KeyF13}, + "\x1b[1;2Q": {Type: KeyF14}, + + "\x1b[25~": {Type: KeyF13}, // vt100, xterm, also urxvt + "\x1b[26~": {Type: KeyF14}, // vt100, xterm, also urxvt + + "\x1b[25;3~": {Type: KeyF13, Alt: true}, // vt100, xterm + "\x1b[26;3~": {Type: KeyF14, Alt: true}, // vt100, xterm + + "\x1b\x1b[25~": {Type: KeyF13, Alt: true}, // urxvt + "\x1b\x1b[26~": {Type: KeyF14, Alt: true}, // urxvt + + "\x1b[1;2R": {Type: KeyF15}, + "\x1b[1;2S": {Type: KeyF16}, + + "\x1b[28~": {Type: KeyF15}, // vt100, xterm, also urxvt + "\x1b[29~": {Type: KeyF16}, // vt100, xterm, also urxvt + + "\x1b[28;3~": {Type: KeyF15, Alt: true}, // vt100, xterm + "\x1b[29;3~": {Type: KeyF16, Alt: true}, // vt100, xterm + + "\x1b\x1b[28~": {Type: KeyF15, Alt: true}, // urxvt + "\x1b\x1b[29~": {Type: KeyF16, Alt: true}, // urxvt + "\x1b[15;2~": {Type: KeyF17}, "\x1b[17;2~": {Type: KeyF18}, "\x1b[18;2~": {Type: KeyF19}, "\x1b[19;2~": {Type: KeyF20}, - // Function keys with the alt modifier, X11 - "\x1b[1;3P": {Type: KeyF1, Alt: true}, - "\x1b[1;3Q": {Type: KeyF2, Alt: true}, - "\x1b[1;3R": {Type: KeyF3, Alt: true}, - "\x1b[1;3S": {Type: KeyF4, Alt: true}, - "\x1b[15;3~": {Type: KeyF5, Alt: true}, - "\x1b[17;3~": {Type: KeyF6, Alt: true}, - "\x1b[18;3~": {Type: KeyF7, Alt: true}, - "\x1b[19;3~": {Type: KeyF8, Alt: true}, - "\x1b[20;3~": {Type: KeyF9, Alt: true}, - "\x1b[21;3~": {Type: KeyF10, Alt: true}, - "\x1b[23;3~": {Type: KeyF11, Alt: true}, - "\x1b[24;3~": {Type: KeyF12, Alt: true}, - - // Function keys, urxvt - "\x1b[11~": {Type: KeyF1}, - "\x1b[12~": {Type: KeyF2}, - "\x1b[13~": {Type: KeyF3}, - "\x1b[14~": {Type: KeyF4}, - "\x1b[25~": {Type: KeyF13}, - "\x1b[26~": {Type: KeyF14}, - "\x1b[28~": {Type: KeyF15}, - "\x1b[29~": {Type: KeyF16}, "\x1b[31~": {Type: KeyF17}, "\x1b[32~": {Type: KeyF18}, "\x1b[33~": {Type: KeyF19}, "\x1b[34~": {Type: KeyF20}, - // Function keys with the alt modifier, urxvt - "\x1b\x1b[11~": {Type: KeyF1, Alt: true}, - "\x1b\x1b[12~": {Type: KeyF2, Alt: true}, - "\x1b\x1b[13~": {Type: KeyF3, Alt: true}, - "\x1b\x1b[14~": {Type: KeyF4, Alt: true}, - "\x1b\x1b[25~": {Type: KeyF13, Alt: true}, - "\x1b\x1b[26~": {Type: KeyF14, Alt: true}, - "\x1b\x1b[28~": {Type: KeyF15, Alt: true}, - "\x1b\x1b[29~": {Type: KeyF16, Alt: true}, - "\x1b\x1b[31~": {Type: KeyF17, Alt: true}, - "\x1b\x1b[32~": {Type: KeyF18, Alt: true}, - "\x1b\x1b[33~": {Type: KeyF19, Alt: true}, - "\x1b\x1b[34~": {Type: KeyF20, Alt: true}, -} + "\x1b\x1b[31~": {Type: KeyF17, Alt: true}, // urxvt + "\x1b\x1b[32~": {Type: KeyF18, Alt: true}, // urxvt + "\x1b\x1b[33~": {Type: KeyF19, Alt: true}, // urxvt + "\x1b\x1b[34~": {Type: KeyF20, Alt: true}, // urxvt -// Hex code mappings. -var hexes = map[string]Key{ - "1b0d": {Type: KeyEnter, Alt: true}, - "1b7f": {Type: KeyBackspace, Alt: true}, - - // Powershell - "1b4f41": {Type: KeyUp, Alt: false}, - "1b4f42": {Type: KeyDown, Alt: false}, - "1b4f43": {Type: KeyRight, Alt: false}, - "1b4f44": {Type: KeyLeft, Alt: false}, + // Powershell sequences. + "\x1bOA": {Type: KeyUp, Alt: false}, + "\x1bOB": {Type: KeyDown, Alt: false}, + "\x1bOC": {Type: KeyRight, Alt: false}, + "\x1bOD": {Type: KeyLeft, Alt: false}, } // readInputs reads keypress and mouse inputs from a TTY and returns messages @@ -534,37 +626,37 @@ func readInputs(input io.Reader) ([]Msg, error) { continue } - // Some of these need special handling. - hex := fmt.Sprintf("%x", runes) - if k, ok := hexes[hex]; ok { - msgs = append(msgs, KeyMsg(k)) + // Is this an unrecognized CSI sequence? If so, ignore it. + if len(runes) > 2 && runes[0] == 0x1b && (runes[1] == '[' || + (len(runes) > 3 && runes[1] == 0x1b && runes[2] == '[')) { continue } // Is the alt key pressed? If so, the buffer will be prefixed with an // escape. + alt := false if len(runes) > 1 && runes[0] == 0x1b { - msgs = append(msgs, KeyMsg(Key{Alt: true, Type: KeyRunes, Runes: runes[1:]})) - continue + alt = true + runes = runes[1:] } for _, v := range runes { // Is the first rune a control character? r := KeyType(v) if r <= keyUS || r == keyDEL { - msgs = append(msgs, KeyMsg(Key{Type: r})) + msgs = append(msgs, KeyMsg(Key{Type: r, Alt: alt})) continue } // If it's a space, override the type with KeySpace (but still include // the rune). if r == ' ' { - msgs = append(msgs, KeyMsg(Key{Type: KeySpace, Runes: []rune{v}})) + msgs = append(msgs, KeyMsg(Key{Type: KeySpace, Runes: []rune{v}, Alt: alt})) continue } // Welp, just regular, ol' runes. - msgs = append(msgs, KeyMsg(Key{Type: KeyRunes, Runes: []rune{v}})) + msgs = append(msgs, KeyMsg(Key{Type: KeyRunes, Runes: []rune{v}, Alt: alt})) } } diff --git a/vendor/github.com/charmbracelet/bubbletea/logging.go b/vendor/github.com/charmbracelet/bubbletea/logging.go index 54a4350a..97ffcb70 100644 --- a/vendor/github.com/charmbracelet/bubbletea/logging.go +++ b/vendor/github.com/charmbracelet/bubbletea/logging.go @@ -19,7 +19,7 @@ import ( // } // defer f.Close() func LogToFile(path string, prefix string) (*os.File, error) { - f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) + f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0o644) if err != nil { return nil, err } diff --git a/vendor/github.com/charmbracelet/bubbletea/nil_renderer.go b/vendor/github.com/charmbracelet/bubbletea/nil_renderer.go index 03dd9494..a0226364 100644 --- a/vendor/github.com/charmbracelet/bubbletea/nil_renderer.go +++ b/vendor/github.com/charmbracelet/bubbletea/nil_renderer.go @@ -2,10 +2,18 @@ package tea type nilRenderer struct{} -func (n nilRenderer) start() {} -func (n nilRenderer) stop() {} -func (n nilRenderer) kill() {} -func (n nilRenderer) write(v string) {} -func (n nilRenderer) repaint() {} -func (n nilRenderer) altScreen() bool { return false } -func (n nilRenderer) setAltScreen(v bool) {} +func (n nilRenderer) start() {} +func (n nilRenderer) stop() {} +func (n nilRenderer) kill() {} +func (n nilRenderer) write(v string) {} +func (n nilRenderer) repaint() {} +func (n nilRenderer) clearScreen() {} +func (n nilRenderer) altScreen() bool { return false } +func (n nilRenderer) enterAltScreen() {} +func (n nilRenderer) exitAltScreen() {} +func (n nilRenderer) showCursor() {} +func (n nilRenderer) hideCursor() {} +func (n nilRenderer) enableMouseCellMotion() {} +func (n nilRenderer) disableMouseCellMotion() {} +func (n nilRenderer) enableMouseAllMotion() {} +func (n nilRenderer) disableMouseAllMotion() {} diff --git a/vendor/github.com/charmbracelet/bubbletea/options.go b/vendor/github.com/charmbracelet/bubbletea/options.go index b60d23c2..d9ce42c7 100644 --- a/vendor/github.com/charmbracelet/bubbletea/options.go +++ b/vendor/github.com/charmbracelet/bubbletea/options.go @@ -1,6 +1,11 @@ package tea -import "io" +import ( + "context" + "io" + + "github.com/muesli/termenv" +) // ProgramOption is used to set options when initializing a Program. Program can // accept a variable number of options. @@ -10,20 +15,29 @@ import "io" // p := NewProgram(model, WithInput(someInput), WithOutput(someOutput)) type ProgramOption func(*Program) +// WithContext lets you specify a context in which to run the Program. This is +// useful if you want to cancel the execution from outside. When a Program gets +// cancelled it will exit with an error ErrProgramKilled. +func WithContext(ctx context.Context) ProgramOption { + return func(p *Program) { + p.ctx = ctx + } +} + // WithOutput sets the output which, by default, is stdout. In most cases you // won't need to use this. func WithOutput(output io.Writer) ProgramOption { - return func(m *Program) { - m.output = output + return func(p *Program) { + p.output = termenv.NewOutput(output, termenv.WithColorCache(true)) } } // WithInput sets the input which, by default, is stdin. In most cases you // won't need to use this. func WithInput(input io.Reader) ProgramOption { - return func(m *Program) { - m.input = input - m.startupOptions |= withCustomInput + return func(p *Program) { + p.input = input + p.startupOptions |= withCustomInput } } @@ -34,13 +48,21 @@ func WithInputTTY() ProgramOption { } } +// WithoutSignalHandler disables the signal handler that Bubble Tea sets up for +// Programs. This is useful if you want to handle signals yourself. +func WithoutSignalHandler() ProgramOption { + return func(p *Program) { + p.startupOptions |= withoutSignalHandler + } +} + // WithoutCatchPanics disables the panic catching that Bubble Tea does by // default. If panic catching is disabled the terminal will be in a fairly // unusable state after a panic because Bubble Tea will not perform its usual // cleanup on exit. func WithoutCatchPanics() ProgramOption { - return func(m *Program) { - m.CatchPanics = false + return func(p *Program) { + p.startupOptions |= withoutCatchPanics } } @@ -51,7 +73,7 @@ func WithoutCatchPanics() ProgramOption { // Example: // // p := tea.NewProgram(Model{}, tea.WithAltScreen()) -// if err := p.Start(); err != nil { +// if _, err := p.Run(); err != nil { // fmt.Println("Error running program:", err) // os.Exit(1) // } @@ -114,8 +136,8 @@ func WithMouseAllMotion() ProgramOption { // programs. For example, your program could behave like a daemon if output is // not a TTY. func WithoutRenderer() ProgramOption { - return func(m *Program) { - m.renderer = &nilRenderer{} + return func(p *Program) { + p.renderer = &nilRenderer{} } } diff --git a/vendor/github.com/charmbracelet/bubbletea/renderer.go b/vendor/github.com/charmbracelet/bubbletea/renderer.go index aedbb666..a6f41627 100644 --- a/vendor/github.com/charmbracelet/bubbletea/renderer.go +++ b/vendor/github.com/charmbracelet/bubbletea/renderer.go @@ -21,12 +21,35 @@ type renderer interface { // in succession. repaint() + // Clears the terminal. + clearScreen() + // Whether or not the alternate screen buffer is enabled. altScreen() bool - - // Record internally that the alternate screen buffer is enabled. This - // does not actually toggle the alternate screen buffer. - setAltScreen(bool) + // Enable the alternate screen buffer. + enterAltScreen() + // Disable the alternate screen buffer. + exitAltScreen() + + // Show the cursor. + showCursor() + // Hide the cursor. + hideCursor() + + // enableMouseCellMotion enables mouse click, release, wheel and motion + // events if a mouse button is pressed (i.e., drag events). + enableMouseCellMotion() + + // DisableMouseCellMotion disables Mouse Cell Motion tracking. + disableMouseCellMotion() + + // EnableMouseAllMotion enables mouse click, release, wheel and motion + // events, regardless of whether a mouse button is pressed. Many modern + // terminals support this, but not all. + enableMouseAllMotion() + + // DisableMouseAllMotion disables All Motion mouse tracking. + disableMouseAllMotion() } // repaintMsg forces a full repaint. diff --git a/vendor/github.com/charmbracelet/bubbletea/screen.go b/vendor/github.com/charmbracelet/bubbletea/screen.go index 63db7ae6..899db3d2 100644 --- a/vendor/github.com/charmbracelet/bubbletea/screen.go +++ b/vendor/github.com/charmbracelet/bubbletea/screen.go @@ -1,53 +1,169 @@ package tea -import ( - "fmt" - "io" +// WindowSizeMsg is used to report the terminal size. It's sent to Update once +// initially and then on every terminal resize. Note that Windows does not +// have support for reporting when resizes occur as it does not support the +// SIGWINCH signal. +type WindowSizeMsg struct { + Width int + Height int +} + +// ClearScreen is a special command that tells the program to clear the screen +// before the next update. This can be used to move the cursor to the top left +// of the screen and clear visual clutter when the alt screen is not in use. +// +// Note that it should never be necessary to call ClearScreen() for regular +// redraws. +func ClearScreen() Msg { + return clearScreenMsg{} +} + +// clearScreenMsg is an internal message that signals to clear the screen. +// You can send a clearScreenMsg with ClearScreen. +type clearScreenMsg struct{} + +// EnterAltScreen is a special command that tells the Bubble Tea program to +// enter the alternate screen buffer. +// +// Because commands run asynchronously, this command should not be used in your +// model's Init function. To initialize your program with the altscreen enabled +// use the WithAltScreen ProgramOption instead. +func EnterAltScreen() Msg { + return enterAltScreenMsg{} +} + +// enterAltScreenMsg in an internal message signals that the program should +// enter alternate screen buffer. You can send a enterAltScreenMsg with +// EnterAltScreen. +type enterAltScreenMsg struct{} - te "github.com/muesli/termenv" -) +// ExitAltScreen is a special command that tells the Bubble Tea program to exit +// the alternate screen buffer. This command should be used to exit the +// alternate screen buffer while the program is running. +// +// Note that the alternate screen buffer will be automatically exited when the +// program quits. +func ExitAltScreen() Msg { + return exitAltScreenMsg{} +} + +// exitAltScreenMsg in an internal message signals that the program should exit +// alternate screen buffer. You can send a exitAltScreenMsg with ExitAltScreen. +type exitAltScreenMsg struct{} -func hideCursor(w io.Writer) { - fmt.Fprintf(w, te.CSI+te.HideCursorSeq) +// EnableMouseCellMotion is a special command that enables mouse click, +// release, and wheel events. Mouse movement events are also captured if +// a mouse button is pressed (i.e., drag events). +// +// Because commands run asynchronously, this command should not be used in your +// model's Init function. Use the WithMouseCellMotion ProgramOption instead. +func EnableMouseCellMotion() Msg { + return enableMouseCellMotionMsg{} } -func showCursor(w io.Writer) { - fmt.Fprintf(w, te.CSI+te.ShowCursorSeq) +// enableMouseCellMotionMsg is a special command that signals to start +// listening for "cell motion" type mouse events (ESC[?1002l). To send an +// enableMouseCellMotionMsg, use the EnableMouseCellMotion command. +type enableMouseCellMotionMsg struct{} + +// EnableMouseAllMotion is a special command that enables mouse click, release, +// wheel, and motion events, which are delivered regardless of whether a mouse +// button is pressed, effectively enabling support for hover interactions. +// +// Many modern terminals support this, but not all. If in doubt, use +// EnableMouseCellMotion instead. +// +// Because commands run asynchronously, this command should not be used in your +// model's Init function. Use the WithMouseAllMotion ProgramOption instead. +func EnableMouseAllMotion() Msg { + return enableMouseAllMotionMsg{} } -func clearLine(w io.Writer) { - fmt.Fprintf(w, te.CSI+te.EraseLineSeq, 2) +// enableMouseAllMotionMsg is a special command that signals to start listening +// for "all motion" type mouse events (ESC[?1003l). To send an +// enableMouseAllMotionMsg, use the EnableMouseAllMotion command. +type enableMouseAllMotionMsg struct{} + +// DisableMouse is a special command that stops listening for mouse events. +func DisableMouse() Msg { + return disableMouseMsg{} } -func cursorUp(w io.Writer) { - fmt.Fprintf(w, te.CSI+te.CursorUpSeq, 1) +// disableMouseMsg is an internal message that signals to stop listening +// for mouse events. To send a disableMouseMsg, use the DisableMouse command. +type disableMouseMsg struct{} + +// HideCursor is a special command for manually instructing Bubble Tea to hide +// the cursor. In some rare cases, certain operations will cause the terminal +// to show the cursor, which is normally hidden for the duration of a Bubble +// Tea program's lifetime. You will most likely not need to use this command. +func HideCursor() Msg { + return hideCursorMsg{} } -func cursorDown(w io.Writer) { - fmt.Fprintf(w, te.CSI+te.CursorDownSeq, 1) +// hideCursorMsg is an internal command used to hide the cursor. You can send +// this message with HideCursor. +type hideCursorMsg struct{} + +// ShowCursor is a special command for manually instructing Bubble Tea to show +// the cursor. +func ShowCursor() Msg { + return showCursorMsg{} } -func insertLine(w io.Writer, numLines int) { - fmt.Fprintf(w, te.CSI+"%dL", numLines) +// showCursorMsg is an internal command used to show the cursor. You can send +// this message with ShowCursor. +type showCursorMsg struct{} + +// EnterAltScreen enters the alternate screen buffer, which consumes the entire +// terminal window. ExitAltScreen will return the terminal to its former state. +// +// Deprecated: Use the WithAltScreen ProgramOption instead. +func (p *Program) EnterAltScreen() { + if p.renderer != nil { + p.renderer.enterAltScreen() + } } -func moveCursor(w io.Writer, row, col int) { - fmt.Fprintf(w, te.CSI+te.CursorPositionSeq, row, col) +// ExitAltScreen exits the alternate screen buffer. +// +// Deprecated: The altscreen will exited automatically when the program exits. +func (p *Program) ExitAltScreen() { + if p.renderer != nil { + p.renderer.exitAltScreen() + } } -func changeScrollingRegion(w io.Writer, top, bottom int) { - fmt.Fprintf(w, te.CSI+te.ChangeScrollingRegionSeq, top, bottom) +// EnableMouseCellMotion enables mouse click, release, wheel and motion events +// if a mouse button is pressed (i.e., drag events). +// +// Deprecated: Use the WithMouseCellMotion ProgramOption instead. +func (p *Program) EnableMouseCellMotion() { + p.renderer.enableMouseCellMotion() } -func cursorBack(w io.Writer, n int) { - fmt.Fprintf(w, te.CSI+te.CursorBackSeq, n) +// DisableMouseCellMotion disables Mouse Cell Motion tracking. This will be +// called automatically when exiting a Bubble Tea program. +// +// Deprecated: The mouse will automatically be disabled when the program exits. +func (p *Program) DisableMouseCellMotion() { + p.renderer.disableMouseCellMotion() } -func enterAltScreen(w io.Writer) { - fmt.Fprintf(w, te.CSI+te.AltScreenSeq) - moveCursor(w, 0, 0) +// EnableMouseAllMotion enables mouse click, release, wheel and motion events, +// regardless of whether a mouse button is pressed. Many modern terminals +// support this, but not all. +// +// Deprecated: Use the WithMouseAllMotion ProgramOption instead. +func (p *Program) EnableMouseAllMotion() { + p.renderer.enableMouseAllMotion() } -func exitAltScreen(w io.Writer) { - fmt.Fprintf(w, te.CSI+te.ExitAltScreenSeq) +// DisableMouseAllMotion disables All Motion mouse tracking. This will be +// called automatically when exiting a Bubble Tea program. +// +// Deprecated: The mouse will automatically be disabled when the program exits. +func (p *Program) DisableMouseAllMotion() { + p.renderer.disableMouseAllMotion() } diff --git a/vendor/github.com/charmbracelet/bubbletea/signals_unix.go b/vendor/github.com/charmbracelet/bubbletea/signals_unix.go index ef9ed9a3..826f58b9 100644 --- a/vendor/github.com/charmbracelet/bubbletea/signals_unix.go +++ b/vendor/github.com/charmbracelet/bubbletea/signals_unix.go @@ -4,18 +4,15 @@ package tea import ( - "context" "os" "os/signal" "syscall" - - "golang.org/x/term" ) // listenForResize sends messages (or errors) when the terminal resizes. // Argument output should be the file descriptor for the terminal; usually // os.Stdout. -func listenForResize(ctx context.Context, output *os.File, msgs chan Msg, errs chan error, done chan struct{}) { +func (p *Program) listenForResize(done chan struct{}) { sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGWINCH) @@ -26,20 +23,11 @@ func listenForResize(ctx context.Context, output *os.File, msgs chan Msg, errs c for { select { - case <-ctx.Done(): + case <-p.ctx.Done(): return case <-sig: } - w, h, err := term.GetSize(int(output.Fd())) - if err != nil { - errs <- err - } - - select { - case <-ctx.Done(): - return - case msgs <- WindowSizeMsg{w, h}: - } + p.checkResize() } } diff --git a/vendor/github.com/charmbracelet/bubbletea/signals_windows.go b/vendor/github.com/charmbracelet/bubbletea/signals_windows.go index 71da4f00..2fc6f8ae 100644 --- a/vendor/github.com/charmbracelet/bubbletea/signals_windows.go +++ b/vendor/github.com/charmbracelet/bubbletea/signals_windows.go @@ -3,14 +3,8 @@ package tea -import ( - "context" - "os" -) - // listenForResize is not available on windows because windows does not // implement syscall.SIGWINCH. -func listenForResize(ctx context.Context, output *os.File, msgs chan Msg, - errs chan error, done chan struct{}) { +func (p *Program) listenForResize(done chan struct{}) { close(done) } diff --git a/vendor/github.com/charmbracelet/bubbletea/standard_renderer.go b/vendor/github.com/charmbracelet/bubbletea/standard_renderer.go index d2bc387f..17b44e23 100644 --- a/vendor/github.com/charmbracelet/bubbletea/standard_renderer.go +++ b/vendor/github.com/charmbracelet/bubbletea/standard_renderer.go @@ -10,6 +10,7 @@ import ( "github.com/muesli/ansi/compressor" "github.com/muesli/reflow/truncate" + "github.com/muesli/termenv" ) const ( @@ -24,18 +25,22 @@ const ( // In cases where very high performance is needed the renderer can be told // to exclude ranges of lines, allowing them to be written to directly. type standardRenderer struct { - out io.Writer + mtx *sync.Mutex + out *termenv.Output + buf bytes.Buffer queuedMessageLines []string framerate time.Duration ticker *time.Ticker - mtx *sync.Mutex done chan struct{} lastRender string linesRendered int useANSICompressor bool once sync.Once + // cursor visibility state + cursorHidden bool + // essentially whether or not we're using the full size of the terminal altScreenActive bool @@ -49,16 +54,16 @@ type standardRenderer struct { // newRenderer creates a new renderer. Normally you'll want to initialize it // with os.Stdout as the first argument. -func newRenderer(out io.Writer, mtx *sync.Mutex, useANSICompressor bool) renderer { +func newRenderer(out *termenv.Output, useANSICompressor bool) renderer { r := &standardRenderer{ out: out, - mtx: mtx, + mtx: &sync.Mutex{}, framerate: defaultFramerate, useANSICompressor: useANSICompressor, queuedMessageLines: []string{}, } if r.useANSICompressor { - r.out = &compressor.Writer{Forward: out} + r.out = termenv.NewOutput(&compressor.Writer{Forward: out}) } return r } @@ -74,14 +79,19 @@ func (r *standardRenderer) start() { // stop permanently halts the renderer, rendering the final frame. func (r *standardRenderer) stop() { + // flush locks the mutex r.flush() - clearLine(r.out) + + r.mtx.Lock() + defer r.mtx.Unlock() + + r.out.ClearLine() r.once.Do(func() { close(r.done) }) if r.useANSICompressor { - if w, ok := r.out.(io.WriteCloser); ok { + if w, ok := r.out.TTY().(io.WriteCloser); ok { _ = w.Close() } } @@ -89,7 +99,10 @@ func (r *standardRenderer) stop() { // kill halts the renderer. The final frame will not be rendered. func (r *standardRenderer) kill() { - clearLine(r.out) + r.mtx.Lock() + defer r.mtx.Unlock() + + r.out.ClearLine() r.once.Do(func() { close(r.done) }) @@ -122,9 +135,19 @@ func (r *standardRenderer) flush() { } // Output buffer - out := new(bytes.Buffer) + buf := &bytes.Buffer{} + out := termenv.NewOutput(buf) newLines := strings.Split(r.buf.String(), "\n") + + // If we know the output's height, we can use it to determine how many + // lines we can render. We drop lines from the top of the render buffer if + // necessary, as we can't navigate the cursor into the terminal's scrollback + // buffer. + if r.height > 0 && len(newLines) > r.height { + newLines = newLines[len(newLines)-r.height:] + } + numLinesThisFlush := len(newLines) oldLines := strings.Split(r.lastRender, "\n") skipLines := make(map[int]struct{}) @@ -145,10 +168,10 @@ func (r *standardRenderer) flush() { if (len(newLines) <= len(oldLines)) && (len(newLines) > i && len(oldLines) > i) && (newLines[i] == oldLines[i]) { skipLines[i] = struct{}{} } else if _, exists := r.ignoreLines[i]; !exists { - clearLine(out) + out.ClearLine() } - cursorUp(out) + out.CursorUp(1) } if _, exists := r.ignoreLines[0]; !exists { @@ -161,8 +184,8 @@ func (r *standardRenderer) flush() { // standard (whereas others are proprietary to, say, VT100/VT52). // If cursor previous line (ESC[ + + F) were better supported // we could use that above to eliminate this step. - cursorBack(out, r.width) - clearLine(out) + out.CursorBack(r.width) + out.ClearLine() } } @@ -179,7 +202,7 @@ func (r *standardRenderer) flush() { if _, skip := skipLines[i]; skip { // Unless this is the last line, move the cursor down. if i < len(newLines)-1 { - cursorDown(out) + out.CursorDown(1) } } else { line := newLines[i] @@ -195,10 +218,10 @@ func (r *standardRenderer) flush() { line = truncate.String(line, uint(r.width)) } - _, _ = io.WriteString(out, line) + _, _ = out.WriteString(line) if i < len(newLines)-1 { - _, _ = io.WriteString(out, "\r\n") + _, _ = out.WriteString("\r\n") } } } @@ -210,12 +233,12 @@ func (r *standardRenderer) flush() { // This case fixes a bug in macOS terminal. In other terminals the // other case seems to do the job regardless of whether or not we're // using the full terminal window. - moveCursor(out, r.linesRendered, 0) + out.MoveCursor(r.linesRendered, 0) } else { - cursorBack(out, r.width) + out.CursorBack(r.width) } - _, _ = r.out.Write(out.Bytes()) + _, _ = r.out.Write(buf.Bytes()) r.lastRender = r.buf.String() r.buf.Reset() } @@ -242,15 +265,122 @@ func (r *standardRenderer) repaint() { r.lastRender = "" } +func (r *standardRenderer) clearScreen() { + r.mtx.Lock() + defer r.mtx.Unlock() + + r.out.ClearScreen() + r.out.MoveCursor(1, 1) + + r.repaint() +} + func (r *standardRenderer) altScreen() bool { + r.mtx.Lock() + defer r.mtx.Unlock() + return r.altScreenActive } -func (r *standardRenderer) setAltScreen(v bool) { - r.altScreenActive = v +func (r *standardRenderer) enterAltScreen() { + r.mtx.Lock() + defer r.mtx.Unlock() + + if r.altScreenActive { + return + } + + r.altScreenActive = true + r.out.AltScreen() + + // Ensure that the terminal is cleared, even when it doesn't support + // alt screen (or alt screen support is disabled, like GNU screen by + // default). + // + // Note: we can't use r.clearScreen() here because the mutex is already + // locked. + r.out.ClearScreen() + r.out.MoveCursor(1, 1) + + // cmd.exe and other terminals keep separate cursor states for the AltScreen + // and the main buffer. We have to explicitly reset the cursor visibility + // whenever we enter AltScreen. + if r.cursorHidden { + r.out.HideCursor() + } else { + r.out.ShowCursor() + } + r.repaint() } +func (r *standardRenderer) exitAltScreen() { + r.mtx.Lock() + defer r.mtx.Unlock() + + if !r.altScreenActive { + return + } + + r.altScreenActive = false + r.out.ExitAltScreen() + + // cmd.exe and other terminals keep separate cursor states for the AltScreen + // and the main buffer. We have to explicitly reset the cursor visibility + // whenever we exit AltScreen. + if r.cursorHidden { + r.out.HideCursor() + } else { + r.out.ShowCursor() + } + + r.repaint() +} + +func (r *standardRenderer) showCursor() { + r.mtx.Lock() + defer r.mtx.Unlock() + + r.cursorHidden = false + r.out.ShowCursor() +} + +func (r *standardRenderer) hideCursor() { + r.mtx.Lock() + defer r.mtx.Unlock() + + r.cursorHidden = true + r.out.HideCursor() +} + +func (r *standardRenderer) enableMouseCellMotion() { + r.mtx.Lock() + defer r.mtx.Unlock() + + r.out.EnableMouseCellMotion() +} + +func (r *standardRenderer) disableMouseCellMotion() { + r.mtx.Lock() + defer r.mtx.Unlock() + + r.out.DisableMouseCellMotion() +} + +func (r *standardRenderer) enableMouseAllMotion() { + r.mtx.Lock() + defer r.mtx.Unlock() + + r.out.EnableMouseAllMotion() +} + +func (r *standardRenderer) disableMouseAllMotion() { + r.mtx.Lock() + defer r.mtx.Unlock() + + r.out.DisableMouseAllMotion() +} + // setIgnoredLines specifies lines not to be touched by the standard Bubble Tea // renderer. func (r *standardRenderer) setIgnoredLines(from int, to int) { @@ -270,15 +400,17 @@ func (r *standardRenderer) setIgnoredLines(from int, to int) { // Erase ignored lines if r.linesRendered > 0 { - out := new(bytes.Buffer) + buf := &bytes.Buffer{} + out := termenv.NewOutput(buf) + for i := r.linesRendered - 1; i >= 0; i-- { if _, exists := r.ignoreLines[i]; exists { - clearLine(out) + out.ClearLine() } - cursorUp(out) + out.CursorUp(1) } - moveCursor(out, r.linesRendered, 0) // put cursor back - _, _ = r.out.Write(out.Bytes()) + out.MoveCursor(r.linesRendered, 0) // put cursor back + _, _ = r.out.Write(buf.Bytes()) } } @@ -311,18 +443,19 @@ func (r *standardRenderer) insertTop(lines []string, topBoundary, bottomBoundary r.mtx.Lock() defer r.mtx.Unlock() - b := new(bytes.Buffer) + buf := &bytes.Buffer{} + out := termenv.NewOutput(buf) - changeScrollingRegion(b, topBoundary, bottomBoundary) - moveCursor(b, topBoundary, 0) - insertLine(b, len(lines)) - _, _ = io.WriteString(b, strings.Join(lines, "\r\n")) - changeScrollingRegion(b, 0, r.height) + out.ChangeScrollingRegion(topBoundary, bottomBoundary) + out.MoveCursor(topBoundary, 0) + out.InsertLines(len(lines)) + _, _ = out.WriteString(strings.Join(lines, "\r\n")) + out.ChangeScrollingRegion(0, r.height) // Move cursor back to where the main rendering routine expects it to be - moveCursor(b, r.linesRendered, 0) + out.MoveCursor(r.linesRendered, 0) - _, _ = r.out.Write(b.Bytes()) + _, _ = r.out.Write(buf.Bytes()) } // insertBottom effectively scrolls down. It inserts lines at the bottom of @@ -338,17 +471,18 @@ func (r *standardRenderer) insertBottom(lines []string, topBoundary, bottomBound r.mtx.Lock() defer r.mtx.Unlock() - b := new(bytes.Buffer) + buf := &bytes.Buffer{} + out := termenv.NewOutput(buf) - changeScrollingRegion(b, topBoundary, bottomBoundary) - moveCursor(b, bottomBoundary, 0) - _, _ = io.WriteString(b, "\r\n"+strings.Join(lines, "\r\n")) - changeScrollingRegion(b, 0, r.height) + out.ChangeScrollingRegion(topBoundary, bottomBoundary) + out.MoveCursor(bottomBoundary, 0) + _, _ = out.WriteString("\r\n" + strings.Join(lines, "\r\n")) + out.ChangeScrollingRegion(0, r.height) // Move cursor back to where the main rendering routine expects it to be - moveCursor(b, r.linesRendered, 0) + out.MoveCursor(r.linesRendered, 0) - _, _ = r.out.Write(b.Bytes()) + _, _ = r.out.Write(buf.Bytes()) } // handleMessages handles internal messages for the renderer. @@ -365,6 +499,7 @@ func (r *standardRenderer) handleMessages(msg Msg) { r.mtx.Lock() r.width = msg.Width r.height = msg.Height + r.repaint() r.mtx.Unlock() case clearScrollAreaMsg: diff --git a/vendor/github.com/charmbracelet/bubbletea/tea.go b/vendor/github.com/charmbracelet/bubbletea/tea.go index 28065a00..03734bc5 100644 --- a/vendor/github.com/charmbracelet/bubbletea/tea.go +++ b/vendor/github.com/charmbracelet/bubbletea/tea.go @@ -11,6 +11,7 @@ package tea import ( "context" + "errors" "fmt" "io" "os" @@ -18,15 +19,16 @@ import ( "runtime/debug" "sync" "syscall" - "time" "github.com/containerd/console" isatty "github.com/mattn/go-isatty" "github.com/muesli/cancelreader" - te "github.com/muesli/termenv" - "golang.org/x/term" + "github.com/muesli/termenv" ) +// ErrProgramKilled is returned by [Program.Run] when the program got killed. +var ErrProgramKilled = errors.New("program was killed") + // Msg contain data from the result of a IO operation. Msgs trigger the update // function and, henceforth, the UI. type Msg interface{} @@ -55,6 +57,8 @@ type Model interface { // update function. type Cmd func() Msg +type handlers []chan struct{} + // Options to customize the program during its initialization. These are // generally set with ProgramOptions. // @@ -72,6 +76,13 @@ const ( withInputTTY withCustomInput withANSICompressor + withoutSignalHandler + + // Catching panics is incredibly useful for restoring the terminal to a + // usable state after a panic occurs. When this is set, Bubble Tea will + // recover from panics, print the stack trace, and disable raw mode. This + // feature is on by default. + withoutCatchPanics ) // Program is a terminal user interface. @@ -82,32 +93,26 @@ type Program struct { // treated as bits. These options can be set via various ProgramOptions. startupOptions startupOptions - ctx context.Context - mtx *sync.Mutex - - msgs chan Msg - errs chan error - readLoopDone chan struct{} - - output io.Writer // where to send output. this will usually be os.Stdout. - input io.Reader // this will usually be os.Stdin. - cancelReader cancelreader.CancelReader - - renderer renderer - altScreenActive bool - altScreenWasActive bool // was the altscreen active before releasing the terminal? + ctx context.Context + cancel context.CancelFunc - // CatchPanics is incredibly useful for restoring the terminal to a usable - // state after a panic occurs. When this is set, Bubble Tea will recover - // from panics, print the stack trace, and disable raw mode. This feature - // is on by default. - CatchPanics bool + msgs chan Msg + errs chan error - ignoreSignals bool + // where to send output, this will usually be os.Stdout. + output *termenv.Output + restoreOutput func() error + renderer renderer - killc chan bool + // where to read inputs from, this will usually be os.Stdin. + input io.Reader + cancelReader cancelreader.CancelReader + readLoopDone chan struct{} + console console.Console - console console.Console + // was the altscreen active before releasing the terminal? + altScreenWasActive bool + ignoreSignals bool // Stores the original reference to stdin for cases where input is not a // TTY on windows and we've automatically opened CONIN$ to receive input. @@ -119,34 +124,6 @@ type Program struct { windowsStdin *os.File //nolint:golint,structcheck,unused } -// Batch performs a bunch of commands concurrently with no ordering guarantees -// about the results. Use a Batch to return several commands. -// -// Example: -// -// func (m model) Init() Cmd { -// return tea.Batch(someCommand, someOtherCommand) -// } -func Batch(cmds ...Cmd) Cmd { - var validCmds []Cmd - for _, c := range cmds { - if c == nil { - continue - } - validCmds = append(validCmds, c) - } - if len(validCmds) == 0 { - return nil - } - return func() Msg { - return batchMsg(validCmds) - } -} - -// batchMsg is the internal message used to perform a bunch of commands. You -// can send a batchMsg with Batch. -type batchMsg []Cmd - // Quit is a special command that tells the Bubble Tea program to exit. func Quit() Msg { return quitMsg{} @@ -156,150 +133,210 @@ func Quit() Msg { // send a quitMsg with Quit. type quitMsg struct{} -// EnterAltScreen is a special command that tells the Bubble Tea program to -// enter the alternate screen buffer. -// -// Because commands run asynchronously, this command should not be used in your -// model's Init function. To initialize your program with the altscreen enabled -// use the WithAltScreen ProgramOption instead. -func EnterAltScreen() Msg { - return enterAltScreenMsg{} -} +// NewProgram creates a new Program. +func NewProgram(model Model, opts ...ProgramOption) *Program { + p := &Program{ + initialModel: model, + input: os.Stdin, + msgs: make(chan Msg), + } -// enterAltScreenMsg in an internal message signals that the program should -// enter alternate screen buffer. You can send a enterAltScreenMsg with -// EnterAltScreen. -type enterAltScreenMsg struct{} + // Apply all options to the program. + for _, opt := range opts { + opt(p) + } -// ExitAltScreen is a special command that tells the Bubble Tea program to exit -// the alternate screen buffer. This command should be used to exit the -// alternate screen buffer while the program is running. -// -// Note that the alternate screen buffer will be automatically exited when the -// program quits. -func ExitAltScreen() Msg { - return exitAltScreenMsg{} -} + // A context can be provided with a ProgramOption, but if none was provided + // we'll use the default background context. + if p.ctx == nil { + p.ctx = context.Background() + } + // Initialize context and teardown channel. + p.ctx, p.cancel = context.WithCancel(p.ctx) -// exitAltScreenMsg in an internal message signals that the program should exit -// alternate screen buffer. You can send a exitAltScreenMsg with ExitAltScreen. -type exitAltScreenMsg struct{} + // if no output was set, set it to stdout + if p.output == nil { + p.output = termenv.DefaultOutput() -// EnableMouseCellMotion is a special command that enables mouse click, -// release, and wheel events. Mouse movement events are also captured if -// a mouse button is pressed (i.e., drag events). -// -// Because commands run asynchronously, this command should not be used in your -// model's Init function. Use the WithMouseCellMotion ProgramOption instead. -func EnableMouseCellMotion() Msg { - return enableMouseCellMotionMsg{} -} + // cache detected color values + termenv.WithColorCache(true)(p.output) + } -// enableMouseCellMotionMsg is a special command that signals to start -// listening for "cell motion" type mouse events (ESC[?1002l). To send an -// enableMouseCellMotionMsg, use the EnableMouseCellMotion command. -type enableMouseCellMotionMsg struct{} + p.restoreOutput, _ = termenv.EnableVirtualTerminalProcessing(p.output) -// EnableMouseAllMotion is a special command that enables mouse click, release, -// wheel, and motion events, which are delivered regardless of whether a mouse -// button is pressed, effectively enabling support for hover interactions. -// -// Many modern terminals support this, but not all. If in doubt, use -// EnableMouseCellMotion instead. -// -// Because commands run asynchronously, this command should not be used in your -// model's Init function. Use the WithMouseAllMotion ProgramOption instead. -func EnableMouseAllMotion() Msg { - return enableMouseAllMotionMsg{} + return p } -// enableMouseAllMotionMsg is a special command that signals to start listening -// for "all motion" type mouse events (ESC[?1003l). To send an -// enableMouseAllMotionMsg, use the EnableMouseAllMotion command. -type enableMouseAllMotionMsg struct{} +func (p *Program) handleSignals() chan struct{} { + ch := make(chan struct{}) -// DisableMouse is a special command that stops listening for mouse events. -func DisableMouse() Msg { - return disableMouseMsg{} -} + // Listen for SIGINT and SIGTERM. + // + // In most cases ^C will not send an interrupt because the terminal will be + // in raw mode and ^C will be captured as a keystroke and sent along to + // Program.Update as a KeyMsg. When input is not a TTY, however, ^C will be + // caught here. + // + // SIGTERM is sent by unix utilities (like kill) to terminate a process. + go func() { + sig := make(chan os.Signal, 1) + signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) + defer func() { + signal.Stop(sig) + close(ch) + }() -// disableMouseMsg is an internal message that that signals to stop listening -// for mouse events. To send a disableMouseMsg, use the DisableMouse command. -type disableMouseMsg struct{} - -// WindowSizeMsg is used to report the terminal size. It's sent to Update once -// initially and then on every terminal resize. Note that Windows does not -// have support for reporting when resizes occur as it does not support the -// SIGWINCH signal. -type WindowSizeMsg struct { - Width int - Height int -} + for { + select { + case <-p.ctx.Done(): + return -// HideCursor is a special command for manually instructing Bubble Tea to hide -// the cursor. In some rare cases, certain operations will cause the terminal -// to show the cursor, which is normally hidden for the duration of a Bubble -// Tea program's lifetime. You will most likely not need to use this command. -func HideCursor() Msg { - return hideCursorMsg{} + case <-sig: + if !p.ignoreSignals { + p.msgs <- quitMsg{} + return + } + } + } + }() + + return ch } -// hideCursorMsg is an internal command used to hide the cursor. You can send -// this message with HideCursor. -type hideCursorMsg struct{} +// handleResize handles terminal resize events. +func (p *Program) handleResize() chan struct{} { + ch := make(chan struct{}) -// NewProgram creates a new Program. -func NewProgram(model Model, opts ...ProgramOption) *Program { - p := &Program{ - mtx: &sync.Mutex{}, - initialModel: model, - output: os.Stdout, - input: os.Stdin, - msgs: make(chan Msg), - CatchPanics: true, - killc: make(chan bool, 1), - } + if f, ok := p.output.TTY().(*os.File); ok && isatty.IsTerminal(f.Fd()) { + // Get the initial terminal size and send it to the program. + go p.checkResize() - // Apply all options to the program. - for _, opt := range opts { - opt(p) + // Listen for window resizes. + go p.listenForResize(ch) + } else { + close(ch) } - return p + return ch } -// StartReturningModel initializes the program. Returns the final model. -func (p *Program) StartReturningModel() (Model, error) { - cmds := make(chan Cmd) - p.errs = make(chan error) +// handleCommands runs commands in a goroutine and sends the result to the +// program's message channel. +func (p *Program) handleCommands(cmds chan Cmd) chan struct{} { + ch := make(chan struct{}) + + go func() { + defer close(ch) + + for { + select { + case <-p.ctx.Done(): + return + + case cmd := <-cmds: + if cmd == nil { + continue + } + + // Don't wait on these goroutines, otherwise the shutdown + // latency would get too large as a Cmd can run for some time + // (e.g. tick commands that sleep for half a second). It's not + // possible to cancel them so we'll have to leak the goroutine + // until Cmd returns. + go func() { + msg := cmd() // this can be long. + p.Send(msg) + }() + } + } + }() + + return ch +} + +// eventLoop is the central message loop. It receives and handles the default +// Bubble Tea messages, update the model and triggers redraws. +func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) { + for { + select { + case <-p.ctx.Done(): + return model, nil + + case err := <-p.errs: + return model, err + + case msg := <-p.msgs: + // Handle special internal messages. + switch msg := msg.(type) { + case quitMsg: + return model, nil + + case clearScreenMsg: + p.renderer.clearScreen() - // Channels for managing goroutine lifecycles. - var ( - sigintLoopDone = make(chan struct{}) - cmdLoopDone = make(chan struct{}) - resizeLoopDone = make(chan struct{}) - initSignalDone = make(chan struct{}) - - waitForGoroutines = func(withReadLoop bool) { - if withReadLoop { - select { - case <-p.readLoopDone: - case <-time.After(500 * time.Millisecond): - // The read loop hangs, which means the input - // cancelReader's cancel function has returned true even - // though it was not able to cancel the read. + case enterAltScreenMsg: + p.renderer.enterAltScreen() + + case exitAltScreenMsg: + p.renderer.exitAltScreen() + + case enableMouseCellMotionMsg: + p.renderer.enableMouseCellMotion() + + case enableMouseAllMotionMsg: + p.renderer.enableMouseAllMotion() + + case disableMouseMsg: + p.renderer.disableMouseCellMotion() + p.renderer.disableMouseAllMotion() + + case showCursorMsg: + p.renderer.showCursor() + + case hideCursorMsg: + p.renderer.hideCursor() + + case execMsg: + // NB: this blocks. + p.exec(msg.cmd, msg.fn) + + case BatchMsg: + for _, cmd := range msg { + cmds <- cmd } + continue + + case sequenceMsg: + go func() { + // Execute commands one at a time, in order. + for _, cmd := range msg { + p.Send(cmd()) + } + }() + } + + // Process internal messages for the renderer. + if r, ok := p.renderer.(*standardRenderer); ok { + r.handleMessages(msg) } - <-cmdLoopDone - <-resizeLoopDone - <-sigintLoopDone - <-initSignalDone + + var cmd Cmd + model, cmd = model.Update(msg) // run update + cmds <- cmd // process command (if any) + p.renderer.write(model.View()) // send view to renderer } - ) + } +} + +// Run initializes the program and runs its event loops, blocking until it gets +// terminated by either [Program.Quit], [Program.Kill], or its signal handler. +// Returns the final model. +func (p *Program) Run() (Model, error) { + handlers := handlers{} + cmds := make(chan Cmd) + p.errs = make(chan error) - var cancelContext context.CancelFunc - p.ctx, cancelContext = context.WithCancel(context.Background()) - defer cancelContext() + defer p.cancel() switch { case p.startupOptions.has(withInputTTY): @@ -308,9 +345,7 @@ func (p *Program) StartReturningModel() (Model, error) { if err != nil { return p.initialModel, err } - - defer f.Close() // nolint:errcheck - + defer f.Close() //nolint:errcheck p.input = f case !p.startupOptions.has(withCustomInput): @@ -322,7 +357,6 @@ func (p *Program) StartReturningModel() (Model, error) { if !isFile { break } - if isatty.IsTerminal(f.Fd()) { break } @@ -331,38 +365,17 @@ func (p *Program) StartReturningModel() (Model, error) { if err != nil { return p.initialModel, err } - - defer f.Close() // nolint:errcheck - + defer f.Close() //nolint:errcheck p.input = f } - // Listen for SIGINT. Note that in most cases ^C will not send an - // interrupt because the terminal will be in raw mode and thus capture - // that keystroke and send it along to Program.Update. If input is not a - // TTY, however, ^C will be caught here. - go func() { - sig := make(chan os.Signal, 1) - signal.Notify(sig, syscall.SIGINT) - defer func() { - signal.Stop(sig) - close(sigintLoopDone) - }() - - for { - select { - case <-p.ctx.Done(): - return - case <-sig: - if !p.ignoreSignals { - p.msgs <- quitMsg{} - return - } - } - } - }() + // Handle signals. + if !p.startupOptions.has(withoutSignalHandler) { + handlers.add(p.handleSignals()) + } - if p.CatchPanics { + // Recover from panics. + if !p.startupOptions.has(withoutCatchPanics) { defer func() { if r := recover(); r != nil { p.shutdown(true) @@ -373,44 +386,45 @@ func (p *Program) StartReturningModel() (Model, error) { }() } + // If no renderer is set use the standard one. + if p.renderer == nil { + p.renderer = newRenderer(p.output, p.startupOptions.has(withANSICompressor)) + } + // Check if output is a TTY before entering raw mode, hiding the cursor and // so on. if err := p.initTerminal(); err != nil { return p.initialModel, err } - // If no renderer is set use the standard one. - if p.renderer == nil { - p.renderer = newRenderer(p.output, p.mtx, p.startupOptions.has(withANSICompressor)) - } - // Honor program startup options. if p.startupOptions&withAltScreen != 0 { - p.EnterAltScreen() + p.renderer.enterAltScreen() } if p.startupOptions&withMouseCellMotion != 0 { - p.EnableMouseCellMotion() + p.renderer.enableMouseCellMotion() } else if p.startupOptions&withMouseAllMotion != 0 { - p.EnableMouseAllMotion() + p.renderer.enableMouseAllMotion() } // Initialize the program. model := p.initialModel if initCmd := model.Init(); initCmd != nil { + ch := make(chan struct{}) + handlers.add(ch) + go func() { - defer close(initSignalDone) + defer close(ch) + select { case cmds <- initCmd: case <-p.ctx.Done(): } }() - } else { - close(initSignalDone) } // Start the renderer. p.renderer.start() - p.renderer.setAltScreen(p.altScreenActive) // Render the initial view. p.renderer.write(model.View()) @@ -420,132 +434,58 @@ func (p *Program) StartReturningModel() (Model, error) { if err := p.initCancelReader(); err != nil { return model, err } - } else { - defer close(p.readLoopDone) } - defer p.cancelReader.Close() // nolint:errcheck - if f, ok := p.output.(*os.File); ok && isatty.IsTerminal(f.Fd()) { - // Get the initial terminal size and send it to the program. - go func() { - w, h, err := term.GetSize(int(f.Fd())) - if err != nil { - p.errs <- err - } + // Handle resize events. + handlers.add(p.handleResize()) - select { - case <-p.ctx.Done(): - case p.msgs <- WindowSizeMsg{w, h}: - } - }() + // Process commands. + handlers.add(p.handleCommands(cmds)) - // Listen for window resizes. - go listenForResize(p.ctx, f, p.msgs, p.errs, resizeLoopDone) + // Run event loop, handle updates and draw. + model, err := p.eventLoop(model, cmds) + killed := p.ctx.Err() != nil + if killed { + err = ErrProgramKilled } else { - close(resizeLoopDone) + // Ensure we rendered the final state of the model. + p.renderer.write(model.View()) } - // Process commands. - go func() { - defer close(cmdLoopDone) - - for { - select { - case <-p.ctx.Done(): - - return - case cmd := <-cmds: - if cmd == nil { - continue - } - - // Don't wait on these goroutines, otherwise the shutdown - // latency would get too large as a Cmd can run for some time - // (e.g. tick commands that sleep for half a second). It's not - // possible to cancel them so we'll have to leak the goroutine - // until Cmd returns. - go func() { - select { - case p.msgs <- cmd(): - case <-p.ctx.Done(): - } - }() - } - } - }() - - // Handle updates and draw. - for { - select { - case <-p.killc: - return nil, nil - case err := <-p.errs: - cancelContext() - waitForGoroutines(p.cancelReader.Cancel()) - p.shutdown(false) - return model, err - - case msg := <-p.msgs: - - // Handle special internal messages. - switch msg := msg.(type) { - case quitMsg: - cancelContext() - waitForGoroutines(p.cancelReader.Cancel()) - p.shutdown(false) - return model, nil - - case batchMsg: - for _, cmd := range msg { - cmds <- cmd - } - continue - - case WindowSizeMsg: - p.mtx.Lock() - p.renderer.repaint() - p.mtx.Unlock() - - case enterAltScreenMsg: - p.EnterAltScreen() - - case exitAltScreenMsg: - p.ExitAltScreen() - - case enableMouseCellMotionMsg: - p.EnableMouseCellMotion() + // Tear down. + p.cancel() - case enableMouseAllMotionMsg: - p.EnableMouseAllMotion() + // Wait for input loop to finish. + if p.cancelReader.Cancel() { + p.waitForReadLoop() + } + _ = p.cancelReader.Close() - case disableMouseMsg: - p.DisableMouseCellMotion() - p.DisableMouseAllMotion() + // Wait for all handlers to finish. + handlers.shutdown() - case hideCursorMsg: - hideCursor(p.output) + // Restore terminal state. + p.shutdown(killed) - case execMsg: - // NB: this blocks. - p.exec(msg.cmd, msg.fn) - } - - // Process internal messages for the renderer. - if r, ok := p.renderer.(*standardRenderer); ok { - r.handleMessages(msg) - } + return model, err +} - var cmd Cmd - model, cmd = model.Update(msg) // run update - cmds <- cmd // process command (if any) - p.renderer.write(model.View()) // send view to renderer - } - } +// StartReturningModel initializes the program and runs its event loops, +// blocking until it gets terminated by either [Program.Quit], [Program.Kill], +// or its signal handler. Returns the final model. +// +// Deprecated: please use [Program.Run] instead. +func (p *Program) StartReturningModel() (Model, error) { + return p.Run() } -// Start initializes the program. Ignores the final model. +// Start initializes the program and runs its event loops, blocking until it +// gets terminated by either [Program.Quit], [Program.Kill], or its signal +// handler. +// +// Deprecated: please use [Program.Run] instead. func (p *Program) Start() error { - _, err := p.StartReturningModel() + _, err := p.Run() return err } @@ -553,10 +493,14 @@ func (p *Program) Start() error { // messages to be injected from outside the program for interoperability // purposes. // -// If the program is not running this this will be a no-op, so it's safe to -// send messages if the program is unstarted, or has exited. +// If the program hasn't started yet this will be a blocking operation. +// If the program has already been terminated this will be a no-op, so it's safe +// to send messages after the program has exited. func (p *Program) Send(msg Msg) { - p.msgs <- msg + select { + case <-p.ctx.Done(): + case p.msgs <- msg: + } } // Quit is a convenience function for quitting Bubble Tea programs. Use it @@ -572,9 +516,9 @@ func (p *Program) Quit() { // Kill stops the program immediately and restores the former terminal state. // The final render that you would normally see when quitting will be skipped. +// [program.Run] returns a [ErrProgramKilled] error. func (p *Program) Kill() { - p.killc <- true - p.shutdown(true) + p.cancel() } // shutdown performs operations to free up resources and restore the terminal @@ -587,102 +531,21 @@ func (p *Program) shutdown(kill bool) { p.renderer.stop() } } - p.ExitAltScreen() - p.DisableMouseCellMotion() - p.DisableMouseAllMotion() - _ = p.restoreTerminalState() -} - -// EnterAltScreen enters the alternate screen buffer, which consumes the entire -// terminal window. ExitAltScreen will return the terminal to its former state. -// -// Deprecated: Use the WithAltScreen ProgramOption instead. -func (p *Program) EnterAltScreen() { - p.mtx.Lock() - defer p.mtx.Unlock() - - if p.altScreenActive { - return - } - - enterAltScreen(p.output) - - p.altScreenActive = true - if p.renderer != nil { - p.renderer.setAltScreen(p.altScreenActive) - } -} - -// ExitAltScreen exits the alternate screen buffer. -// -// Deprecated: The altscreen will exited automatically when the program exits. -func (p *Program) ExitAltScreen() { - p.mtx.Lock() - defer p.mtx.Unlock() - - if !p.altScreenActive { - return - } - exitAltScreen(p.output) - - p.altScreenActive = false - if p.renderer != nil { - p.renderer.setAltScreen(p.altScreenActive) + _ = p.restoreTerminalState() + if p.restoreOutput != nil { + _ = p.restoreOutput() } } -// EnableMouseCellMotion enables mouse click, release, wheel and motion events -// if a mouse button is pressed (i.e., drag events). -// -// Deprecated: Use the WithMouseCellMotion ProgramOption instead. -func (p *Program) EnableMouseCellMotion() { - p.mtx.Lock() - defer p.mtx.Unlock() - fmt.Fprintf(p.output, te.CSI+te.EnableMouseCellMotionSeq) -} - -// DisableMouseCellMotion disables Mouse Cell Motion tracking. This will be -// called automatically when exiting a Bubble Tea program. -// -// Deprecated: The mouse will automatically be disabled when the program exits. -func (p *Program) DisableMouseCellMotion() { - p.mtx.Lock() - defer p.mtx.Unlock() - fmt.Fprintf(p.output, te.CSI+te.DisableMouseCellMotionSeq) -} - -// EnableMouseAllMotion enables mouse click, release, wheel and motion events, -// regardless of whether a mouse button is pressed. Many modern terminals -// support this, but not all. -// -// Deprecated: Use the WithMouseAllMotion ProgramOption instead. -func (p *Program) EnableMouseAllMotion() { - p.mtx.Lock() - defer p.mtx.Unlock() - fmt.Fprintf(p.output, te.CSI+te.EnableMouseAllMotionSeq) -} - -// DisableMouseAllMotion disables All Motion mouse tracking. This will be -// called automatically when exiting a Bubble Tea program. -// -// Deprecated: The mouse will automatically be disabled when the program exits. -func (p *Program) DisableMouseAllMotion() { - p.mtx.Lock() - defer p.mtx.Unlock() - fmt.Fprintf(p.output, te.CSI+te.DisableMouseAllMotionSeq) -} - // ReleaseTerminal restores the original terminal state and cancels the input // reader. You can return control to the Program with RestoreTerminal. func (p *Program) ReleaseTerminal() error { p.ignoreSignals = true - p.cancelInput() - p.altScreenWasActive = p.altScreenActive - if p.altScreenActive { - p.ExitAltScreen() - time.Sleep(time.Millisecond * 10) // give the terminal a moment to catch up - } + p.cancelReader.Cancel() + p.waitForReadLoop() + + p.altScreenWasActive = p.renderer.altScreen() return p.restoreTerminalState() } @@ -695,16 +558,22 @@ func (p *Program) RestoreTerminal() error { if err := p.initTerminal(); err != nil { return err } - if err := p.initCancelReader(); err != nil { return err } if p.altScreenWasActive { - p.EnterAltScreen() + p.renderer.enterAltScreen() + } else { + // entering alt screen already causes a repaint. + go p.Send(repaintMsg{}) } - go p.Send(repaintMsg{}) + // If the output is a terminal, it may have been resized while another + // process was at the foreground, in which case we may not have received + // SIGWINCH. Detect any size change now and propagate the new size as + // needed. + go p.checkResize() return nil } @@ -732,3 +601,22 @@ func (p *Program) Printf(template string, args ...interface{}) { messageBody: fmt.Sprintf(template, args...), } } + +// Adds a handler to the list of handlers. We wait for all handlers to terminate +// gracefully on shutdown. +func (h *handlers) add(ch chan struct{}) { + *h = append(*h, ch) +} + +// Shutdown waits for all handlers to terminate. +func (h handlers) shutdown() { + var wg sync.WaitGroup + for _, ch := range h { + wg.Add(1) + go func(ch chan struct{}) { + <-ch + wg.Done() + }(ch) + } + wg.Wait() +} diff --git a/vendor/github.com/charmbracelet/bubbletea/tty.go b/vendor/github.com/charmbracelet/bubbletea/tty.go index 17e508b9..3ab6639b 100644 --- a/vendor/github.com/charmbracelet/bubbletea/tty.go +++ b/vendor/github.com/charmbracelet/bubbletea/tty.go @@ -3,8 +3,12 @@ package tea import ( "errors" "io" + "os" + "time" + isatty "github.com/mattn/go-isatty" "github.com/muesli/cancelreader" + "golang.org/x/term" ) func (p *Program) initTerminal() error { @@ -20,14 +24,25 @@ func (p *Program) initTerminal() error { } } - hideCursor(p.output) + p.renderer.hideCursor() return nil } // restoreTerminalState restores the terminal to the state prior to running the // Bubble Tea program. -func (p Program) restoreTerminalState() error { - showCursor(p.output) +func (p *Program) restoreTerminalState() error { + if p.renderer != nil { + p.renderer.showCursor() + p.renderer.disableMouseCellMotion() + p.renderer.disableMouseAllMotion() + + if p.renderer.altScreen() { + p.renderer.exitAltScreen() + + // give the terminal a moment to catch up + time.Sleep(time.Millisecond * 10) + } + } if p.console != nil { err := p.console.Reset() @@ -48,33 +63,69 @@ func (p *Program) initCancelReader() error { } p.readLoopDone = make(chan struct{}) - go func() { - defer close(p.readLoopDone) + go p.readLoop() - for { - if p.ctx.Err() != nil { - return - } + return nil +} - msgs, err := readInputs(p.cancelReader) - if err != nil { - if !errors.Is(err, io.EOF) && !errors.Is(err, cancelreader.ErrCanceled) { - p.errs <- err - } +func (p *Program) readLoop() { + defer close(p.readLoopDone) - return - } + for { + if p.ctx.Err() != nil { + return + } - for _, msg := range msgs { - p.msgs <- msg + msgs, err := readInputs(p.cancelReader) + if err != nil { + if !errors.Is(err, io.EOF) && !errors.Is(err, cancelreader.ErrCanceled) { + select { + case <-p.ctx.Done(): + case p.errs <- err: + } } + + return } - }() - return nil + for _, msg := range msgs { + p.msgs <- msg + } + } } -// cancelInput cancels the input reader. -func (p *Program) cancelInput() { - p.cancelReader.Cancel() +// waitForReadLoop waits for the cancelReader to finish its read loop. +func (p *Program) waitForReadLoop() { + select { + case <-p.readLoopDone: + case <-time.After(500 * time.Millisecond): + // The read loop hangs, which means the input + // cancelReader's cancel function has returned true even + // though it was not able to cancel the read. + } +} + +// checkResize detects the current size of the output and informs the program +// via a WindowSizeMsg. +func (p *Program) checkResize() { + f, ok := p.output.TTY().(*os.File) + if !ok || !isatty.IsTerminal(f.Fd()) { + // can't query window size + return + } + + w, h, err := term.GetSize(int(f.Fd())) + if err != nil { + select { + case <-p.ctx.Done(): + case p.errs <- err: + } + + return + } + + p.Send(WindowSizeMsg{ + Width: w, + Height: h, + }) } diff --git a/vendor/github.com/charmbracelet/bubbletea/tty_windows.go b/vendor/github.com/charmbracelet/bubbletea/tty_windows.go index 9cfc7d9c..be415aef 100644 --- a/vendor/github.com/charmbracelet/bubbletea/tty_windows.go +++ b/vendor/github.com/charmbracelet/bubbletea/tty_windows.go @@ -4,11 +4,9 @@ package tea import ( - "io" "os" "github.com/containerd/console" - "golang.org/x/sys/windows" ) func (p *Program) initInput() error { @@ -26,8 +24,6 @@ func (p *Program) initInput() error { p.console = c } - enableAnsiColors(p.output) - return nil } @@ -49,18 +45,3 @@ func openInputTTY() (*os.File, error) { } return f, nil } - -// enableAnsiColors enables support for ANSI color sequences in Windows -// default console. Note that this only works with Windows 10. -func enableAnsiColors(w io.Writer) { - f, ok := w.(*os.File) - if !ok { - return - } - - stdout := windows.Handle(f.Fd()) - var originalMode uint32 - - _ = windows.GetConsoleMode(stdout, &originalMode) - _ = windows.SetConsoleMode(stdout, originalMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING) -} diff --git a/vendor/github.com/go-rod/rod/.golangci.yml b/vendor/github.com/go-rod/rod/.golangci.yml index e54b0f94..8bff6df2 100644 --- a/vendor/github.com/go-rod/rod/.golangci.yml +++ b/vendor/github.com/go-rod/rod/.golangci.yml @@ -6,7 +6,6 @@ linters: - gofmt - revive - gocyclo - - misspell - bodyclose gocyclo: diff --git a/vendor/github.com/go-rod/rod/README.md b/vendor/github.com/go-rod/rod/README.md index 827e878e..42590c9e 100644 --- a/vendor/github.com/go-rod/rod/README.md +++ b/vendor/github.com/go-rod/rod/README.md @@ -3,12 +3,14 @@ [![Go Reference](https://pkg.go.dev/badge/github.com/go-rod/rod.svg)](https://pkg.go.dev/github.com/go-rod/rod) [![Discord Chat](https://img.shields.io/discord/719933559456006165.svg)][discord room] -## [Documentation](https://go-rod.github.io/) | [API reference](https://pkg.go.dev/github.com/go-rod/rod?tab=doc) | [中文 API 参考文档](https://github.com/go-rod/go-rod-chinese) | [Management](https://github.com/orgs/go-rod/projects/1) | [FAQ](https://go-rod.github.io/#/faq/README) +## [Documentation](https://go-rod.github.io/) | [API reference](https://pkg.go.dev/github.com/go-rod/rod?tab=doc) | [FAQ](https://go-rod.github.io/#/faq/README) Rod is a high-level driver directly based on [DevTools Protocol](https://chromedevtools.github.io/devtools-protocol). It's designed for web automation and scraping for both high-level and low-level use, senior developers can use the low-level packages and functions to easily customize or build up their own version of Rod, the high-level functions are just examples to build a default version of Rod. +[中文 API 文档](https://pkg.go.dev/github.com/go-rod/go-rod-chinese) + ## Features - Chained context design, intuitive to timeout or cancel the long-running task @@ -42,7 +44,7 @@ Your help is more than welcome! Even just open an issue to ask a question may gr Please read [How To Ask Questions The Smart Way](http://www.catb.org/~esr/faqs/smart-questions.html) before you ask questions. -We use Github Projects to manage tasks, you can see the priority and progress of the issues [here](https://github.com/orgs/go-rod/projects/1). +We use Github Projects to manage tasks, you can see the priority and progress of the issues [here](https://github.com/go-rod/rod/projects). If you want to contribute please read the [Contributor Guide](.github/CONTRIBUTING.md). diff --git a/vendor/github.com/go-rod/rod/browser.go b/vendor/github.com/go-rod/rod/browser.go index 715406af..42770980 100644 --- a/vendor/github.com/go-rod/rod/browser.go +++ b/vendor/github.com/go-rod/rod/browser.go @@ -71,7 +71,7 @@ func New() *Browser { trace: defaults.Trace, monitor: defaults.Monitor, logger: DefaultLogger, - defaultDevice: devices.LaptopWithMDPIScreen.Landescape(), + defaultDevice: devices.LaptopWithMDPIScreen.Landscape(), targetsLock: &sync.Mutex{}, states: &sync.Map{}, }).WithPanic(utils.Panic) diff --git a/vendor/github.com/go-rod/rod/dev_helpers.go b/vendor/github.com/go-rod/rod/dev_helpers.go index 3532b6e4..e245388c 100644 --- a/vendor/github.com/go-rod/rod/dev_helpers.go +++ b/vendor/github.com/go-rod/rod/dev_helpers.go @@ -93,7 +93,7 @@ func (b *Browser) ServeMonitor(host string) string { } // check method and sleep if needed -func (b *Browser) trySlowmotion() { +func (b *Browser) trySlowMotion() { if b.slowMotion == 0 { return } @@ -176,7 +176,7 @@ func (p *Page) tryTraceReq(includes, excludes []string) func(map[proto.NetworkRe } go func() { - var waitlist map[string]string + var waitList map[string]string t := time.NewTicker(time.Second) for { select { @@ -184,9 +184,9 @@ func (p *Page) tryTraceReq(includes, excludes []string) func(map[proto.NetworkRe t.Stop() cleanup() return - case waitlist = <-ch: + case waitList = <-ch: case <-t.C: - p.browser.logger.Println(TraceTypeWaitRequests, p, waitlist) + p.browser.logger.Println(TraceTypeWaitRequests, p, waitList) } } }() diff --git a/vendor/github.com/go-rod/rod/element.go b/vendor/github.com/go-rod/rod/element.go index 83e5dee0..f6d6e684 100644 --- a/vendor/github.com/go-rod/rod/element.go +++ b/vendor/github.com/go-rod/rod/element.go @@ -66,7 +66,7 @@ func (el *Element) Focus() error { // window if it's not already within the visible area. func (el *Element) ScrollIntoView() error { defer el.tryTrace(TraceTypeInput, "scroll into view")() - el.page.browser.trySlowmotion() + el.page.browser.trySlowMotion() err := el.WaitStableRAF() if err != nil { @@ -190,9 +190,9 @@ func (el *Element) Interactable() (pt *proto.Point, err error) { return } -// Shape of the DOM element content. The shape is a group of 4-sides polygons (4-gons). -// A 4-gon is not necessary a rectangle. 4-gons can be apart from each other. -// For example, we use 2 4-gons to describe the shape below: +// Shape of the DOM element content. The shape is a group of 4-sides polygons. +// A 4-sides polygon is not necessary a rectangle. 4-sides polygons can be apart from each other. +// For example, we use 2 4-sides polygons to describe the shape below: // // ____________ ____________ // / ___/ = /___________/ + _________ @@ -231,7 +231,7 @@ func (el *Element) SelectText(regex string) error { } defer el.tryTrace(TraceTypeInput, "select text: "+regex)() - el.page.browser.trySlowmotion() + el.page.browser.trySlowMotion() _, err = el.Evaluate(evalHelper(js.SelectText, regex).ByUser()) return err @@ -246,7 +246,7 @@ func (el *Element) SelectAllText() error { } defer el.tryTrace(TraceTypeInput, "select all text")() - el.page.browser.trySlowmotion() + el.page.browser.trySlowMotion() _, err = el.Evaluate(evalHelper(js.SelectAllText).ByUser()) return err @@ -317,7 +317,7 @@ func (el *Element) Select(selectors []string, selected bool, t SelectorType) err } defer el.tryTrace(TraceTypeInput, fmt.Sprintf(`select "%s"`, strings.Join(selectors, "; ")))() - el.page.browser.trySlowmotion() + el.page.browser.trySlowMotion() res, err := el.Evaluate(evalHelper(js.Select, selectors, selected, t).ByUser()) if err != nil { @@ -370,7 +370,7 @@ func (el *Element) SetFiles(paths []string) error { absPaths := utils.AbsolutePaths(paths) defer el.tryTrace(TraceTypeInput, fmt.Sprintf("set files: %v", absPaths))() - el.page.browser.trySlowmotion() + el.page.browser.trySlowMotion() err := proto.DOMSetFileInputFiles{ Files: absPaths, diff --git a/vendor/github.com/go-rod/rod/go.work.sum b/vendor/github.com/go-rod/rod/go.work.sum new file mode 100644 index 00000000..3497548a --- /dev/null +++ b/vendor/github.com/go-rod/rod/go.work.sum @@ -0,0 +1,2 @@ +github.com/ysmood/got v0.32.0 h1:aAHdQgfgMb/lo4v+OekM+SSqEJYFI035h5YYvLXsVyU= +github.com/ysmood/got v0.32.0/go.mod h1:pE1l4LOwOBhQg6A/8IAatkGp7uZjnalzrZolnlhhMgY= diff --git a/vendor/github.com/go-rod/rod/input.go b/vendor/github.com/go-rod/rod/input.go index 945ca594..0104f9eb 100644 --- a/vendor/github.com/go-rod/rod/input.go +++ b/vendor/github.com/go-rod/rod/input.go @@ -44,7 +44,7 @@ func (k *Keyboard) modifiers() int { // use method like Page.InsertText . func (k *Keyboard) Press(key input.Key) error { defer k.page.tryTrace(TraceTypeInput, "press key: "+key.Info().Code)() - k.page.browser.trySlowmotion() + k.page.browser.trySlowMotion() k.Lock() defer k.Unlock() @@ -184,7 +184,7 @@ func (ka *KeyActions) balance() []KeyAction { // InsertText is like pasting text into the page func (p *Page) InsertText(text string) error { defer p.tryTrace(TraceTypeInput, "insert text "+text)() - p.browser.trySlowmotion() + p.browser.trySlowMotion() err := proto.InputInsertText{Text: text}.Call(p) return err @@ -223,7 +223,7 @@ func (m *Mouse) MoveTo(p proto.Point) error { button, buttons := input.EncodeMouseButton(m.buttons) - m.page.browser.trySlowmotion() + m.page.browser.trySlowMotion() err := proto.InputDispatchMouseEvent{ Type: proto.InputDispatchMouseEventTypeMouseMoved, @@ -291,7 +291,7 @@ func (m *Mouse) Scroll(offsetX, offsetY float64, steps int) error { defer m.Unlock() defer m.page.tryTrace(TraceTypeInput, fmt.Sprintf("scroll (%.2f, %.2f)", offsetX, offsetY))() - m.page.browser.trySlowmotion() + m.page.browser.trySlowMotion() if steps < 1 { steps = 1 @@ -379,7 +379,7 @@ func (m *Mouse) Up(button proto.InputMouseButton, clickCount int) error { // Click the button. It's the combination of Mouse.Down and Mouse.Up func (m *Mouse) Click(button proto.InputMouseButton, clickCount int) error { - m.page.browser.trySlowmotion() + m.page.browser.trySlowMotion() err := m.Down(button, clickCount) if err != nil { @@ -444,7 +444,7 @@ func (t *Touch) Cancel() error { // Tap dispatches a touchstart and touchend event. func (t *Touch) Tap(x, y float64) error { defer t.page.tryTrace(TraceTypeInput, "touch")() - t.page.browser.trySlowmotion() + t.page.browser.trySlowMotion() p := &proto.InputTouchPoint{X: x, Y: y} diff --git a/vendor/github.com/go-rod/rod/lib/defaults/defaults.go b/vendor/github.com/go-rod/rod/lib/defaults/defaults.go index a3583ff9..35bb1e44 100644 --- a/vendor/github.com/go-rod/rod/lib/defaults/defaults.go +++ b/vendor/github.com/go-rod/rod/lib/defaults/defaults.go @@ -18,7 +18,7 @@ import ( // Option name is "trace". var Trace bool -// Slow is the default of rod.Browser.Slowmotion . +// Slow is the default of rod.Browser.SlowMotion . // The format is same as https://golang.org/pkg/time/#ParseDuration // Option name is "slow". var Slow time.Duration diff --git a/vendor/github.com/go-rod/rod/lib/devices/device.go b/vendor/github.com/go-rod/rod/lib/devices/device.go index 8ce04a3d..058bff87 100644 --- a/vendor/github.com/go-rod/rod/lib/devices/device.go +++ b/vendor/github.com/go-rod/rod/lib/devices/device.go @@ -31,8 +31,8 @@ type ScreenSize struct { Height int } -// Landescape clones the device and set it to landscape mode -func (device Device) Landescape() Device { +// Landscape clones the device and set it to landscape mode +func (device Device) Landscape() Device { d := device d.landscape = true return d diff --git a/vendor/github.com/go-rod/rod/lib/js/helper.js b/vendor/github.com/go-rod/rod/lib/js/helper.js index 680d9825..8b201a2b 100644 --- a/vendor/github.com/go-rod/rod/lib/js/helper.js +++ b/vendor/github.com/go-rod/rod/lib/js/helper.js @@ -41,6 +41,7 @@ const functions = { elementR(selector, regex) { var reg var m = regex.match(/(\/?)(.+)\1([a-z]*)/i) + // cSpell:ignore gmix if (m[3] && !/^(?!.*?(.).*?\1)[gmixXsuUAJ]+$/.test(m[3])) reg = new RegExp(regex) else reg = new RegExp(m[2], m[3]) diff --git a/vendor/github.com/go-rod/rod/lib/launcher/launcher.go b/vendor/github.com/go-rod/rod/lib/launcher/launcher.go index 204e8c07..af2a33ca 100644 --- a/vendor/github.com/go-rod/rod/lib/launcher/launcher.go +++ b/vendor/github.com/go-rod/rod/lib/launcher/launcher.go @@ -3,6 +3,7 @@ package launcher import ( "context" + "crypto" "errors" "fmt" "io" @@ -253,6 +254,23 @@ func (l *Launcher) Devtools(autoOpenForTabs bool) *Launcher { return l.Delete("auto-open-devtools-for-tabs") } +// IgnoreCerts configure the Chrome's ignore-certificate-errors-spki-list argument with the public keys. +func (l *Launcher) IgnoreCerts(pks []crypto.PublicKey) error { + spkis := make([]string, 0, len(pks)) + + for _, pk := range pks { + spki, err := certSPKI(pk) + if err != nil { + return fmt.Errorf("certSPKI: %w", err) + } + spkis = append(spkis, string(spki)) + } + + l.Set("ignore-certificate-errors-spki-list", spkis...) + + return nil +} + // UserDataDir is where the browser will look for all of its state, such as cookie and cache. // When set to empty, browser will use current OS home dir. // Related doc: https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md @@ -284,7 +302,7 @@ func (l *Launcher) RemoteDebuggingPort(port int) *Launcher { return l.Set(flags.RemoteDebuggingPort, fmt.Sprintf("%d", port)) } -// Proxy switch. When disabled leakless will be disabled. +// Proxy for the browser func (l *Launcher) Proxy(host string) *Launcher { return l.Set(flags.ProxyServer, host) } @@ -307,7 +325,7 @@ func (l *Launcher) StartURL(u string) *Launcher { return l.Set("", u) } -// FormatArgs returns the formated arg list for cli +// FormatArgs returns the formatted arg list for cli func (l *Launcher) FormatArgs() []string { execArgs := []string{} for k, v := range l.Flags { diff --git a/vendor/github.com/go-rod/rod/lib/launcher/manager.go b/vendor/github.com/go-rod/rod/lib/launcher/manager.go index 1ab4cd37..237f35b6 100644 --- a/vendor/github.com/go-rod/rod/lib/launcher/manager.go +++ b/vendor/github.com/go-rod/rod/lib/launcher/manager.go @@ -90,7 +90,7 @@ func (l *Launcher) mustManaged() { var _ http.Handler = &Manager{} // Manager is used to launch browsers via http server on another machine. -// The reason why we have Manager is after we launcher a browser, we can't dynamicall change its +// The reason why we have Manager is after we launcher a browser, we can't dynamically change its // CLI arguments, such as "--headless". The Manager allows us to decide what CLI arguments to // pass to the browser when launch it remotely. // The work flow looks like: @@ -133,7 +133,7 @@ func NewManager() *Manager { for f, allowed := range allowedPath { p := l.Get(f) if p != "" && !strings.HasPrefix(p, allowed) { - b := []byte(fmt.Sprintf("not allowed %s path: %s", f, p)) + b := []byte(fmt.Sprintf("[rod-manager] not allowed %s path: %s (use --allow-all to disable the protection)", f, p)) w.Header().Add("Content-Length", fmt.Sprintf("%d", len(b))) w.WriteHeader(http.StatusBadRequest) utils.E(w.Write(b)) diff --git a/vendor/github.com/go-rod/rod/lib/launcher/revision.go b/vendor/github.com/go-rod/rod/lib/launcher/revision.go index 51153b05..76beb074 100644 --- a/vendor/github.com/go-rod/rod/lib/launcher/revision.go +++ b/vendor/github.com/go-rod/rod/lib/launcher/revision.go @@ -3,7 +3,7 @@ package launcher // RevisionDefault for chromium -const RevisionDefault = 1033860 +const RevisionDefault = 1065323 // RevisionPlaywright for arm linux -const RevisionPlaywright = 1019 +const RevisionPlaywright = 1028 diff --git a/vendor/github.com/go-rod/rod/lib/launcher/utils.go b/vendor/github.com/go-rod/rod/lib/launcher/utils.go index 70e802cc..a2d80cb5 100644 --- a/vendor/github.com/go-rod/rod/lib/launcher/utils.go +++ b/vendor/github.com/go-rod/rod/lib/launcher/utils.go @@ -2,6 +2,10 @@ package launcher import ( "archive/zip" + "crypto" + "crypto/sha256" + "crypto/x509" + "encoding/base64" "fmt" "io" "net/url" @@ -110,3 +114,18 @@ func unzip(logger utils.Logger, from, to string) (err error) { return zr.Close() } + +// certSPKI generates the SPKI of a certificate public key +// https://blog.afoolishmanifesto.com/posts/golang-self-signed-and-pinned-certs/ +func certSPKI(pk crypto.PublicKey) ([]byte, error) { + pubDER, err := x509.MarshalPKIXPublicKey(pk) + if err != nil { + return nil, fmt.Errorf("x509.MarshalPKIXPublicKey: %w", err) + } + + sum := sha256.Sum256(pubDER) + pin := make([]byte, base64.StdEncoding.EncodedLen(len(sum))) + base64.StdEncoding.Encode(pin, sum[:]) + + return pin, nil +} diff --git a/vendor/github.com/go-rod/rod/lib/proto/accessibility.go b/vendor/github.com/go-rod/rod/lib/proto/accessibility.go index 8ae4177e..5a9eaba2 100644 --- a/vendor/github.com/go-rod/rod/lib/proto/accessibility.go +++ b/vendor/github.com/go-rod/rod/lib/proto/accessibility.go @@ -435,7 +435,7 @@ type AccessibilityGetFullAXTree struct { Depth *int `json:"depth,omitempty"` // FrameID (optional) The frame for whose document the AX tree should be retrieved. - // If omitted, the root frame is used. + // If omited, the root frame is used. FrameID PageFrameID `json:"frameId,omitempty"` } diff --git a/vendor/github.com/go-rod/rod/lib/proto/audits.go b/vendor/github.com/go-rod/rod/lib/proto/audits.go index c24004f1..d583d053 100644 --- a/vendor/github.com/go-rod/rod/lib/proto/audits.go +++ b/vendor/github.com/go-rod/rod/lib/proto/audits.go @@ -531,6 +531,9 @@ const ( // AuditsAttributionReportingIssueTypePermissionPolicyDisabled enum const AuditsAttributionReportingIssueTypePermissionPolicyDisabled AuditsAttributionReportingIssueType = "PermissionPolicyDisabled" + // AuditsAttributionReportingIssueTypePermissionPolicyNotDelegated enum const + AuditsAttributionReportingIssueTypePermissionPolicyNotDelegated AuditsAttributionReportingIssueType = "PermissionPolicyNotDelegated" + // AuditsAttributionReportingIssueTypeUntrustworthyReportingOrigin enum const AuditsAttributionReportingIssueTypeUntrustworthyReportingOrigin AuditsAttributionReportingIssueType = "UntrustworthyReportingOrigin" @@ -689,9 +692,6 @@ const ( // AuditsDeprecationIssueTypeInsecurePrivateNetworkSubresourceRequest enum const AuditsDeprecationIssueTypeInsecurePrivateNetworkSubresourceRequest AuditsDeprecationIssueType = "InsecurePrivateNetworkSubresourceRequest" - // AuditsDeprecationIssueTypeLegacyConstraintGoogIPv6 enum const - AuditsDeprecationIssueTypeLegacyConstraintGoogIPv6 AuditsDeprecationIssueType = "LegacyConstraintGoogIPv6" - // AuditsDeprecationIssueTypeLocalCSSFileExtensionRejected enum const AuditsDeprecationIssueTypeLocalCSSFileExtensionRejected AuditsDeprecationIssueType = "LocalCSSFileExtensionRejected" @@ -701,12 +701,6 @@ const ( // AuditsDeprecationIssueTypeMediaSourceDurationTruncatingBuffered enum const AuditsDeprecationIssueTypeMediaSourceDurationTruncatingBuffered AuditsDeprecationIssueType = "MediaSourceDurationTruncatingBuffered" - // AuditsDeprecationIssueTypeNavigateEventRestoreScroll enum const - AuditsDeprecationIssueTypeNavigateEventRestoreScroll AuditsDeprecationIssueType = "NavigateEventRestoreScroll" - - // AuditsDeprecationIssueTypeNavigateEventTransitionWhile enum const - AuditsDeprecationIssueTypeNavigateEventTransitionWhile AuditsDeprecationIssueType = "NavigateEventTransitionWhile" - // AuditsDeprecationIssueTypeNoSysexWebMIDIWithoutPermission enum const AuditsDeprecationIssueTypeNoSysexWebMIDIWithoutPermission AuditsDeprecationIssueType = "NoSysexWebMIDIWithoutPermission" @@ -725,6 +719,12 @@ const ( // AuditsDeprecationIssueTypeOverflowVisibleOnReplacedElement enum const AuditsDeprecationIssueTypeOverflowVisibleOnReplacedElement AuditsDeprecationIssueType = "OverflowVisibleOnReplacedElement" + // AuditsDeprecationIssueTypePaymentInstruments enum const + AuditsDeprecationIssueTypePaymentInstruments AuditsDeprecationIssueType = "PaymentInstruments" + + // AuditsDeprecationIssueTypePaymentRequestCSPViolation enum const + AuditsDeprecationIssueTypePaymentRequestCSPViolation AuditsDeprecationIssueType = "PaymentRequestCSPViolation" + // AuditsDeprecationIssueTypePersistentQuotaType enum const AuditsDeprecationIssueTypePersistentQuotaType AuditsDeprecationIssueType = "PersistentQuotaType" @@ -837,8 +837,8 @@ type AuditsFederatedAuthRequestIssueDetails struct { type AuditsFederatedAuthRequestIssueReason string const ( - // AuditsFederatedAuthRequestIssueReasonApprovalDeclined enum const - AuditsFederatedAuthRequestIssueReasonApprovalDeclined AuditsFederatedAuthRequestIssueReason = "ApprovalDeclined" + // AuditsFederatedAuthRequestIssueReasonShouldEmbargo enum const + AuditsFederatedAuthRequestIssueReasonShouldEmbargo AuditsFederatedAuthRequestIssueReason = "ShouldEmbargo" // AuditsFederatedAuthRequestIssueReasonTooManyRequests enum const AuditsFederatedAuthRequestIssueReasonTooManyRequests AuditsFederatedAuthRequestIssueReason = "TooManyRequests" @@ -911,6 +911,9 @@ const ( // AuditsFederatedAuthRequestIssueReasonCanceled enum const AuditsFederatedAuthRequestIssueReasonCanceled AuditsFederatedAuthRequestIssueReason = "Canceled" + + // AuditsFederatedAuthRequestIssueReasonRpPageNotVisible enum const + AuditsFederatedAuthRequestIssueReasonRpPageNotVisible AuditsFederatedAuthRequestIssueReason = "RpPageNotVisible" ) // AuditsClientHintIssueDetails This issue tracks client hints related issues. It's used to deprecate old diff --git a/vendor/github.com/go-rod/rod/lib/proto/css.go b/vendor/github.com/go-rod/rod/lib/proto/css.go index 8c228dda..6c505c14 100644 --- a/vendor/github.com/go-rod/rod/lib/proto/css.go +++ b/vendor/github.com/go-rod/rod/lib/proto/css.go @@ -298,6 +298,10 @@ type CSSCSSProperty struct { // Range (optional) The entire property range in the enclosing style declaration (if available). Range *CSSSourceRange `json:"range,omitempty"` + + // LonghandProperties (experimental) (optional) Parsed longhand components of this property if it is a shorthand. + // This field will be empty if the given property is not a shorthand. + LonghandProperties []*CSSCSSProperty `json:"longhandProperties,omitempty"` } // CSSCSSMediaSource enum diff --git a/vendor/github.com/go-rod/rod/lib/proto/debugger.go b/vendor/github.com/go-rod/rod/lib/proto/debugger.go index 24282597..c1c982c3 100644 --- a/vendor/github.com/go-rod/rod/lib/proto/debugger.go +++ b/vendor/github.com/go-rod/rod/lib/proto/debugger.go @@ -574,7 +574,7 @@ const ( // DebuggerRestartFrame Restarts particular call frame from the beginning. The old, deprecated // behavior of `restartFrame` is to stay paused and allow further CDP commands // after a restart was scheduled. This can cause problems with restarting, so -// we now continue execution immediately after it has been scheduled until we +// we now continue execution immediatly after it has been scheduled until we // reach the beginning of the restarted frame. // // To stay back-wards compatible, `restartFrame` now expects a `mode` diff --git a/vendor/github.com/go-rod/rod/lib/proto/definitions.go b/vendor/github.com/go-rod/rod/lib/proto/definitions.go index cefccb46..46d4bba2 100644 --- a/vendor/github.com/go-rod/rod/lib/proto/definitions.go +++ b/vendor/github.com/go-rod/rod/lib/proto/definitions.go @@ -723,6 +723,8 @@ var types = map[string]reflect.Type{ "Page.getManifestIconsResult": reflect.TypeOf(PageGetManifestIconsResult{}), "Page.getAppId": reflect.TypeOf(PageGetAppID{}), "Page.getAppIdResult": reflect.TypeOf(PageGetAppIDResult{}), + "Page.getAdScriptId": reflect.TypeOf(PageGetAdScriptID{}), + "Page.getAdScriptIdResult": reflect.TypeOf(PageGetAdScriptIDResult{}), "Page.getCookies": reflect.TypeOf(PageGetCookies{}), "Page.getCookiesResult": reflect.TypeOf(PageGetCookiesResult{}), "Page.getFrameTree": reflect.TypeOf(PageGetFrameTree{}), @@ -852,6 +854,11 @@ var types = map[string]reflect.Type{ "Storage.TrustTokens": reflect.TypeOf(StorageTrustTokens{}), "Storage.InterestGroupAd": reflect.TypeOf(StorageInterestGroupAd{}), "Storage.InterestGroupDetails": reflect.TypeOf(StorageInterestGroupDetails{}), + "Storage.SharedStorageEntry": reflect.TypeOf(StorageSharedStorageEntry{}), + "Storage.SharedStorageMetadata": reflect.TypeOf(StorageSharedStorageMetadata{}), + "Storage.SharedStorageReportingMetadata": reflect.TypeOf(StorageSharedStorageReportingMetadata{}), + "Storage.SharedStorageUrlWithMetadata": reflect.TypeOf(StorageSharedStorageURLWithMetadata{}), + "Storage.SharedStorageAccessParams": reflect.TypeOf(StorageSharedStorageAccessParams{}), "Storage.getStorageKeyForFrame": reflect.TypeOf(StorageGetStorageKeyForFrame{}), "Storage.getStorageKeyForFrameResult": reflect.TypeOf(StorageGetStorageKeyForFrameResult{}), "Storage.clearDataForOrigin": reflect.TypeOf(StorageClearDataForOrigin{}), @@ -876,11 +883,17 @@ var types = map[string]reflect.Type{ "Storage.getInterestGroupDetails": reflect.TypeOf(StorageGetInterestGroupDetails{}), "Storage.getInterestGroupDetailsResult": reflect.TypeOf(StorageGetInterestGroupDetailsResult{}), "Storage.setInterestGroupTracking": reflect.TypeOf(StorageSetInterestGroupTracking{}), + "Storage.getSharedStorageMetadata": reflect.TypeOf(StorageGetSharedStorageMetadata{}), + "Storage.getSharedStorageMetadataResult": reflect.TypeOf(StorageGetSharedStorageMetadataResult{}), + "Storage.getSharedStorageEntries": reflect.TypeOf(StorageGetSharedStorageEntries{}), + "Storage.getSharedStorageEntriesResult": reflect.TypeOf(StorageGetSharedStorageEntriesResult{}), + "Storage.setSharedStorageTracking": reflect.TypeOf(StorageSetSharedStorageTracking{}), "Storage.cacheStorageContentUpdated": reflect.TypeOf(StorageCacheStorageContentUpdated{}), "Storage.cacheStorageListUpdated": reflect.TypeOf(StorageCacheStorageListUpdated{}), "Storage.indexedDBContentUpdated": reflect.TypeOf(StorageIndexedDBContentUpdated{}), "Storage.indexedDBListUpdated": reflect.TypeOf(StorageIndexedDBListUpdated{}), "Storage.interestGroupAccessed": reflect.TypeOf(StorageInterestGroupAccessed{}), + "Storage.sharedStorageAccessed": reflect.TypeOf(StorageSharedStorageAccessed{}), "SystemInfo.GPUDevice": reflect.TypeOf(SystemInfoGPUDevice{}), "SystemInfo.Size": reflect.TypeOf(SystemInfoSize{}), "SystemInfo.VideoDecodeAcceleratorCapability": reflect.TypeOf(SystemInfoVideoDecodeAcceleratorCapability{}), @@ -1104,9 +1117,6 @@ var types = map[string]reflect.Type{ "Profiler.CoverageRange": reflect.TypeOf(ProfilerCoverageRange{}), "Profiler.FunctionCoverage": reflect.TypeOf(ProfilerFunctionCoverage{}), "Profiler.ScriptCoverage": reflect.TypeOf(ProfilerScriptCoverage{}), - "Profiler.TypeObject": reflect.TypeOf(ProfilerTypeObject{}), - "Profiler.TypeProfileEntry": reflect.TypeOf(ProfilerTypeProfileEntry{}), - "Profiler.ScriptTypeProfile": reflect.TypeOf(ProfilerScriptTypeProfile{}), "Profiler.disable": reflect.TypeOf(ProfilerDisable{}), "Profiler.enable": reflect.TypeOf(ProfilerEnable{}), "Profiler.getBestEffortCoverage": reflect.TypeOf(ProfilerGetBestEffortCoverage{}), @@ -1115,15 +1125,11 @@ var types = map[string]reflect.Type{ "Profiler.start": reflect.TypeOf(ProfilerStart{}), "Profiler.startPreciseCoverage": reflect.TypeOf(ProfilerStartPreciseCoverage{}), "Profiler.startPreciseCoverageResult": reflect.TypeOf(ProfilerStartPreciseCoverageResult{}), - "Profiler.startTypeProfile": reflect.TypeOf(ProfilerStartTypeProfile{}), "Profiler.stop": reflect.TypeOf(ProfilerStop{}), "Profiler.stopResult": reflect.TypeOf(ProfilerStopResult{}), "Profiler.stopPreciseCoverage": reflect.TypeOf(ProfilerStopPreciseCoverage{}), - "Profiler.stopTypeProfile": reflect.TypeOf(ProfilerStopTypeProfile{}), "Profiler.takePreciseCoverage": reflect.TypeOf(ProfilerTakePreciseCoverage{}), "Profiler.takePreciseCoverageResult": reflect.TypeOf(ProfilerTakePreciseCoverageResult{}), - "Profiler.takeTypeProfile": reflect.TypeOf(ProfilerTakeTypeProfile{}), - "Profiler.takeTypeProfileResult": reflect.TypeOf(ProfilerTakeTypeProfileResult{}), "Profiler.consoleProfileFinished": reflect.TypeOf(ProfilerConsoleProfileFinished{}), "Profiler.consoleProfileStarted": reflect.TypeOf(ProfilerConsoleProfileStarted{}), "Profiler.preciseCoverageDeltaUpdate": reflect.TypeOf(ProfilerPreciseCoverageDeltaUpdate{}), diff --git a/vendor/github.com/go-rod/rod/lib/proto/dom.go b/vendor/github.com/go-rod/rod/lib/proto/dom.go index 2072a5c4..e87b10ce 100644 --- a/vendor/github.com/go-rod/rod/lib/proto/dom.go +++ b/vendor/github.com/go-rod/rod/lib/proto/dom.go @@ -1546,7 +1546,7 @@ type DOMChildNodeInserted struct { // ParentNodeID Id of the node that has changed. ParentNodeID DOMNodeID `json:"parentNodeId"` - // PreviousNodeID If of the previous siblint. + // PreviousNodeID Id of the previous sibling. PreviousNodeID DOMNodeID `json:"previousNodeId"` // Node Inserted node data. diff --git a/vendor/github.com/go-rod/rod/lib/proto/fetch.go b/vendor/github.com/go-rod/rod/lib/proto/fetch.go index b59e0360..b81a6b47 100644 --- a/vendor/github.com/go-rod/rod/lib/proto/fetch.go +++ b/vendor/github.com/go-rod/rod/lib/proto/fetch.go @@ -211,7 +211,9 @@ type FetchContinueRequest struct { // PostData (optional) If set, overrides the post data in the request. PostData []byte `json:"postData,omitempty"` - // Headers (optional) If set, overrides the request headers. + // Headers (optional) If set, overrides the request headers. Note that the overrides do not + // extend to subsequent redirect hops, if a redirect happens. Another override + // may be applied to a different request produced by a redirect. Headers []*FetchHeaderEntry `json:"headers,omitempty"` // InterceptResponse (experimental) (optional) If set, overrides response interception behavior for this request. @@ -374,7 +376,11 @@ type FetchRequestPaused struct { // NetworkID (optional) If the intercepted request had a corresponding Network.requestWillBeSent event fired for it, // then this networkId will be the same as the requestId present in the requestWillBeSent event. - NetworkID FetchRequestID `json:"networkId,omitempty"` + NetworkID NetworkRequestID `json:"networkId,omitempty"` + + // RedirectedRequestID (experimental) (optional) If the request is due to a redirect response from the server, the id of the request that + // has caused the redirect. + RedirectedRequestID FetchRequestID `json:"redirectedRequestId,omitempty"` } // ProtoEvent name diff --git a/vendor/github.com/go-rod/rod/lib/proto/headless_experimental.go b/vendor/github.com/go-rod/rod/lib/proto/headless_experimental.go index e2de1ff9..ec5c3bfc 100644 --- a/vendor/github.com/go-rod/rod/lib/proto/headless_experimental.go +++ b/vendor/github.com/go-rod/rod/lib/proto/headless_experimental.go @@ -19,6 +19,9 @@ const ( // HeadlessExperimentalScreenshotParamsFormatPng enum const HeadlessExperimentalScreenshotParamsFormatPng HeadlessExperimentalScreenshotParamsFormat = "png" + + // HeadlessExperimentalScreenshotParamsFormatWebp enum const + HeadlessExperimentalScreenshotParamsFormatWebp HeadlessExperimentalScreenshotParamsFormat = "webp" ) // HeadlessExperimentalScreenshotParams Encoding options for a screenshot. @@ -29,6 +32,9 @@ type HeadlessExperimentalScreenshotParams struct { // Quality (optional) Compression quality from range [0..100] (jpeg only). Quality *int `json:"quality,omitempty"` + + // OptimizeForSpeed (optional) Optimize image encoding for speed, not for resulting size (defaults to false) + OptimizeForSpeed bool `json:"optimizeForSpeed,omitempty"` } // HeadlessExperimentalBeginFrame Sends a BeginFrame to the target and returns when the frame was completed. Optionally captures a diff --git a/vendor/github.com/go-rod/rod/lib/proto/heap_profiler.go b/vendor/github.com/go-rod/rod/lib/proto/heap_profiler.go index 313e2f30..1ced51da 100644 --- a/vendor/github.com/go-rod/rod/lib/proto/heap_profiler.go +++ b/vendor/github.com/go-rod/rod/lib/proto/heap_profiler.go @@ -182,6 +182,24 @@ type HeapProfilerStartSampling struct { // SamplingInterval (optional) Average sample interval in bytes. Poisson distribution is used for the intervals. The // default value is 32768 bytes. SamplingInterval *float64 `json:"samplingInterval,omitempty"` + + // IncludeObjectsCollectedByMajorGC (optional) By default, the sampling heap profiler reports only objects which are + // still alive when the profile is returned via getSamplingProfile or + // stopSampling, which is useful for determining what functions contribute + // the most to steady-state memory usage. This flag instructs the sampling + // heap profiler to also include information about objects discarded by + // major GC, which will show which functions cause large temporary memory + // usage or long GC pauses. + IncludeObjectsCollectedByMajorGC bool `json:"includeObjectsCollectedByMajorGC,omitempty"` + + // IncludeObjectsCollectedByMinorGC (optional) By default, the sampling heap profiler reports only objects which are + // still alive when the profile is returned via getSamplingProfile or + // stopSampling, which is useful for determining what functions contribute + // the most to steady-state memory usage. This flag instructs the sampling + // heap profiler to also include information about objects discarded by + // minor GC, which is useful when tuning a latency-sensitive application + // for minimal GC activity. + IncludeObjectsCollectedByMinorGC bool `json:"includeObjectsCollectedByMinorGC,omitempty"` } // ProtoReq name diff --git a/vendor/github.com/go-rod/rod/lib/proto/network.go b/vendor/github.com/go-rod/rod/lib/proto/network.go index 7d7d5ebd..d34a4f0b 100644 --- a/vendor/github.com/go-rod/rod/lib/proto/network.go +++ b/vendor/github.com/go-rod/rod/lib/proto/network.go @@ -670,6 +670,35 @@ const ( NetworkTrustTokenOperationTypeSigning NetworkTrustTokenOperationType = "Signing" ) +// NetworkAlternateProtocolUsage (experimental) The reason why Chrome uses a specific transport protocol for HTTP semantics. +type NetworkAlternateProtocolUsage string + +const ( + // NetworkAlternateProtocolUsageAlternativeJobWonWithoutRace enum const + NetworkAlternateProtocolUsageAlternativeJobWonWithoutRace NetworkAlternateProtocolUsage = "alternativeJobWonWithoutRace" + + // NetworkAlternateProtocolUsageAlternativeJobWonRace enum const + NetworkAlternateProtocolUsageAlternativeJobWonRace NetworkAlternateProtocolUsage = "alternativeJobWonRace" + + // NetworkAlternateProtocolUsageMainJobWonRace enum const + NetworkAlternateProtocolUsageMainJobWonRace NetworkAlternateProtocolUsage = "mainJobWonRace" + + // NetworkAlternateProtocolUsageMappingMissing enum const + NetworkAlternateProtocolUsageMappingMissing NetworkAlternateProtocolUsage = "mappingMissing" + + // NetworkAlternateProtocolUsageBroken enum const + NetworkAlternateProtocolUsageBroken NetworkAlternateProtocolUsage = "broken" + + // NetworkAlternateProtocolUsageDNSAlpnH3JobWonWithoutRace enum const + NetworkAlternateProtocolUsageDNSAlpnH3JobWonWithoutRace NetworkAlternateProtocolUsage = "dnsAlpnH3JobWonWithoutRace" + + // NetworkAlternateProtocolUsageDNSAlpnH3JobWonRace enum const + NetworkAlternateProtocolUsageDNSAlpnH3JobWonRace NetworkAlternateProtocolUsage = "dnsAlpnH3JobWonRace" + + // NetworkAlternateProtocolUsageUnspecifiedReason enum const + NetworkAlternateProtocolUsageUnspecifiedReason NetworkAlternateProtocolUsage = "unspecifiedReason" +) + // NetworkResponse HTTP response data. type NetworkResponse struct { @@ -736,6 +765,9 @@ type NetworkResponse struct { // Protocol (optional) Protocol used to fetch this request. Protocol string `json:"protocol,omitempty"` + // AlternateProtocolUsage (experimental) (optional) The reason why Chrome uses a specific transport protocol for HTTP semantics. + AlternateProtocolUsage NetworkAlternateProtocolUsage `json:"alternateProtocolUsage,omitempty"` + // SecurityState Security state of the request resource. SecurityState SecuritySecurityState `json:"securityState"` diff --git a/vendor/github.com/go-rod/rod/lib/proto/page.go b/vendor/github.com/go-rod/rod/lib/proto/page.go index dfe5787c..59107ee0 100644 --- a/vendor/github.com/go-rod/rod/lib/proto/page.go +++ b/vendor/github.com/go-rod/rod/lib/proto/page.go @@ -147,11 +147,14 @@ const ( PagePermissionsPolicyFeatureChDownlink PagePermissionsPolicyFeature = "ch-downlink" // PagePermissionsPolicyFeatureChEct enum const - PagePermissionsPolicyFeatureChEct PagePermissionsPolicyFeature = "ch-etc" + PagePermissionsPolicyFeatureChEct PagePermissionsPolicyFeature = "ch-ect" // PagePermissionsPolicyFeatureChPrefersColorScheme enum const PagePermissionsPolicyFeatureChPrefersColorScheme PagePermissionsPolicyFeature = "ch-prefers-color-scheme" + // PagePermissionsPolicyFeatureChPrefersReducedMotion enum const + PagePermissionsPolicyFeatureChPrefersReducedMotion PagePermissionsPolicyFeature = "ch-prefers-reduced-motion" + // PagePermissionsPolicyFeatureChRtt enum const PagePermissionsPolicyFeatureChRtt PagePermissionsPolicyFeature = "ch-rtt" @@ -209,6 +212,9 @@ const ( // PagePermissionsPolicyFeatureClipboardWrite enum const PagePermissionsPolicyFeatureClipboardWrite PagePermissionsPolicyFeature = "clipboard-write" + // PagePermissionsPolicyFeatureComputePressure enum const + PagePermissionsPolicyFeatureComputePressure PagePermissionsPolicyFeature = "compute-pressure" + // PagePermissionsPolicyFeatureCrossOriginIsolated enum const PagePermissionsPolicyFeatureCrossOriginIsolated PagePermissionsPolicyFeature = "cross-origin-isolated" @@ -230,9 +236,6 @@ const ( // PagePermissionsPolicyFeatureExecutionWhileNotRendered enum const PagePermissionsPolicyFeatureExecutionWhileNotRendered PagePermissionsPolicyFeature = "execution-while-not-rendered" - // PagePermissionsPolicyFeatureFederatedCredentials enum const - PagePermissionsPolicyFeatureFederatedCredentials PagePermissionsPolicyFeature = "federated-credentials" - // PagePermissionsPolicyFeatureFocusWithoutUserActivation enum const PagePermissionsPolicyFeatureFocusWithoutUserActivation PagePermissionsPolicyFeature = "focus-without-user-activation" @@ -254,6 +257,9 @@ const ( // PagePermissionsPolicyFeatureHid enum const PagePermissionsPolicyFeatureHid PagePermissionsPolicyFeature = "hid" + // PagePermissionsPolicyFeatureIdentityCredentialsGet enum const + PagePermissionsPolicyFeatureIdentityCredentialsGet PagePermissionsPolicyFeature = "identity-credentials-get" + // PagePermissionsPolicyFeatureIdleDetection enum const PagePermissionsPolicyFeatureIdleDetection PagePermissionsPolicyFeature = "idle-detection" @@ -305,8 +311,8 @@ const ( // PagePermissionsPolicyFeatureSharedStorage enum const PagePermissionsPolicyFeatureSharedStorage PagePermissionsPolicyFeature = "shared-storage" - // PagePermissionsPolicyFeatureStorageAccessAPI enum const - PagePermissionsPolicyFeatureStorageAccessAPI PagePermissionsPolicyFeature = "storage-access-api" + // PagePermissionsPolicyFeatureStorageAccess enum const + PagePermissionsPolicyFeatureStorageAccess PagePermissionsPolicyFeature = "storage-access" // PagePermissionsPolicyFeatureSyncXhr enum const PagePermissionsPolicyFeatureSyncXhr PagePermissionsPolicyFeature = "sync-xhr" @@ -1144,9 +1150,6 @@ const ( // PageBackForwardCacheNotRestoredReasonOutstandingIndexedDBTransaction enum const PageBackForwardCacheNotRestoredReasonOutstandingIndexedDBTransaction PageBackForwardCacheNotRestoredReason = "OutstandingIndexedDBTransaction" - // PageBackForwardCacheNotRestoredReasonRequestedNotificationsPermission enum const - PageBackForwardCacheNotRestoredReasonRequestedNotificationsPermission PageBackForwardCacheNotRestoredReason = "RequestedNotificationsPermission" - // PageBackForwardCacheNotRestoredReasonRequestedMIDIPermission enum const PageBackForwardCacheNotRestoredReasonRequestedMIDIPermission PageBackForwardCacheNotRestoredReason = "RequestedMIDIPermission" @@ -1237,6 +1240,9 @@ const ( // PageBackForwardCacheNotRestoredReasonInjectedStyleSheet enum const PageBackForwardCacheNotRestoredReasonInjectedStyleSheet PageBackForwardCacheNotRestoredReason = "InjectedStyleSheet" + // PageBackForwardCacheNotRestoredReasonKeepaliveRequest enum const + PageBackForwardCacheNotRestoredReasonKeepaliveRequest PageBackForwardCacheNotRestoredReason = "KeepaliveRequest" + // PageBackForwardCacheNotRestoredReasonDummy enum const PageBackForwardCacheNotRestoredReasonDummy PageBackForwardCacheNotRestoredReason = "Dummy" @@ -1371,12 +1377,6 @@ const ( // PagePrerenderFinalStatusLowEndDevice enum const PagePrerenderFinalStatusLowEndDevice PagePrerenderFinalStatus = "LowEndDevice" - // PagePrerenderFinalStatusCrossOriginRedirect enum const - PagePrerenderFinalStatusCrossOriginRedirect PagePrerenderFinalStatus = "CrossOriginRedirect" - - // PagePrerenderFinalStatusCrossOriginNavigation enum const - PagePrerenderFinalStatusCrossOriginNavigation PagePrerenderFinalStatus = "CrossOriginNavigation" - // PagePrerenderFinalStatusInvalidSchemeRedirect enum const PagePrerenderFinalStatusInvalidSchemeRedirect PagePrerenderFinalStatus = "InvalidSchemeRedirect" @@ -1452,20 +1452,53 @@ const ( // PagePrerenderFinalStatusTriggerBackgrounded enum const PagePrerenderFinalStatusTriggerBackgrounded PagePrerenderFinalStatus = "TriggerBackgrounded" - // PagePrerenderFinalStatusEmbedderTriggeredAndSameOriginRedirected enum const - PagePrerenderFinalStatusEmbedderTriggeredAndSameOriginRedirected PagePrerenderFinalStatus = "EmbedderTriggeredAndSameOriginRedirected" - // PagePrerenderFinalStatusEmbedderTriggeredAndCrossOriginRedirected enum const PagePrerenderFinalStatusEmbedderTriggeredAndCrossOriginRedirected PagePrerenderFinalStatus = "EmbedderTriggeredAndCrossOriginRedirected" - // PagePrerenderFinalStatusEmbedderTriggeredAndDestroyed enum const - PagePrerenderFinalStatusEmbedderTriggeredAndDestroyed PagePrerenderFinalStatus = "EmbedderTriggeredAndDestroyed" - // PagePrerenderFinalStatusMemoryLimitExceeded enum const PagePrerenderFinalStatusMemoryLimitExceeded PagePrerenderFinalStatus = "MemoryLimitExceeded" // PagePrerenderFinalStatusFailToGetMemoryUsage enum const PagePrerenderFinalStatusFailToGetMemoryUsage PagePrerenderFinalStatus = "FailToGetMemoryUsage" + + // PagePrerenderFinalStatusDataSaverEnabled enum const + PagePrerenderFinalStatusDataSaverEnabled PagePrerenderFinalStatus = "DataSaverEnabled" + + // PagePrerenderFinalStatusHasEffectiveURL enum const + PagePrerenderFinalStatusHasEffectiveURL PagePrerenderFinalStatus = "HasEffectiveUrl" + + // PagePrerenderFinalStatusActivatedBeforeStarted enum const + PagePrerenderFinalStatusActivatedBeforeStarted PagePrerenderFinalStatus = "ActivatedBeforeStarted" + + // PagePrerenderFinalStatusInactivePageRestriction enum const + PagePrerenderFinalStatusInactivePageRestriction PagePrerenderFinalStatus = "InactivePageRestriction" + + // PagePrerenderFinalStatusStartFailed enum const + PagePrerenderFinalStatusStartFailed PagePrerenderFinalStatus = "StartFailed" + + // PagePrerenderFinalStatusTimeoutBackgrounded enum const + PagePrerenderFinalStatusTimeoutBackgrounded PagePrerenderFinalStatus = "TimeoutBackgrounded" + + // PagePrerenderFinalStatusCrossSiteRedirect enum const + PagePrerenderFinalStatusCrossSiteRedirect PagePrerenderFinalStatus = "CrossSiteRedirect" + + // PagePrerenderFinalStatusCrossSiteNavigation enum const + PagePrerenderFinalStatusCrossSiteNavigation PagePrerenderFinalStatus = "CrossSiteNavigation" + + // PagePrerenderFinalStatusSameSiteCrossOriginRedirect enum const + PagePrerenderFinalStatusSameSiteCrossOriginRedirect PagePrerenderFinalStatus = "SameSiteCrossOriginRedirect" + + // PagePrerenderFinalStatusSameSiteCrossOriginNavigation enum const + PagePrerenderFinalStatusSameSiteCrossOriginNavigation PagePrerenderFinalStatus = "SameSiteCrossOriginNavigation" + + // PagePrerenderFinalStatusSameSiteCrossOriginRedirectNotOptIn enum const + PagePrerenderFinalStatusSameSiteCrossOriginRedirectNotOptIn PagePrerenderFinalStatus = "SameSiteCrossOriginRedirectNotOptIn" + + // PagePrerenderFinalStatusSameSiteCrossOriginNavigationNotOptIn enum const + PagePrerenderFinalStatusSameSiteCrossOriginNavigationNotOptIn PagePrerenderFinalStatus = "SameSiteCrossOriginNavigationNotOptIn" + + // PagePrerenderFinalStatusActivationNavigationParameterMismatch enum const + PagePrerenderFinalStatusActivationNavigationParameterMismatch PagePrerenderFinalStatus = "ActivationNavigationParameterMismatch" ) // PageAddScriptToEvaluateOnLoad (deprecated) (experimental) Deprecated, please use addScriptToEvaluateOnNewDocument instead. @@ -1568,6 +1601,9 @@ type PageCaptureScreenshot struct { // CaptureBeyondViewport (experimental) (optional) Capture the screenshot beyond the viewport. Defaults to false. CaptureBeyondViewport bool `json:"captureBeyondViewport,omitempty"` + + // OptimizeForSpeed (experimental) (optional) Optimize image encoding for speed, not for resulting size (defaults to false) + OptimizeForSpeed bool `json:"optimizeForSpeed,omitempty"` } // ProtoReq name @@ -1821,8 +1857,33 @@ type PageGetAppIDResult struct { RecommendedID string `json:"recommendedId,omitempty"` } -// PageGetCookies (deprecated) (experimental) Returns all browser cookies. Depending on the backend support, will return detailed cookie -// information in the `cookies` field. +// PageGetAdScriptID (experimental) ... +type PageGetAdScriptID struct { + + // FrameID ... + FrameID PageFrameID `json:"frameId"` +} + +// ProtoReq name +func (m PageGetAdScriptID) ProtoReq() string { return "Page.getAdScriptId" } + +// Call the request +func (m PageGetAdScriptID) Call(c Client) (*PageGetAdScriptIDResult, error) { + var res PageGetAdScriptIDResult + return &res, call(m.ProtoReq(), m, &res, c) +} + +// PageGetAdScriptIDResult (experimental) ... +type PageGetAdScriptIDResult struct { + + // AdScriptID (optional) Identifies the bottom-most script which caused the frame to be labelled + // as an ad. Only sent if frame is labelled as an ad and id is available. + AdScriptID *PageAdScriptID `json:"adScriptId,omitempty"` +} + +// PageGetCookies (deprecated) (experimental) Returns all browser cookies for the page and all of its subframes. Depending +// on the backend support, will return detailed cookie information in the +// `cookies` field. type PageGetCookies struct { } @@ -2851,10 +2912,6 @@ type PageFrameAttached struct { // Stack (optional) JavaScript stack trace of when frame was attached, only set if frame initiated from script. Stack *RuntimeStackTrace `json:"stack,omitempty"` - - // AdScriptID (experimental) (optional) Identifies the bottom-most script which caused the frame to be labelled - // as an ad. Only sent if frame is labelled as an ad and id is available. - AdScriptID *PageAdScriptID `json:"adScriptId,omitempty"` } // ProtoEvent name @@ -3215,9 +3272,9 @@ type PagePrerenderAttemptCompleted struct { // FinalStatus ... FinalStatus PagePrerenderFinalStatus `json:"finalStatus"` - // ReasonDetails (optional) This is used to give users more information about the cancellation details, - // and this will be formatted for display. - ReasonDetails string `json:"reasonDetails,omitempty"` + // DisallowedAPIMethod (optional) This is used to give users more information about the name of the API call + // that is incompatible with prerender and has caused the cancellation of the attempt + DisallowedAPIMethod string `json:"disallowedApiMethod,omitempty"` } // ProtoEvent name diff --git a/vendor/github.com/go-rod/rod/lib/proto/profiler.go b/vendor/github.com/go-rod/rod/lib/proto/profiler.go index 0850fd7b..2675eb47 100644 --- a/vendor/github.com/go-rod/rod/lib/proto/profiler.go +++ b/vendor/github.com/go-rod/rod/lib/proto/profiler.go @@ -100,36 +100,6 @@ type ProfilerScriptCoverage struct { Functions []*ProfilerFunctionCoverage `json:"functions"` } -// ProfilerTypeObject (experimental) Describes a type collected during runtime. -type ProfilerTypeObject struct { - - // Name Name of a type collected with type profiling. - Name string `json:"name"` -} - -// ProfilerTypeProfileEntry (experimental) Source offset and types for a parameter or return value. -type ProfilerTypeProfileEntry struct { - - // Offset Source offset of the parameter or end of function for return values. - Offset int `json:"offset"` - - // Types The types for this parameter or return value. - Types []*ProfilerTypeObject `json:"types"` -} - -// ProfilerScriptTypeProfile (experimental) Type profile data collected during runtime for a JavaScript script. -type ProfilerScriptTypeProfile struct { - - // ScriptID JavaScript script id. - ScriptID RuntimeScriptID `json:"scriptId"` - - // URL JavaScript script name or url. - URL string `json:"url"` - - // Entries Type profile entries for parameters and return values of the functions in the script. - Entries []*ProfilerTypeProfileEntry `json:"entries"` -} - // ProfilerDisable ... type ProfilerDisable struct { } @@ -233,18 +203,6 @@ type ProfilerStartPreciseCoverageResult struct { Timestamp float64 `json:"timestamp"` } -// ProfilerStartTypeProfile (experimental) Enable type profile. -type ProfilerStartTypeProfile struct { -} - -// ProtoReq name -func (m ProfilerStartTypeProfile) ProtoReq() string { return "Profiler.startTypeProfile" } - -// Call sends the request -func (m ProfilerStartTypeProfile) Call(c Client) error { - return call(m.ProtoReq(), m, nil, c) -} - // ProfilerStop ... type ProfilerStop struct { } @@ -278,18 +236,6 @@ func (m ProfilerStopPreciseCoverage) Call(c Client) error { return call(m.ProtoReq(), m, nil, c) } -// ProfilerStopTypeProfile (experimental) Disable type profile. Disabling releases type profile data collected so far. -type ProfilerStopTypeProfile struct { -} - -// ProtoReq name -func (m ProfilerStopTypeProfile) ProtoReq() string { return "Profiler.stopTypeProfile" } - -// Call sends the request -func (m ProfilerStopTypeProfile) Call(c Client) error { - return call(m.ProtoReq(), m, nil, c) -} - // ProfilerTakePreciseCoverage Collect coverage data for the current isolate, and resets execution counters. Precise code // coverage needs to have started. type ProfilerTakePreciseCoverage struct { @@ -314,26 +260,6 @@ type ProfilerTakePreciseCoverageResult struct { Timestamp float64 `json:"timestamp"` } -// ProfilerTakeTypeProfile (experimental) Collect type profile. -type ProfilerTakeTypeProfile struct { -} - -// ProtoReq name -func (m ProfilerTakeTypeProfile) ProtoReq() string { return "Profiler.takeTypeProfile" } - -// Call the request -func (m ProfilerTakeTypeProfile) Call(c Client) (*ProfilerTakeTypeProfileResult, error) { - var res ProfilerTakeTypeProfileResult - return &res, call(m.ProtoReq(), m, &res, c) -} - -// ProfilerTakeTypeProfileResult (experimental) ... -type ProfilerTakeTypeProfileResult struct { - - // Result Type profile for all scripts since startTypeProfile() was turned on. - Result []*ProfilerScriptTypeProfile `json:"result"` -} - // ProfilerConsoleProfileFinished ... type ProfilerConsoleProfileFinished struct { diff --git a/vendor/github.com/go-rod/rod/lib/proto/storage.go b/vendor/github.com/go-rod/rod/lib/proto/storage.go index 0e1455a3..73766c8f 100644 --- a/vendor/github.com/go-rod/rod/lib/proto/storage.go +++ b/vendor/github.com/go-rod/rod/lib/proto/storage.go @@ -45,6 +45,9 @@ const ( // StorageStorageTypeInterestGroups enum const StorageStorageTypeInterestGroups StorageStorageType = "interest_groups" + // StorageStorageTypeSharedStorage enum const + StorageStorageTypeSharedStorage StorageStorageType = "shared_storage" + // StorageStorageTypeAll enum const StorageStorageTypeAll StorageStorageType = "all" @@ -143,6 +146,147 @@ type StorageInterestGroupDetails struct { AdComponents []*StorageInterestGroupAd `json:"adComponents"` } +// StorageSharedStorageAccessType Enum of shared storage access types. +type StorageSharedStorageAccessType string + +const ( + // StorageSharedStorageAccessTypeDocumentAddModule enum const + StorageSharedStorageAccessTypeDocumentAddModule StorageSharedStorageAccessType = "documentAddModule" + + // StorageSharedStorageAccessTypeDocumentSelectURL enum const + StorageSharedStorageAccessTypeDocumentSelectURL StorageSharedStorageAccessType = "documentSelectURL" + + // StorageSharedStorageAccessTypeDocumentRun enum const + StorageSharedStorageAccessTypeDocumentRun StorageSharedStorageAccessType = "documentRun" + + // StorageSharedStorageAccessTypeDocumentSet enum const + StorageSharedStorageAccessTypeDocumentSet StorageSharedStorageAccessType = "documentSet" + + // StorageSharedStorageAccessTypeDocumentAppend enum const + StorageSharedStorageAccessTypeDocumentAppend StorageSharedStorageAccessType = "documentAppend" + + // StorageSharedStorageAccessTypeDocumentDelete enum const + StorageSharedStorageAccessTypeDocumentDelete StorageSharedStorageAccessType = "documentDelete" + + // StorageSharedStorageAccessTypeDocumentClear enum const + StorageSharedStorageAccessTypeDocumentClear StorageSharedStorageAccessType = "documentClear" + + // StorageSharedStorageAccessTypeWorkletSet enum const + StorageSharedStorageAccessTypeWorkletSet StorageSharedStorageAccessType = "workletSet" + + // StorageSharedStorageAccessTypeWorkletAppend enum const + StorageSharedStorageAccessTypeWorkletAppend StorageSharedStorageAccessType = "workletAppend" + + // StorageSharedStorageAccessTypeWorkletDelete enum const + StorageSharedStorageAccessTypeWorkletDelete StorageSharedStorageAccessType = "workletDelete" + + // StorageSharedStorageAccessTypeWorkletClear enum const + StorageSharedStorageAccessTypeWorkletClear StorageSharedStorageAccessType = "workletClear" + + // StorageSharedStorageAccessTypeWorkletGet enum const + StorageSharedStorageAccessTypeWorkletGet StorageSharedStorageAccessType = "workletGet" + + // StorageSharedStorageAccessTypeWorkletKeys enum const + StorageSharedStorageAccessTypeWorkletKeys StorageSharedStorageAccessType = "workletKeys" + + // StorageSharedStorageAccessTypeWorkletEntries enum const + StorageSharedStorageAccessTypeWorkletEntries StorageSharedStorageAccessType = "workletEntries" + + // StorageSharedStorageAccessTypeWorkletLength enum const + StorageSharedStorageAccessTypeWorkletLength StorageSharedStorageAccessType = "workletLength" + + // StorageSharedStorageAccessTypeWorkletRemainingBudget enum const + StorageSharedStorageAccessTypeWorkletRemainingBudget StorageSharedStorageAccessType = "workletRemainingBudget" +) + +// StorageSharedStorageEntry Struct for a single key-value pair in an origin's shared storage. +type StorageSharedStorageEntry struct { + + // Key ... + Key string `json:"key"` + + // Value ... + Value string `json:"value"` +} + +// StorageSharedStorageMetadata Details for an origin's shared storage. +type StorageSharedStorageMetadata struct { + + // CreationTime ... + CreationTime TimeSinceEpoch `json:"creationTime"` + + // Length ... + Length int `json:"length"` + + // RemainingBudget ... + RemainingBudget float64 `json:"remainingBudget"` +} + +// StorageSharedStorageReportingMetadata Pair of reporting metadata details for a candidate URL for `selectURL()`. +type StorageSharedStorageReportingMetadata struct { + + // EventType ... + EventType string `json:"eventType"` + + // ReportingURL ... + ReportingURL string `json:"reportingUrl"` +} + +// StorageSharedStorageURLWithMetadata Bundles a candidate URL with its reporting metadata. +type StorageSharedStorageURLWithMetadata struct { + + // URL Spec of candidate URL. + URL string `json:"url"` + + // ReportingMetadata Any associated reporting metadata. + ReportingMetadata []*StorageSharedStorageReportingMetadata `json:"reportingMetadata"` +} + +// StorageSharedStorageAccessParams Bundles the parameters for shared storage access events whose +// presence/absence can vary according to SharedStorageAccessType. +type StorageSharedStorageAccessParams struct { + + // ScriptSourceURL (optional) Spec of the module script URL. + // Present only for SharedStorageAccessType.documentAddModule. + ScriptSourceURL string `json:"scriptSourceUrl,omitempty"` + + // OperationName (optional) Name of the registered operation to be run. + // Present only for SharedStorageAccessType.documentRun and + // SharedStorageAccessType.documentSelectURL. + OperationName string `json:"operationName,omitempty"` + + // SerializedData (optional) The operation's serialized data in bytes (converted to a string). + // Present only for SharedStorageAccessType.documentRun and + // SharedStorageAccessType.documentSelectURL. + SerializedData string `json:"serializedData,omitempty"` + + // UrlsWithMetadata (optional) Array of candidate URLs' specs, along with any associated metadata. + // Present only for SharedStorageAccessType.documentSelectURL. + UrlsWithMetadata []*StorageSharedStorageURLWithMetadata `json:"urlsWithMetadata,omitempty"` + + // Key (optional) Key for a specific entry in an origin's shared storage. + // Present only for SharedStorageAccessType.documentSet, + // SharedStorageAccessType.documentAppend, + // SharedStorageAccessType.documentDelete, + // SharedStorageAccessType.workletSet, + // SharedStorageAccessType.workletAppend, + // SharedStorageAccessType.workletDelete, and + // SharedStorageAccessType.workletGet. + Key string `json:"key,omitempty"` + + // Value (optional) Value for a specific entry in an origin's shared storage. + // Present only for SharedStorageAccessType.documentSet, + // SharedStorageAccessType.documentAppend, + // SharedStorageAccessType.workletSet, and + // SharedStorageAccessType.workletAppend. + Value string `json:"value,omitempty"` + + // IgnoreIfPresent (optional) Whether or not to set an entry for a key if that key is already present. + // Present only for SharedStorageAccessType.documentSet and + // SharedStorageAccessType.workletSet. + IgnoreIfPresent bool `json:"ignoreIfPresent,omitempty"` +} + // StorageGetStorageKeyForFrame Returns a storage key given a frame id. type StorageGetStorageKeyForFrame struct { @@ -500,6 +644,67 @@ func (m StorageSetInterestGroupTracking) Call(c Client) error { return call(m.ProtoReq(), m, nil, c) } +// StorageGetSharedStorageMetadata (experimental) Gets metadata for an origin's shared storage. +type StorageGetSharedStorageMetadata struct { + + // OwnerOrigin ... + OwnerOrigin string `json:"ownerOrigin"` +} + +// ProtoReq name +func (m StorageGetSharedStorageMetadata) ProtoReq() string { return "Storage.getSharedStorageMetadata" } + +// Call the request +func (m StorageGetSharedStorageMetadata) Call(c Client) (*StorageGetSharedStorageMetadataResult, error) { + var res StorageGetSharedStorageMetadataResult + return &res, call(m.ProtoReq(), m, &res, c) +} + +// StorageGetSharedStorageMetadataResult (experimental) ... +type StorageGetSharedStorageMetadataResult struct { + + // Metadata ... + Metadata *StorageSharedStorageMetadata `json:"metadata"` +} + +// StorageGetSharedStorageEntries (experimental) Gets the entries in an given origin's shared storage. +type StorageGetSharedStorageEntries struct { + + // OwnerOrigin ... + OwnerOrigin string `json:"ownerOrigin"` +} + +// ProtoReq name +func (m StorageGetSharedStorageEntries) ProtoReq() string { return "Storage.getSharedStorageEntries" } + +// Call the request +func (m StorageGetSharedStorageEntries) Call(c Client) (*StorageGetSharedStorageEntriesResult, error) { + var res StorageGetSharedStorageEntriesResult + return &res, call(m.ProtoReq(), m, &res, c) +} + +// StorageGetSharedStorageEntriesResult (experimental) ... +type StorageGetSharedStorageEntriesResult struct { + + // Entries ... + Entries []*StorageSharedStorageEntry `json:"entries"` +} + +// StorageSetSharedStorageTracking (experimental) Enables/disables issuing of sharedStorageAccessed events. +type StorageSetSharedStorageTracking struct { + + // Enable ... + Enable bool `json:"enable"` +} + +// ProtoReq name +func (m StorageSetSharedStorageTracking) ProtoReq() string { return "Storage.setSharedStorageTracking" } + +// Call sends the request +func (m StorageSetSharedStorageTracking) Call(c Client) error { + return call(m.ProtoReq(), m, nil, c) +} + // StorageCacheStorageContentUpdated A cache's contents have been modified. type StorageCacheStorageContentUpdated struct { @@ -583,3 +788,29 @@ type StorageInterestGroupAccessed struct { func (evt StorageInterestGroupAccessed) ProtoEvent() string { return "Storage.interestGroupAccessed" } + +// StorageSharedStorageAccessed Shared storage was accessed by the associated page. +// The following parameters are included in all events. +type StorageSharedStorageAccessed struct { + + // AccessTime Time of the access. + AccessTime TimeSinceEpoch `json:"accessTime"` + + // Type Enum value indicating the Shared Storage API method invoked. + Type StorageSharedStorageAccessType `json:"type"` + + // MainFrameID DevTools Frame Token for the primary frame tree's root. + MainFrameID PageFrameID `json:"mainFrameId"` + + // OwnerOrigin Serialized origin for the context that invoked the Shared Storage API. + OwnerOrigin string `json:"ownerOrigin"` + + // Params The sub-parameters warapped by `params` are all optional and their + // presence/absence depends on `type`. + Params *StorageSharedStorageAccessParams `json:"params"` +} + +// ProtoEvent name +func (evt StorageSharedStorageAccessed) ProtoEvent() string { + return "Storage.sharedStorageAccessed" +} diff --git a/vendor/github.com/go-rod/rod/lib/proto/target.go b/vendor/github.com/go-rod/rod/lib/proto/target.go index 2b1aebae..4509fc71 100644 --- a/vendor/github.com/go-rod/rod/lib/proto/target.go +++ b/vendor/github.com/go-rod/rod/lib/proto/target.go @@ -68,6 +68,10 @@ type TargetTargetInfo struct { // BrowserContextID (experimental) (optional) ... BrowserContextID BrowserBrowserContextID `json:"browserContextId,omitempty"` + + // Subtype (experimental) (optional) Provides additional details for specific target types. For example, for + // the type of "page", this may be set to "portal" or "prerender". + Subtype string `json:"subtype,omitempty"` } // TargetFilterEntry (experimental) A filter used by target query/discovery/auto-attach operations. diff --git a/vendor/github.com/go-rod/rod/lib/utils/sleeper.go b/vendor/github.com/go-rod/rod/lib/utils/sleeper.go index dd4976cf..7423ba60 100644 --- a/vendor/github.com/go-rod/rod/lib/utils/sleeper.go +++ b/vendor/github.com/go-rod/rod/lib/utils/sleeper.go @@ -14,7 +14,7 @@ func Sleep(seconds float64) { time.Sleep(d) } -// Sleeper sleeps the current gouroutine for sometime, returns the reason to wake, if ctx is done release resource +// Sleeper sleeps the current goroutine for sometime, returns the reason to wake, if ctx is done release resource type Sleeper func(context.Context) error // ErrMaxSleepCount type diff --git a/vendor/github.com/go-rod/rod/lib/utils/utils.go b/vendor/github.com/go-rod/rod/lib/utils/utils.go index 1bd91d80..91936ac1 100644 --- a/vendor/github.com/go-rod/rod/lib/utils/utils.go +++ b/vendor/github.com/go-rod/rod/lib/utils/utils.go @@ -27,6 +27,11 @@ import ( "github.com/ysmood/gson" ) +// TestEnvs for testing +var TestEnvs = map[string]string{ + "GODEBUG": "tracebackancestors=100", +} + // InContainer will be true if is inside container environment, such as docker var InContainer = FileExists("/.dockerenv") || FileExists("/.containerenv") @@ -274,7 +279,7 @@ func Exec(line string, rest ...string) string { return ExecLine(true, line, rest...) } -var execLogger = log.New(os.Stdout, "[exec]", log.LstdFlags) +var execLogger = log.New(os.Stdout, "[exec] ", 0) // ExecLine of command func ExecLine(std bool, line string, rest ...string) string { @@ -298,6 +303,9 @@ func ExecLine(std bool, line string, rest ...string) string { } if err := cmd.Run(); err != nil { + if std { + panic(err) + } panic(fmt.Sprintf("%v\n%v", err, buf.String())) } diff --git a/vendor/github.com/go-rod/rod/page.go b/vendor/github.com/go-rod/rod/page.go index d5d399f6..9eb052b2 100644 --- a/vendor/github.com/go-rod/rod/page.go +++ b/vendor/github.com/go-rod/rod/page.go @@ -301,7 +301,7 @@ func (p *Page) Close() error { for { err := proto.PageClose{}.Call(p) if errors.Is(err, cdp.ErrNotAttachedToActivePage) { - // TODO: I don't know why chromium doesn't allow to close a page while it's navigating. + // TODO: I don't know why chromium doesn't allow us to close a page while it's navigating. // Looks like a bug in chromium. utils.Sleep(0.1) continue @@ -390,11 +390,11 @@ func (p *Page) HandleFileDialog() (func([]string) error, error) { } // Screenshot captures the screenshot of current page. -func (p *Page) Screenshot(fullpage bool, req *proto.PageCaptureScreenshot) ([]byte, error) { +func (p *Page) Screenshot(fullPage bool, req *proto.PageCaptureScreenshot) ([]byte, error) { if req == nil { req = &proto.PageCaptureScreenshot{} } - if fullpage { + if fullPage { metrics, err := proto.PageGetLayoutMetrics{}.Call(p) if err != nil { return nil, err @@ -536,15 +536,15 @@ func (p *Page) WaitRequestIdle(d time.Duration, includes, excludes []string) fun p, cancel := p.WithCancel() match := genRegMatcher(includes, excludes) - waitlist := map[proto.NetworkRequestID]string{} + waitList := map[proto.NetworkRequestID]string{} idleCounter := utils.NewIdleCounter(d) update := p.tryTraceReq(includes, excludes) update(nil) checkDone := func(id proto.NetworkRequestID) { - if _, has := waitlist[id]; has { - delete(waitlist, id) - update(waitlist) + if _, has := waitList[id]; has { + delete(waitList, id) + update(waitList) idleCounter.Done() } } @@ -553,9 +553,9 @@ func (p *Page) WaitRequestIdle(d time.Duration, includes, excludes []string) fun if match(sent.Request.URL) { // Redirect will send multiple NetworkRequestWillBeSent events with the same RequestID, // we should filter them out. - if _, has := waitlist[sent.RequestID]; !has { - waitlist[sent.RequestID] = sent.Request.URL - update(waitlist) + if _, has := waitList[sent.RequestID]; !has { + waitList[sent.RequestID] = sent.Request.URL + update(waitList) idleCounter.Add() } } diff --git a/vendor/github.com/go-rod/rod/page_eval.go b/vendor/github.com/go-rod/rod/page_eval.go index 23d52e9f..e20d6b96 100644 --- a/vendor/github.com/go-rod/rod/page_eval.go +++ b/vendor/github.com/go-rod/rod/page_eval.go @@ -225,22 +225,22 @@ func (p *Page) Expose(name string, fn func(gson.JSON) (interface{}, error)) (sto } func (p *Page) formatArgs(opts *EvalOptions) ([]*proto.RuntimeCallArgument, error) { - formated := []*proto.RuntimeCallArgument{} + formatted := []*proto.RuntimeCallArgument{} for _, arg := range opts.JSArgs { if obj, ok := arg.(*proto.RuntimeRemoteObject); ok { // remote object - formated = append(formated, &proto.RuntimeCallArgument{ObjectID: obj.ObjectID}) + formatted = append(formatted, &proto.RuntimeCallArgument{ObjectID: obj.ObjectID}) } else if obj, ok := arg.(*js.Function); ok { // js helper id, err := p.ensureJSHelper(obj) if err != nil { return nil, err } - formated = append(formated, &proto.RuntimeCallArgument{ObjectID: id}) + formatted = append(formatted, &proto.RuntimeCallArgument{ObjectID: id}) } else { // plain json data - formated = append(formated, &proto.RuntimeCallArgument{Value: gson.New(arg)}) + formatted = append(formatted, &proto.RuntimeCallArgument{Value: gson.New(arg)}) } } - return formated, nil + return formatted, nil } // Check the doc of EvalHelper diff --git a/vendor/github.com/inconshreveable/mousetrap/trap_others.go b/vendor/github.com/inconshreveable/mousetrap/trap_others.go index 9d2d8a4b..06a91f08 100644 --- a/vendor/github.com/inconshreveable/mousetrap/trap_others.go +++ b/vendor/github.com/inconshreveable/mousetrap/trap_others.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package mousetrap diff --git a/vendor/github.com/inconshreveable/mousetrap/trap_windows.go b/vendor/github.com/inconshreveable/mousetrap/trap_windows.go index 336142a5..0c568802 100644 --- a/vendor/github.com/inconshreveable/mousetrap/trap_windows.go +++ b/vendor/github.com/inconshreveable/mousetrap/trap_windows.go @@ -1,81 +1,32 @@ -// +build windows -// +build !go1.4 - package mousetrap import ( - "fmt" - "os" "syscall" "unsafe" ) -const ( - // defined by the Win32 API - th32cs_snapprocess uintptr = 0x2 -) - -var ( - kernel = syscall.MustLoadDLL("kernel32.dll") - CreateToolhelp32Snapshot = kernel.MustFindProc("CreateToolhelp32Snapshot") - Process32First = kernel.MustFindProc("Process32FirstW") - Process32Next = kernel.MustFindProc("Process32NextW") -) - -// ProcessEntry32 structure defined by the Win32 API -type processEntry32 struct { - dwSize uint32 - cntUsage uint32 - th32ProcessID uint32 - th32DefaultHeapID int - th32ModuleID uint32 - cntThreads uint32 - th32ParentProcessID uint32 - pcPriClassBase int32 - dwFlags uint32 - szExeFile [syscall.MAX_PATH]uint16 -} - -func getProcessEntry(pid int) (pe *processEntry32, err error) { - snapshot, _, e1 := CreateToolhelp32Snapshot.Call(th32cs_snapprocess, uintptr(0)) - if snapshot == uintptr(syscall.InvalidHandle) { - err = fmt.Errorf("CreateToolhelp32Snapshot: %v", e1) - return +func getProcessEntry(pid int) (*syscall.ProcessEntry32, error) { + snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0) + if err != nil { + return nil, err } - defer syscall.CloseHandle(syscall.Handle(snapshot)) - - var processEntry processEntry32 - processEntry.dwSize = uint32(unsafe.Sizeof(processEntry)) - ok, _, e1 := Process32First.Call(snapshot, uintptr(unsafe.Pointer(&processEntry))) - if ok == 0 { - err = fmt.Errorf("Process32First: %v", e1) - return + defer syscall.CloseHandle(snapshot) + var procEntry syscall.ProcessEntry32 + procEntry.Size = uint32(unsafe.Sizeof(procEntry)) + if err = syscall.Process32First(snapshot, &procEntry); err != nil { + return nil, err } - for { - if processEntry.th32ProcessID == uint32(pid) { - pe = &processEntry - return + if procEntry.ProcessID == uint32(pid) { + return &procEntry, nil } - - ok, _, e1 = Process32Next.Call(snapshot, uintptr(unsafe.Pointer(&processEntry))) - if ok == 0 { - err = fmt.Errorf("Process32Next: %v", e1) - return + err = syscall.Process32Next(snapshot, &procEntry) + if err != nil { + return nil, err } } } -func getppid() (pid int, err error) { - pe, err := getProcessEntry(os.Getpid()) - if err != nil { - return - } - - pid = int(pe.th32ParentProcessID) - return -} - // StartedByExplorer returns true if the program was invoked by the user double-clicking // on the executable from explorer.exe // @@ -83,16 +34,9 @@ func getppid() (pid int, err error) { // It does not guarantee that the program was run from a terminal. It only can tell you // whether it was launched from explorer.exe func StartedByExplorer() bool { - ppid, err := getppid() + pe, err := getProcessEntry(syscall.Getppid()) if err != nil { return false } - - pe, err := getProcessEntry(ppid) - if err != nil { - return false - } - - name := syscall.UTF16ToString(pe.szExeFile[:]) - return name == "explorer.exe" + return "explorer.exe" == syscall.UTF16ToString(pe.ExeFile[:]) } diff --git a/vendor/github.com/inconshreveable/mousetrap/trap_windows_1.4.go b/vendor/github.com/inconshreveable/mousetrap/trap_windows_1.4.go deleted file mode 100644 index 9a28e57c..00000000 --- a/vendor/github.com/inconshreveable/mousetrap/trap_windows_1.4.go +++ /dev/null @@ -1,46 +0,0 @@ -// +build windows -// +build go1.4 - -package mousetrap - -import ( - "os" - "syscall" - "unsafe" -) - -func getProcessEntry(pid int) (*syscall.ProcessEntry32, error) { - snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0) - if err != nil { - return nil, err - } - defer syscall.CloseHandle(snapshot) - var procEntry syscall.ProcessEntry32 - procEntry.Size = uint32(unsafe.Sizeof(procEntry)) - if err = syscall.Process32First(snapshot, &procEntry); err != nil { - return nil, err - } - for { - if procEntry.ProcessID == uint32(pid) { - return &procEntry, nil - } - err = syscall.Process32Next(snapshot, &procEntry) - if err != nil { - return nil, err - } - } -} - -// StartedByExplorer returns true if the program was invoked by the user double-clicking -// on the executable from explorer.exe -// -// It is conservative and returns false if any of the internal calls fail. -// It does not guarantee that the program was run from a terminal. It only can tell you -// whether it was launched from explorer.exe -func StartedByExplorer() bool { - pe, err := getProcessEntry(os.Getppid()) - if err != nil { - return false - } - return "explorer.exe" == syscall.UTF16ToString(pe.ExeFile[:]) -} diff --git a/vendor/github.com/invopop/jsonschema/reflect.go b/vendor/github.com/invopop/jsonschema/reflect.go index 1b6732d2..6ebc6be5 100644 --- a/vendor/github.com/invopop/jsonschema/reflect.go +++ b/vendor/github.com/invopop/jsonschema/reflect.go @@ -108,7 +108,14 @@ type customSchemaImpl interface { JSONSchema() *Schema } +// Function to be run after the schema has been generated. +// this will let you modify a schema afterwards +type extendSchemaImpl interface { + JSONSchemaExtend(*Schema) +} + var customType = reflect.TypeOf((*customSchemaImpl)(nil)).Elem() +var extendType = reflect.TypeOf((*extendSchemaImpl)(nil)).Elem() // customSchemaGetFieldDocString type customSchemaGetFieldDocString interface { @@ -395,6 +402,8 @@ func (r *Reflector) reflectTypeToSchema(definitions Definitions, t reflect.Type) panic("unsupported type " + t.String()) } + r.reflectSchemaExtend(definitions, t, st) + // Always try to reference the definition which may have just been created if def := r.refDefinition(definitions, t); def != nil { return def @@ -422,6 +431,19 @@ func (r *Reflector) reflectCustomSchema(definitions Definitions, t reflect.Type) return nil } +func (r *Reflector) reflectSchemaExtend(definitions Definitions, t reflect.Type, s *Schema) *Schema { + if t.Implements(extendType) { + v := reflect.New(t) + o := v.Interface().(extendSchemaImpl) + o.JSONSchemaExtend(s) + if ref := r.refDefinition(definitions, t); ref != nil { + return ref + } + } + + return s +} + func (r *Reflector) reflectSliceOrArray(definitions Definitions, t reflect.Type, st *Schema) { if t == rawMessageType { return @@ -683,6 +705,21 @@ func (t *Schema) genericKeywords(tags []string, parent *Schema, propertyName str parent.OneOf = append(parent.OneOf, typeFound) } typeFound.Required = append(typeFound.Required, propertyName) + case "anyof_required": + var typeFound *Schema + for i := range parent.AnyOf { + if parent.AnyOf[i].Title == nameValue[1] { + typeFound = parent.AnyOf[i] + } + } + if typeFound == nil { + typeFound = &Schema{ + Title: nameValue[1], + Required: []string{}, + } + parent.AnyOf = append(parent.AnyOf, typeFound) + } + typeFound.Required = append(typeFound.Required, propertyName) case "oneof_type": if t.OneOf == nil { t.OneOf = make([]*Schema, 0, 1) @@ -694,6 +731,17 @@ func (t *Schema) genericKeywords(tags []string, parent *Schema, propertyName str Type: ty, }) } + case "anyof_type": + if t.AnyOf == nil { + t.AnyOf = make([]*Schema, 0, 1) + } + t.Type = "" + types := strings.Split(nameValue[1], ";") + for _, ty := range types { + t.AnyOf = append(t.AnyOf, &Schema{ + Type: ty, + }) + } case "enum": switch t.Type { case "string": @@ -855,7 +903,7 @@ func (t *Schema) arrayKeywords(tags []string) { func (t *Schema) extraKeywords(tags []string) { for _, tag := range tags { - nameValue := strings.Split(tag, "=") + nameValue := strings.SplitN(tag, "=", 2) if len(nameValue) == 2 { t.setExtra(nameValue[0], nameValue[1]) } @@ -874,13 +922,23 @@ func (t *Schema) setExtra(key, val string) { t.Extras[key] = append(existingVal, val) case int: t.Extras[key], _ = strconv.Atoi(val) + case bool: + t.Extras[key] = (val == "true" || val == "t") } } else { switch key { case "minimum": t.Extras[key], _ = strconv.Atoi(val) default: - t.Extras[key] = val + var x interface{} + if val == "true" { + x = true + } else if val == "false" { + x = false + } else { + x = val + } + t.Extras[key] = x } } } @@ -955,6 +1013,11 @@ func (r *Reflector) reflectFieldName(f reflect.StructField) (string, bool, bool, if f.Type.Kind() == reflect.Struct { return "", true, false, false } + + // As per JSON Marshal rules, anonymous pointer to structs are inherited + if f.Type.Kind() == reflect.Ptr && f.Type.Elem().Kind() == reflect.Struct { + return "", true, false, false + } } // Try to determine the name from the different combos diff --git a/vendor/github.com/magiconair/properties/.travis.yml b/vendor/github.com/magiconair/properties/.travis.yml deleted file mode 100644 index baf9031d..00000000 --- a/vendor/github.com/magiconair/properties/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -language: go -go: - - 1.3.x - - 1.4.x - - 1.5.x - - 1.6.x - - 1.7.x - - 1.8.x - - 1.9.x - - "1.10.x" - - "1.11.x" - - "1.12.x" - - "1.13.x" - - "1.14.x" - - "1.15.x" - - "1.16.x" - - tip diff --git a/vendor/github.com/magiconair/properties/CHANGELOG.md b/vendor/github.com/magiconair/properties/CHANGELOG.md index ff8d0253..842e8e24 100644 --- a/vendor/github.com/magiconair/properties/CHANGELOG.md +++ b/vendor/github.com/magiconair/properties/CHANGELOG.md @@ -1,5 +1,50 @@ ## Changelog +### [1.8.7](https://github.com/magiconair/properties/tree/v1.8.7) - 08 Dec 2022 + + * [PR #65](https://github.com/magiconair/properties/pull/65): Speedup Merge + + Thanks to [@AdityaVallabh](https://github.com/AdityaVallabh) for the patch. + + * [PR #66](https://github.com/magiconair/properties/pull/66): use github actions + +### [1.8.6](https://github.com/magiconair/properties/tree/v1.8.6) - 23 Feb 2022 + + * [PR #57](https://github.com/magiconair/properties/pull/57):Fix "unreachable code" lint error + + Thanks to [@ellie](https://github.com/ellie) for the patch. + + * [PR #63](https://github.com/magiconair/properties/pull/63): Make TestMustGetParsedDuration backwards compatible + + This patch ensures that the `TestMustGetParsedDuration` still works with `go1.3` to make the + author happy until it affects real users. + + Thanks to [@maage](https://github.com/maage) for the patch. + +### [1.8.5](https://github.com/magiconair/properties/tree/v1.8.5) - 24 Mar 2021 + + * [PR #55](https://github.com/magiconair/properties/pull/55): Fix: Encoding Bug in Comments + + When reading comments \ are loaded correctly, but when writing they are then + replaced by \\. This leads to wrong comments when writing and reading multiple times. + + Thanks to [@doxsch](https://github.com/doxsch) for the patch. + +### [1.8.4](https://github.com/magiconair/properties/tree/v1.8.4) - 23 Sep 2020 + + * [PR #50](https://github.com/magiconair/properties/pull/50): enhance error message for circular references + + Thanks to [@sriv](https://github.com/sriv) for the patch. + +### [1.8.3](https://github.com/magiconair/properties/tree/v1.8.3) - 14 Sep 2020 + + * [PR #49](https://github.com/magiconair/properties/pull/49): Include the key in error message causing the circular reference + + The change is include the key in the error message which is causing the circular + reference when parsing/loading the properties files. + + Thanks to [@haroon-sheikh](https://github.com/haroon-sheikh) for the patch. + ### [1.8.2](https://github.com/magiconair/properties/tree/v1.8.2) - 25 Aug 2020 * [PR #36](https://github.com/magiconair/properties/pull/36): Escape backslash on write diff --git a/vendor/github.com/magiconair/properties/decode.go b/vendor/github.com/magiconair/properties/decode.go index 3ebf8049..8e6aa441 100644 --- a/vendor/github.com/magiconair/properties/decode.go +++ b/vendor/github.com/magiconair/properties/decode.go @@ -1,4 +1,4 @@ -// Copyright 2018 Frank Schroeder. All rights reserved. +// Copyright 2013-2022 Frank Schroeder. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -48,49 +48,49 @@ import ( // // Examples: // -// // Field is ignored. -// Field int `properties:"-"` +// // Field is ignored. +// Field int `properties:"-"` // -// // Field is assigned value of 'Field'. -// Field int +// // Field is assigned value of 'Field'. +// Field int // -// // Field is assigned value of 'myName'. -// Field int `properties:"myName"` +// // Field is assigned value of 'myName'. +// Field int `properties:"myName"` // -// // Field is assigned value of key 'myName' and has a default -// // value 15 if the key does not exist. -// Field int `properties:"myName,default=15"` +// // Field is assigned value of key 'myName' and has a default +// // value 15 if the key does not exist. +// Field int `properties:"myName,default=15"` // -// // Field is assigned value of key 'Field' and has a default -// // value 15 if the key does not exist. -// Field int `properties:",default=15"` +// // Field is assigned value of key 'Field' and has a default +// // value 15 if the key does not exist. +// Field int `properties:",default=15"` // -// // Field is assigned value of key 'date' and the date -// // is in format 2006-01-02 -// Field time.Time `properties:"date,layout=2006-01-02"` +// // Field is assigned value of key 'date' and the date +// // is in format 2006-01-02 +// Field time.Time `properties:"date,layout=2006-01-02"` // -// // Field is assigned the non-empty and whitespace trimmed -// // values of key 'Field' split by commas. -// Field []string +// // Field is assigned the non-empty and whitespace trimmed +// // values of key 'Field' split by commas. +// Field []string // -// // Field is assigned the non-empty and whitespace trimmed -// // values of key 'Field' split by commas and has a default -// // value ["a", "b", "c"] if the key does not exist. -// Field []string `properties:",default=a;b;c"` +// // Field is assigned the non-empty and whitespace trimmed +// // values of key 'Field' split by commas and has a default +// // value ["a", "b", "c"] if the key does not exist. +// Field []string `properties:",default=a;b;c"` // -// // Field is decoded recursively with "Field." as key prefix. -// Field SomeStruct +// // Field is decoded recursively with "Field." as key prefix. +// Field SomeStruct // -// // Field is decoded recursively with "myName." as key prefix. -// Field SomeStruct `properties:"myName"` +// // Field is decoded recursively with "myName." as key prefix. +// Field SomeStruct `properties:"myName"` // -// // Field is decoded recursively with "Field." as key prefix -// // and the next dotted element of the key as map key. -// Field map[string]string +// // Field is decoded recursively with "Field." as key prefix +// // and the next dotted element of the key as map key. +// Field map[string]string // -// // Field is decoded recursively with "myName." as key prefix -// // and the next dotted element of the key as map key. -// Field map[string]string `properties:"myName"` +// // Field is decoded recursively with "myName." as key prefix +// // and the next dotted element of the key as map key. +// Field map[string]string `properties:"myName"` func (p *Properties) Decode(x interface{}) error { t, v := reflect.TypeOf(x), reflect.ValueOf(x) if t.Kind() != reflect.Ptr || v.Elem().Type().Kind() != reflect.Struct { diff --git a/vendor/github.com/magiconair/properties/doc.go b/vendor/github.com/magiconair/properties/doc.go index f8822da2..7c797931 100644 --- a/vendor/github.com/magiconair/properties/doc.go +++ b/vendor/github.com/magiconair/properties/doc.go @@ -1,4 +1,4 @@ -// Copyright 2018 Frank Schroeder. All rights reserved. +// Copyright 2013-2022 Frank Schroeder. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -13,7 +13,7 @@ // // To load a single properties file use MustLoadFile(): // -// p := properties.MustLoadFile(filename, properties.UTF8) +// p := properties.MustLoadFile(filename, properties.UTF8) // // To load multiple properties files use MustLoadFiles() // which loads the files in the given order and merges the @@ -23,25 +23,25 @@ // Filenames can contain environment variables which are expanded // before loading. // -// f1 := "/etc/myapp/myapp.conf" -// f2 := "/home/${USER}/myapp.conf" -// p := MustLoadFiles([]string{f1, f2}, properties.UTF8, true) +// f1 := "/etc/myapp/myapp.conf" +// f2 := "/home/${USER}/myapp.conf" +// p := MustLoadFiles([]string{f1, f2}, properties.UTF8, true) // // All of the different key/value delimiters ' ', ':' and '=' are // supported as well as the comment characters '!' and '#' and // multi-line values. // -// ! this is a comment -// # and so is this +// ! this is a comment +// # and so is this // -// # the following expressions are equal -// key value -// key=value -// key:value -// key = value -// key : value -// key = val\ -// ue +// # the following expressions are equal +// key value +// key=value +// key:value +// key = value +// key : value +// key = val\ +// ue // // Properties stores all comments preceding a key and provides // GetComments() and SetComments() methods to retrieve and @@ -55,62 +55,62 @@ // and malformed expressions are not allowed and cause an // error. Expansion of environment variables is supported. // -// # standard property -// key = value +// # standard property +// key = value // -// # property expansion: key2 = value -// key2 = ${key} +// # property expansion: key2 = value +// key2 = ${key} // -// # recursive expansion: key3 = value -// key3 = ${key2} +// # recursive expansion: key3 = value +// key3 = ${key2} // -// # circular reference (error) -// key = ${key} +// # circular reference (error) +// key = ${key} // -// # malformed expression (error) -// key = ${ke +// # malformed expression (error) +// key = ${ke // -// # refers to the users' home dir -// home = ${HOME} +// # refers to the users' home dir +// home = ${HOME} // -// # local key takes precedence over env var: u = foo -// USER = foo -// u = ${USER} +// # local key takes precedence over env var: u = foo +// USER = foo +// u = ${USER} // // The default property expansion format is ${key} but can be // changed by setting different pre- and postfix values on the // Properties object. // -// p := properties.NewProperties() -// p.Prefix = "#[" -// p.Postfix = "]#" +// p := properties.NewProperties() +// p.Prefix = "#[" +// p.Postfix = "]#" // // Properties provides convenience functions for getting typed // values with default values if the key does not exist or the // type conversion failed. // -// # Returns true if the value is either "1", "on", "yes" or "true" -// # Returns false for every other value and the default value if -// # the key does not exist. -// v = p.GetBool("key", false) +// # Returns true if the value is either "1", "on", "yes" or "true" +// # Returns false for every other value and the default value if +// # the key does not exist. +// v = p.GetBool("key", false) // -// # Returns the value if the key exists and the format conversion -// # was successful. Otherwise, the default value is returned. -// v = p.GetInt64("key", 999) -// v = p.GetUint64("key", 999) -// v = p.GetFloat64("key", 123.0) -// v = p.GetString("key", "def") -// v = p.GetDuration("key", 999) +// # Returns the value if the key exists and the format conversion +// # was successful. Otherwise, the default value is returned. +// v = p.GetInt64("key", 999) +// v = p.GetUint64("key", 999) +// v = p.GetFloat64("key", 123.0) +// v = p.GetString("key", "def") +// v = p.GetDuration("key", 999) // // As an alternative properties may be applied with the standard // library's flag implementation at any time. // -// # Standard configuration -// v = flag.Int("key", 999, "help message") -// flag.Parse() +// # Standard configuration +// v = flag.Int("key", 999, "help message") +// flag.Parse() // -// # Merge p into the flag set -// p.MustFlag(flag.CommandLine) +// # Merge p into the flag set +// p.MustFlag(flag.CommandLine) // // Properties provides several MustXXX() convenience functions // which will terminate the app if an error occurs. The behavior @@ -119,30 +119,30 @@ // of logging the error set a different ErrorHandler before // you use the Properties package. // -// properties.ErrorHandler = properties.PanicHandler +// properties.ErrorHandler = properties.PanicHandler // -// # Will panic instead of logging an error -// p := properties.MustLoadFile("config.properties") +// # Will panic instead of logging an error +// p := properties.MustLoadFile("config.properties") // // You can also provide your own ErrorHandler function. The only requirement // is that the error handler function must exit after handling the error. // -// properties.ErrorHandler = func(err error) { -// fmt.Println(err) -// os.Exit(1) -// } +// properties.ErrorHandler = func(err error) { +// fmt.Println(err) +// os.Exit(1) +// } // -// # Will write to stdout and then exit -// p := properties.MustLoadFile("config.properties") +// # Will write to stdout and then exit +// p := properties.MustLoadFile("config.properties") // // Properties can also be loaded into a struct via the `Decode` // method, e.g. // -// type S struct { -// A string `properties:"a,default=foo"` -// D time.Duration `properties:"timeout,default=5s"` -// E time.Time `properties:"expires,layout=2006-01-02,default=2015-01-01"` -// } +// type S struct { +// A string `properties:"a,default=foo"` +// D time.Duration `properties:"timeout,default=5s"` +// E time.Time `properties:"expires,layout=2006-01-02,default=2015-01-01"` +// } // // See `Decode()` method for the full documentation. // @@ -152,5 +152,4 @@ // http://en.wikipedia.org/wiki/.properties // // http://docs.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29 -// package properties diff --git a/vendor/github.com/magiconair/properties/integrate.go b/vendor/github.com/magiconair/properties/integrate.go index 74d38dc6..35d0ae97 100644 --- a/vendor/github.com/magiconair/properties/integrate.go +++ b/vendor/github.com/magiconair/properties/integrate.go @@ -1,4 +1,4 @@ -// Copyright 2018 Frank Schroeder. All rights reserved. +// Copyright 2013-2022 Frank Schroeder. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -10,8 +10,9 @@ import "flag" // the respective key for flag.Flag.Name. // // It's use is recommended with command line arguments as in: -// flag.Parse() -// p.MustFlag(flag.CommandLine) +// +// flag.Parse() +// p.MustFlag(flag.CommandLine) func (p *Properties) MustFlag(dst *flag.FlagSet) { m := make(map[string]*flag.Flag) dst.VisitAll(func(f *flag.Flag) { diff --git a/vendor/github.com/magiconair/properties/lex.go b/vendor/github.com/magiconair/properties/lex.go index e1e9dd7b..3d15a1f6 100644 --- a/vendor/github.com/magiconair/properties/lex.go +++ b/vendor/github.com/magiconair/properties/lex.go @@ -1,4 +1,4 @@ -// Copyright 2018 Frank Schroeder. All rights reserved. +// Copyright 2013-2022 Frank Schroeder. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/vendor/github.com/magiconair/properties/load.go b/vendor/github.com/magiconair/properties/load.go index c83c2dad..635368dc 100644 --- a/vendor/github.com/magiconair/properties/load.go +++ b/vendor/github.com/magiconair/properties/load.go @@ -1,4 +1,4 @@ -// Copyright 2018 Frank Schroeder. All rights reserved. +// Copyright 2013-2022 Frank Schroeder. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/vendor/github.com/magiconair/properties/parser.go b/vendor/github.com/magiconair/properties/parser.go index 430e4fcd..fccfd39f 100644 --- a/vendor/github.com/magiconair/properties/parser.go +++ b/vendor/github.com/magiconair/properties/parser.go @@ -1,4 +1,4 @@ -// Copyright 2018 Frank Schroeder. All rights reserved. +// Copyright 2013-2022 Frank Schroeder. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/vendor/github.com/magiconair/properties/properties.go b/vendor/github.com/magiconair/properties/properties.go index 62ae2d67..fb2f7b40 100644 --- a/vendor/github.com/magiconair/properties/properties.go +++ b/vendor/github.com/magiconair/properties/properties.go @@ -1,4 +1,4 @@ -// Copyright 2018 Frank Schroeder. All rights reserved. +// Copyright 2013-2022 Frank Schroeder. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -700,22 +700,17 @@ func (p *Properties) Delete(key string) { // Merge merges properties, comments and keys from other *Properties into p func (p *Properties) Merge(other *Properties) { + for _, k := range other.k { + if _, ok := p.m[k]; !ok { + p.k = append(p.k, k) + } + } for k, v := range other.m { p.m[k] = v } for k, v := range other.c { p.c[k] = v } - -outer: - for _, otherKey := range other.k { - for _, key := range p.k { - if otherKey == key { - continue outer - } - } - p.k = append(p.k, otherKey) - } } // ---------------------------------------------------------------------------- diff --git a/vendor/github.com/magiconair/properties/rangecheck.go b/vendor/github.com/magiconair/properties/rangecheck.go index b013a2e5..dbd60b36 100644 --- a/vendor/github.com/magiconair/properties/rangecheck.go +++ b/vendor/github.com/magiconair/properties/rangecheck.go @@ -1,4 +1,4 @@ -// Copyright 2018 Frank Schroeder. All rights reserved. +// Copyright 2013-2022 Frank Schroeder. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/vendor/github.com/pelletier/go-toml/v2/README.md b/vendor/github.com/pelletier/go-toml/v2/README.md index a63c3a79..9f8439cc 100644 --- a/vendor/github.com/pelletier/go-toml/v2/README.md +++ b/vendor/github.com/pelletier/go-toml/v2/README.md @@ -140,6 +140,17 @@ fmt.Println(string(b)) [marshal]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#Marshal +## Unstable API + +This API does not yet follow the backward compatibility guarantees of this +library. They provide early access to features that may have rough edges or an +API subject to change. + +### Parser + +Parser is the unstable API that allows iterative parsing of a TOML document at +the AST level. See https://pkg.go.dev/github.com/pelletier/go-toml/v2/unstable. + ## Benchmarks Execution time speedup compared to other Go TOML libraries: diff --git a/vendor/github.com/pelletier/go-toml/v2/decode.go b/vendor/github.com/pelletier/go-toml/v2/decode.go index 4af96536..3a860d0f 100644 --- a/vendor/github.com/pelletier/go-toml/v2/decode.go +++ b/vendor/github.com/pelletier/go-toml/v2/decode.go @@ -5,6 +5,8 @@ import ( "math" "strconv" "time" + + "github.com/pelletier/go-toml/v2/unstable" ) func parseInteger(b []byte) (int64, error) { @@ -32,7 +34,7 @@ func parseLocalDate(b []byte) (LocalDate, error) { var date LocalDate if len(b) != 10 || b[4] != '-' || b[7] != '-' { - return date, newDecodeError(b, "dates are expected to have the format YYYY-MM-DD") + return date, unstable.NewParserError(b, "dates are expected to have the format YYYY-MM-DD") } var err error @@ -53,7 +55,7 @@ func parseLocalDate(b []byte) (LocalDate, error) { } if !isValidDate(date.Year, date.Month, date.Day) { - return LocalDate{}, newDecodeError(b, "impossible date") + return LocalDate{}, unstable.NewParserError(b, "impossible date") } return date, nil @@ -64,7 +66,7 @@ func parseDecimalDigits(b []byte) (int, error) { for i, c := range b { if c < '0' || c > '9' { - return 0, newDecodeError(b[i:i+1], "expected digit (0-9)") + return 0, unstable.NewParserError(b[i:i+1], "expected digit (0-9)") } v *= 10 v += int(c - '0') @@ -97,7 +99,7 @@ func parseDateTime(b []byte) (time.Time, error) { } else { const dateTimeByteLen = 6 if len(b) != dateTimeByteLen { - return time.Time{}, newDecodeError(b, "invalid date-time timezone") + return time.Time{}, unstable.NewParserError(b, "invalid date-time timezone") } var direction int switch b[0] { @@ -106,11 +108,11 @@ func parseDateTime(b []byte) (time.Time, error) { case '+': direction = +1 default: - return time.Time{}, newDecodeError(b[:1], "invalid timezone offset character") + return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset character") } if b[3] != ':' { - return time.Time{}, newDecodeError(b[3:4], "expected a : separator") + return time.Time{}, unstable.NewParserError(b[3:4], "expected a : separator") } hours, err := parseDecimalDigits(b[1:3]) @@ -118,7 +120,7 @@ func parseDateTime(b []byte) (time.Time, error) { return time.Time{}, err } if hours > 23 { - return time.Time{}, newDecodeError(b[:1], "invalid timezone offset hours") + return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset hours") } minutes, err := parseDecimalDigits(b[4:6]) @@ -126,7 +128,7 @@ func parseDateTime(b []byte) (time.Time, error) { return time.Time{}, err } if minutes > 59 { - return time.Time{}, newDecodeError(b[:1], "invalid timezone offset minutes") + return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset minutes") } seconds := direction * (hours*3600 + minutes*60) @@ -139,7 +141,7 @@ func parseDateTime(b []byte) (time.Time, error) { } if len(b) > 0 { - return time.Time{}, newDecodeError(b, "extra bytes at the end of the timezone") + return time.Time{}, unstable.NewParserError(b, "extra bytes at the end of the timezone") } t := time.Date( @@ -160,7 +162,7 @@ func parseLocalDateTime(b []byte) (LocalDateTime, []byte, error) { const localDateTimeByteMinLen = 11 if len(b) < localDateTimeByteMinLen { - return dt, nil, newDecodeError(b, "local datetimes are expected to have the format YYYY-MM-DDTHH:MM:SS[.NNNNNNNNN]") + return dt, nil, unstable.NewParserError(b, "local datetimes are expected to have the format YYYY-MM-DDTHH:MM:SS[.NNNNNNNNN]") } date, err := parseLocalDate(b[:10]) @@ -171,7 +173,7 @@ func parseLocalDateTime(b []byte) (LocalDateTime, []byte, error) { sep := b[10] if sep != 'T' && sep != ' ' && sep != 't' { - return dt, nil, newDecodeError(b[10:11], "datetime separator is expected to be T or a space") + return dt, nil, unstable.NewParserError(b[10:11], "datetime separator is expected to be T or a space") } t, rest, err := parseLocalTime(b[11:]) @@ -195,7 +197,7 @@ func parseLocalTime(b []byte) (LocalTime, []byte, error) { // check if b matches to have expected format HH:MM:SS[.NNNNNN] const localTimeByteLen = 8 if len(b) < localTimeByteLen { - return t, nil, newDecodeError(b, "times are expected to have the format HH:MM:SS[.NNNNNN]") + return t, nil, unstable.NewParserError(b, "times are expected to have the format HH:MM:SS[.NNNNNN]") } var err error @@ -206,10 +208,10 @@ func parseLocalTime(b []byte) (LocalTime, []byte, error) { } if t.Hour > 23 { - return t, nil, newDecodeError(b[0:2], "hour cannot be greater 23") + return t, nil, unstable.NewParserError(b[0:2], "hour cannot be greater 23") } if b[2] != ':' { - return t, nil, newDecodeError(b[2:3], "expecting colon between hours and minutes") + return t, nil, unstable.NewParserError(b[2:3], "expecting colon between hours and minutes") } t.Minute, err = parseDecimalDigits(b[3:5]) @@ -217,10 +219,10 @@ func parseLocalTime(b []byte) (LocalTime, []byte, error) { return t, nil, err } if t.Minute > 59 { - return t, nil, newDecodeError(b[3:5], "minutes cannot be greater 59") + return t, nil, unstable.NewParserError(b[3:5], "minutes cannot be greater 59") } if b[5] != ':' { - return t, nil, newDecodeError(b[5:6], "expecting colon between minutes and seconds") + return t, nil, unstable.NewParserError(b[5:6], "expecting colon between minutes and seconds") } t.Second, err = parseDecimalDigits(b[6:8]) @@ -229,7 +231,7 @@ func parseLocalTime(b []byte) (LocalTime, []byte, error) { } if t.Second > 60 { - return t, nil, newDecodeError(b[6:8], "seconds cannot be greater 60") + return t, nil, unstable.NewParserError(b[6:8], "seconds cannot be greater 60") } b = b[8:] @@ -242,7 +244,7 @@ func parseLocalTime(b []byte) (LocalTime, []byte, error) { for i, c := range b[1:] { if !isDigit(c) { if i == 0 { - return t, nil, newDecodeError(b[0:1], "need at least one digit after fraction point") + return t, nil, unstable.NewParserError(b[0:1], "need at least one digit after fraction point") } break } @@ -266,7 +268,7 @@ func parseLocalTime(b []byte) (LocalTime, []byte, error) { } if precision == 0 { - return t, nil, newDecodeError(b[:1], "nanoseconds need at least one digit") + return t, nil, unstable.NewParserError(b[:1], "nanoseconds need at least one digit") } t.Nanosecond = frac * nspow[precision] @@ -289,24 +291,24 @@ func parseFloat(b []byte) (float64, error) { } if cleaned[0] == '.' { - return 0, newDecodeError(b, "float cannot start with a dot") + return 0, unstable.NewParserError(b, "float cannot start with a dot") } if cleaned[len(cleaned)-1] == '.' { - return 0, newDecodeError(b, "float cannot end with a dot") + return 0, unstable.NewParserError(b, "float cannot end with a dot") } dotAlreadySeen := false for i, c := range cleaned { if c == '.' { if dotAlreadySeen { - return 0, newDecodeError(b[i:i+1], "float can have at most one decimal point") + return 0, unstable.NewParserError(b[i:i+1], "float can have at most one decimal point") } if !isDigit(cleaned[i-1]) { - return 0, newDecodeError(b[i-1:i+1], "float decimal point must be preceded by a digit") + return 0, unstable.NewParserError(b[i-1:i+1], "float decimal point must be preceded by a digit") } if !isDigit(cleaned[i+1]) { - return 0, newDecodeError(b[i:i+2], "float decimal point must be followed by a digit") + return 0, unstable.NewParserError(b[i:i+2], "float decimal point must be followed by a digit") } dotAlreadySeen = true } @@ -317,12 +319,12 @@ func parseFloat(b []byte) (float64, error) { start = 1 } if cleaned[start] == '0' && isDigit(cleaned[start+1]) { - return 0, newDecodeError(b, "float integer part cannot have leading zeroes") + return 0, unstable.NewParserError(b, "float integer part cannot have leading zeroes") } f, err := strconv.ParseFloat(string(cleaned), 64) if err != nil { - return 0, newDecodeError(b, "unable to parse float: %w", err) + return 0, unstable.NewParserError(b, "unable to parse float: %w", err) } return f, nil @@ -336,7 +338,7 @@ func parseIntHex(b []byte) (int64, error) { i, err := strconv.ParseInt(string(cleaned), 16, 64) if err != nil { - return 0, newDecodeError(b, "couldn't parse hexadecimal number: %w", err) + return 0, unstable.NewParserError(b, "couldn't parse hexadecimal number: %w", err) } return i, nil @@ -350,7 +352,7 @@ func parseIntOct(b []byte) (int64, error) { i, err := strconv.ParseInt(string(cleaned), 8, 64) if err != nil { - return 0, newDecodeError(b, "couldn't parse octal number: %w", err) + return 0, unstable.NewParserError(b, "couldn't parse octal number: %w", err) } return i, nil @@ -364,7 +366,7 @@ func parseIntBin(b []byte) (int64, error) { i, err := strconv.ParseInt(string(cleaned), 2, 64) if err != nil { - return 0, newDecodeError(b, "couldn't parse binary number: %w", err) + return 0, unstable.NewParserError(b, "couldn't parse binary number: %w", err) } return i, nil @@ -387,12 +389,12 @@ func parseIntDec(b []byte) (int64, error) { } if len(cleaned) > startIdx+1 && cleaned[startIdx] == '0' { - return 0, newDecodeError(b, "leading zero not allowed on decimal number") + return 0, unstable.NewParserError(b, "leading zero not allowed on decimal number") } i, err := strconv.ParseInt(string(cleaned), 10, 64) if err != nil { - return 0, newDecodeError(b, "couldn't parse decimal number: %w", err) + return 0, unstable.NewParserError(b, "couldn't parse decimal number: %w", err) } return i, nil @@ -409,11 +411,11 @@ func checkAndRemoveUnderscoresIntegers(b []byte) ([]byte, error) { } if b[start] == '_' { - return nil, newDecodeError(b[start:start+1], "number cannot start with underscore") + return nil, unstable.NewParserError(b[start:start+1], "number cannot start with underscore") } if b[len(b)-1] == '_' { - return nil, newDecodeError(b[len(b)-1:], "number cannot end with underscore") + return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore") } // fast path @@ -435,7 +437,7 @@ func checkAndRemoveUnderscoresIntegers(b []byte) ([]byte, error) { c := b[i] if c == '_' { if !before { - return nil, newDecodeError(b[i-1:i+1], "number must have at least one digit between underscores") + return nil, unstable.NewParserError(b[i-1:i+1], "number must have at least one digit between underscores") } before = false } else { @@ -449,11 +451,11 @@ func checkAndRemoveUnderscoresIntegers(b []byte) ([]byte, error) { func checkAndRemoveUnderscoresFloats(b []byte) ([]byte, error) { if b[0] == '_' { - return nil, newDecodeError(b[0:1], "number cannot start with underscore") + return nil, unstable.NewParserError(b[0:1], "number cannot start with underscore") } if b[len(b)-1] == '_' { - return nil, newDecodeError(b[len(b)-1:], "number cannot end with underscore") + return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore") } // fast path @@ -476,10 +478,10 @@ func checkAndRemoveUnderscoresFloats(b []byte) ([]byte, error) { switch c { case '_': if !before { - return nil, newDecodeError(b[i-1:i+1], "number must have at least one digit between underscores") + return nil, unstable.NewParserError(b[i-1:i+1], "number must have at least one digit between underscores") } if i < len(b)-1 && (b[i+1] == 'e' || b[i+1] == 'E') { - return nil, newDecodeError(b[i+1:i+2], "cannot have underscore before exponent") + return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore before exponent") } before = false case '+', '-': @@ -488,15 +490,15 @@ func checkAndRemoveUnderscoresFloats(b []byte) ([]byte, error) { before = false case 'e', 'E': if i < len(b)-1 && b[i+1] == '_' { - return nil, newDecodeError(b[i+1:i+2], "cannot have underscore after exponent") + return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after exponent") } cleaned = append(cleaned, c) case '.': if i < len(b)-1 && b[i+1] == '_' { - return nil, newDecodeError(b[i+1:i+2], "cannot have underscore after decimal point") + return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after decimal point") } if i > 0 && b[i-1] == '_' { - return nil, newDecodeError(b[i-1:i], "cannot have underscore before decimal point") + return nil, unstable.NewParserError(b[i-1:i], "cannot have underscore before decimal point") } cleaned = append(cleaned, c) default: @@ -542,3 +544,7 @@ func daysIn(m int, year int) int { func isLeap(year int) bool { return year%4 == 0 && (year%100 != 0 || year%400 == 0) } + +func isDigit(r byte) bool { + return r >= '0' && r <= '9' +} diff --git a/vendor/github.com/pelletier/go-toml/v2/errors.go b/vendor/github.com/pelletier/go-toml/v2/errors.go index 2e7f0ffd..309733f1 100644 --- a/vendor/github.com/pelletier/go-toml/v2/errors.go +++ b/vendor/github.com/pelletier/go-toml/v2/errors.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/pelletier/go-toml/v2/internal/danger" + "github.com/pelletier/go-toml/v2/unstable" ) // DecodeError represents an error encountered during the parsing or decoding @@ -55,25 +56,6 @@ func (s *StrictMissingError) String() string { type Key []string -// internal version of DecodeError that is used as the base to create a -// DecodeError with full context. -type decodeError struct { - highlight []byte - message string - key Key // optional -} - -func (de *decodeError) Error() string { - return de.message -} - -func newDecodeError(highlight []byte, format string, args ...interface{}) error { - return &decodeError{ - highlight: highlight, - message: fmt.Errorf(format, args...).Error(), - } -} - // Error returns the error message contained in the DecodeError. func (e *DecodeError) Error() string { return "toml: " + e.message @@ -105,12 +87,12 @@ func (e *DecodeError) Key() Key { // highlight can be freely deallocated. // //nolint:funlen -func wrapDecodeError(document []byte, de *decodeError) *DecodeError { - offset := danger.SubsliceOffset(document, de.highlight) +func wrapDecodeError(document []byte, de *unstable.ParserError) *DecodeError { + offset := danger.SubsliceOffset(document, de.Highlight) errMessage := de.Error() errLine, errColumn := positionAtEnd(document[:offset]) - before, after := linesOfContext(document, de.highlight, offset, 3) + before, after := linesOfContext(document, de.Highlight, offset, 3) var buf strings.Builder @@ -140,7 +122,7 @@ func wrapDecodeError(document []byte, de *decodeError) *DecodeError { buf.Write(before[0]) } - buf.Write(de.highlight) + buf.Write(de.Highlight) if len(after) > 0 { buf.Write(after[0]) @@ -158,7 +140,7 @@ func wrapDecodeError(document []byte, de *decodeError) *DecodeError { buf.WriteString(strings.Repeat(" ", len(before[0]))) } - buf.WriteString(strings.Repeat("~", len(de.highlight))) + buf.WriteString(strings.Repeat("~", len(de.Highlight))) if len(errMessage) > 0 { buf.WriteString(" ") @@ -183,7 +165,7 @@ func wrapDecodeError(document []byte, de *decodeError) *DecodeError { message: errMessage, line: errLine, column: errColumn, - key: de.key, + key: de.Key, human: buf.String(), } } diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/ast/builder.go b/vendor/github.com/pelletier/go-toml/v2/internal/ast/builder.go deleted file mode 100644 index 120f16e5..00000000 --- a/vendor/github.com/pelletier/go-toml/v2/internal/ast/builder.go +++ /dev/null @@ -1,51 +0,0 @@ -package ast - -type Reference int - -const InvalidReference Reference = -1 - -func (r Reference) Valid() bool { - return r != InvalidReference -} - -type Builder struct { - tree Root - lastIdx int -} - -func (b *Builder) Tree() *Root { - return &b.tree -} - -func (b *Builder) NodeAt(ref Reference) *Node { - return b.tree.at(ref) -} - -func (b *Builder) Reset() { - b.tree.nodes = b.tree.nodes[:0] - b.lastIdx = 0 -} - -func (b *Builder) Push(n Node) Reference { - b.lastIdx = len(b.tree.nodes) - b.tree.nodes = append(b.tree.nodes, n) - return Reference(b.lastIdx) -} - -func (b *Builder) PushAndChain(n Node) Reference { - newIdx := len(b.tree.nodes) - b.tree.nodes = append(b.tree.nodes, n) - if b.lastIdx >= 0 { - b.tree.nodes[b.lastIdx].next = newIdx - b.lastIdx - } - b.lastIdx = newIdx - return Reference(b.lastIdx) -} - -func (b *Builder) AttachChild(parent Reference, child Reference) { - b.tree.nodes[parent].child = int(child) - int(parent) -} - -func (b *Builder) Chain(from Reference, to Reference) { - b.tree.nodes[from].next = int(to) - int(from) -} diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/characters/ascii.go b/vendor/github.com/pelletier/go-toml/v2/internal/characters/ascii.go new file mode 100644 index 00000000..80f698db --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/internal/characters/ascii.go @@ -0,0 +1,42 @@ +package characters + +var invalidAsciiTable = [256]bool{ + 0x00: true, + 0x01: true, + 0x02: true, + 0x03: true, + 0x04: true, + 0x05: true, + 0x06: true, + 0x07: true, + 0x08: true, + // 0x09 TAB + // 0x0A LF + 0x0B: true, + 0x0C: true, + // 0x0D CR + 0x0E: true, + 0x0F: true, + 0x10: true, + 0x11: true, + 0x12: true, + 0x13: true, + 0x14: true, + 0x15: true, + 0x16: true, + 0x17: true, + 0x18: true, + 0x19: true, + 0x1A: true, + 0x1B: true, + 0x1C: true, + 0x1D: true, + 0x1E: true, + 0x1F: true, + // 0x20 - 0x7E Printable ASCII characters + 0x7F: true, +} + +func InvalidAscii(b byte) bool { + return invalidAsciiTable[b] +} diff --git a/vendor/github.com/pelletier/go-toml/v2/utf8.go b/vendor/github.com/pelletier/go-toml/v2/internal/characters/utf8.go similarity index 87% rename from vendor/github.com/pelletier/go-toml/v2/utf8.go rename to vendor/github.com/pelletier/go-toml/v2/internal/characters/utf8.go index d47a4f20..db4f45ac 100644 --- a/vendor/github.com/pelletier/go-toml/v2/utf8.go +++ b/vendor/github.com/pelletier/go-toml/v2/internal/characters/utf8.go @@ -1,4 +1,4 @@ -package toml +package characters import ( "unicode/utf8" @@ -32,7 +32,7 @@ func (u utf8Err) Zero() bool { // 0x9 => tab, ok // 0xA - 0x1F => invalid // 0x7F => invalid -func utf8TomlValidAlreadyEscaped(p []byte) (err utf8Err) { +func Utf8TomlValidAlreadyEscaped(p []byte) (err utf8Err) { // Fast path. Check for and skip 8 bytes of ASCII characters per iteration. offset := 0 for len(p) >= 8 { @@ -48,7 +48,7 @@ func utf8TomlValidAlreadyEscaped(p []byte) (err utf8Err) { } for i, b := range p[:8] { - if invalidAscii(b) { + if InvalidAscii(b) { err.Index = offset + i err.Size = 1 return @@ -62,7 +62,7 @@ func utf8TomlValidAlreadyEscaped(p []byte) (err utf8Err) { for i := 0; i < n; { pi := p[i] if pi < utf8.RuneSelf { - if invalidAscii(pi) { + if InvalidAscii(pi) { err.Index = offset + i err.Size = 1 return @@ -106,11 +106,11 @@ func utf8TomlValidAlreadyEscaped(p []byte) (err utf8Err) { } // Return the size of the next rune if valid, 0 otherwise. -func utf8ValidNext(p []byte) int { +func Utf8ValidNext(p []byte) int { c := p[0] if c < utf8.RuneSelf { - if invalidAscii(c) { + if InvalidAscii(c) { return 0 } return 1 @@ -140,47 +140,6 @@ func utf8ValidNext(p []byte) int { return size } -var invalidAsciiTable = [256]bool{ - 0x00: true, - 0x01: true, - 0x02: true, - 0x03: true, - 0x04: true, - 0x05: true, - 0x06: true, - 0x07: true, - 0x08: true, - // 0x09 TAB - // 0x0A LF - 0x0B: true, - 0x0C: true, - // 0x0D CR - 0x0E: true, - 0x0F: true, - 0x10: true, - 0x11: true, - 0x12: true, - 0x13: true, - 0x14: true, - 0x15: true, - 0x16: true, - 0x17: true, - 0x18: true, - 0x19: true, - 0x1A: true, - 0x1B: true, - 0x1C: true, - 0x1D: true, - 0x1E: true, - 0x1F: true, - // 0x20 - 0x7E Printable ASCII characters - 0x7F: true, -} - -func invalidAscii(b byte) bool { - return invalidAsciiTable[b] -} - // acceptRange gives the range of valid values for the second byte in a UTF-8 // sequence. type acceptRange struct { diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go index 7c148f48..149b17f5 100644 --- a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go +++ b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go @@ -1,8 +1,6 @@ package tracker -import ( - "github.com/pelletier/go-toml/v2/internal/ast" -) +import "github.com/pelletier/go-toml/v2/unstable" // KeyTracker is a tracker that keeps track of the current Key as the AST is // walked. @@ -11,19 +9,19 @@ type KeyTracker struct { } // UpdateTable sets the state of the tracker with the AST table node. -func (t *KeyTracker) UpdateTable(node *ast.Node) { +func (t *KeyTracker) UpdateTable(node *unstable.Node) { t.reset() t.Push(node) } // UpdateArrayTable sets the state of the tracker with the AST array table node. -func (t *KeyTracker) UpdateArrayTable(node *ast.Node) { +func (t *KeyTracker) UpdateArrayTable(node *unstable.Node) { t.reset() t.Push(node) } // Push the given key on the stack. -func (t *KeyTracker) Push(node *ast.Node) { +func (t *KeyTracker) Push(node *unstable.Node) { it := node.Key() for it.Next() { t.k = append(t.k, string(it.Node().Data)) @@ -31,7 +29,7 @@ func (t *KeyTracker) Push(node *ast.Node) { } // Pop key from stack. -func (t *KeyTracker) Pop(node *ast.Node) { +func (t *KeyTracker) Pop(node *unstable.Node) { it := node.Key() for it.Next() { t.k = t.k[:len(t.k)-1] diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go index a7ee05ba..40e23f83 100644 --- a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go +++ b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go @@ -5,7 +5,7 @@ import ( "fmt" "sync" - "github.com/pelletier/go-toml/v2/internal/ast" + "github.com/pelletier/go-toml/v2/unstable" ) type keyKind uint8 @@ -150,23 +150,23 @@ func (s *SeenTracker) setExplicitFlag(parentIdx int) { // CheckExpression takes a top-level node and checks that it does not contain // keys that have been seen in previous calls, and validates that types are // consistent. -func (s *SeenTracker) CheckExpression(node *ast.Node) error { +func (s *SeenTracker) CheckExpression(node *unstable.Node) error { if s.entries == nil { s.reset() } switch node.Kind { - case ast.KeyValue: + case unstable.KeyValue: return s.checkKeyValue(node) - case ast.Table: + case unstable.Table: return s.checkTable(node) - case ast.ArrayTable: + case unstable.ArrayTable: return s.checkArrayTable(node) default: panic(fmt.Errorf("this should not be a top level node type: %s", node.Kind)) } } -func (s *SeenTracker) checkTable(node *ast.Node) error { +func (s *SeenTracker) checkTable(node *unstable.Node) error { if s.currentIdx >= 0 { s.setExplicitFlag(s.currentIdx) } @@ -219,7 +219,7 @@ func (s *SeenTracker) checkTable(node *ast.Node) error { return nil } -func (s *SeenTracker) checkArrayTable(node *ast.Node) error { +func (s *SeenTracker) checkArrayTable(node *unstable.Node) error { if s.currentIdx >= 0 { s.setExplicitFlag(s.currentIdx) } @@ -267,7 +267,7 @@ func (s *SeenTracker) checkArrayTable(node *ast.Node) error { return nil } -func (s *SeenTracker) checkKeyValue(node *ast.Node) error { +func (s *SeenTracker) checkKeyValue(node *unstable.Node) error { parentIdx := s.currentIdx it := node.Key() @@ -297,26 +297,26 @@ func (s *SeenTracker) checkKeyValue(node *ast.Node) error { value := node.Value() switch value.Kind { - case ast.InlineTable: + case unstable.InlineTable: return s.checkInlineTable(value) - case ast.Array: + case unstable.Array: return s.checkArray(value) } return nil } -func (s *SeenTracker) checkArray(node *ast.Node) error { +func (s *SeenTracker) checkArray(node *unstable.Node) error { it := node.Children() for it.Next() { n := it.Node() switch n.Kind { - case ast.InlineTable: + case unstable.InlineTable: err := s.checkInlineTable(n) if err != nil { return err } - case ast.Array: + case unstable.Array: err := s.checkArray(n) if err != nil { return err @@ -326,7 +326,7 @@ func (s *SeenTracker) checkArray(node *ast.Node) error { return nil } -func (s *SeenTracker) checkInlineTable(node *ast.Node) error { +func (s *SeenTracker) checkInlineTable(node *unstable.Node) error { if pool.New == nil { pool.New = func() interface{} { return &SeenTracker{} diff --git a/vendor/github.com/pelletier/go-toml/v2/localtime.go b/vendor/github.com/pelletier/go-toml/v2/localtime.go index 30a31dcb..a856bfdb 100644 --- a/vendor/github.com/pelletier/go-toml/v2/localtime.go +++ b/vendor/github.com/pelletier/go-toml/v2/localtime.go @@ -4,6 +4,8 @@ import ( "fmt" "strings" "time" + + "github.com/pelletier/go-toml/v2/unstable" ) // LocalDate represents a calendar day in no specific timezone. @@ -75,7 +77,7 @@ func (d LocalTime) MarshalText() ([]byte, error) { func (d *LocalTime) UnmarshalText(b []byte) error { res, left, err := parseLocalTime(b) if err == nil && len(left) != 0 { - err = newDecodeError(left, "extra characters") + err = unstable.NewParserError(left, "extra characters") } if err != nil { return err @@ -109,7 +111,7 @@ func (d LocalDateTime) MarshalText() ([]byte, error) { func (d *LocalDateTime) UnmarshalText(data []byte) error { res, left, err := parseLocalDateTime(data) if err == nil && len(left) != 0 { - err = newDecodeError(left, "extra characters") + err = unstable.NewParserError(left, "extra characters") } if err != nil { return err diff --git a/vendor/github.com/pelletier/go-toml/v2/marshaler.go b/vendor/github.com/pelletier/go-toml/v2/marshaler.go index acb28831..07aceb90 100644 --- a/vendor/github.com/pelletier/go-toml/v2/marshaler.go +++ b/vendor/github.com/pelletier/go-toml/v2/marshaler.go @@ -12,6 +12,8 @@ import ( "strings" "time" "unicode" + + "github.com/pelletier/go-toml/v2/internal/characters" ) // Marshal serializes a Go value as a TOML document. @@ -437,7 +439,7 @@ func (enc *Encoder) encodeString(b []byte, v string, options valueOptions) []byt func needsQuoting(v string) bool { // TODO: vectorize for _, b := range []byte(v) { - if b == '\'' || b == '\r' || b == '\n' || invalidAscii(b) { + if b == '\'' || b == '\r' || b == '\n' || characters.InvalidAscii(b) { return true } } diff --git a/vendor/github.com/pelletier/go-toml/v2/strict.go b/vendor/github.com/pelletier/go-toml/v2/strict.go index b7830d13..802e7e4d 100644 --- a/vendor/github.com/pelletier/go-toml/v2/strict.go +++ b/vendor/github.com/pelletier/go-toml/v2/strict.go @@ -1,9 +1,9 @@ package toml import ( - "github.com/pelletier/go-toml/v2/internal/ast" "github.com/pelletier/go-toml/v2/internal/danger" "github.com/pelletier/go-toml/v2/internal/tracker" + "github.com/pelletier/go-toml/v2/unstable" ) type strict struct { @@ -12,10 +12,10 @@ type strict struct { // Tracks the current key being processed. key tracker.KeyTracker - missing []decodeError + missing []unstable.ParserError } -func (s *strict) EnterTable(node *ast.Node) { +func (s *strict) EnterTable(node *unstable.Node) { if !s.Enabled { return } @@ -23,7 +23,7 @@ func (s *strict) EnterTable(node *ast.Node) { s.key.UpdateTable(node) } -func (s *strict) EnterArrayTable(node *ast.Node) { +func (s *strict) EnterArrayTable(node *unstable.Node) { if !s.Enabled { return } @@ -31,7 +31,7 @@ func (s *strict) EnterArrayTable(node *ast.Node) { s.key.UpdateArrayTable(node) } -func (s *strict) EnterKeyValue(node *ast.Node) { +func (s *strict) EnterKeyValue(node *unstable.Node) { if !s.Enabled { return } @@ -39,7 +39,7 @@ func (s *strict) EnterKeyValue(node *ast.Node) { s.key.Push(node) } -func (s *strict) ExitKeyValue(node *ast.Node) { +func (s *strict) ExitKeyValue(node *unstable.Node) { if !s.Enabled { return } @@ -47,27 +47,27 @@ func (s *strict) ExitKeyValue(node *ast.Node) { s.key.Pop(node) } -func (s *strict) MissingTable(node *ast.Node) { +func (s *strict) MissingTable(node *unstable.Node) { if !s.Enabled { return } - s.missing = append(s.missing, decodeError{ - highlight: keyLocation(node), - message: "missing table", - key: s.key.Key(), + s.missing = append(s.missing, unstable.ParserError{ + Highlight: keyLocation(node), + Message: "missing table", + Key: s.key.Key(), }) } -func (s *strict) MissingField(node *ast.Node) { +func (s *strict) MissingField(node *unstable.Node) { if !s.Enabled { return } - s.missing = append(s.missing, decodeError{ - highlight: keyLocation(node), - message: "missing field", - key: s.key.Key(), + s.missing = append(s.missing, unstable.ParserError{ + Highlight: keyLocation(node), + Message: "missing field", + Key: s.key.Key(), }) } @@ -88,7 +88,7 @@ func (s *strict) Error(doc []byte) error { return err } -func keyLocation(node *ast.Node) []byte { +func keyLocation(node *unstable.Node) []byte { k := node.Key() hasOne := k.Next() diff --git a/vendor/github.com/pelletier/go-toml/v2/types.go b/vendor/github.com/pelletier/go-toml/v2/types.go index 630a4546..3c6b8fe5 100644 --- a/vendor/github.com/pelletier/go-toml/v2/types.go +++ b/vendor/github.com/pelletier/go-toml/v2/types.go @@ -6,9 +6,9 @@ import ( "time" ) -var timeType = reflect.TypeOf(time.Time{}) -var textMarshalerType = reflect.TypeOf(new(encoding.TextMarshaler)).Elem() -var textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem() -var mapStringInterfaceType = reflect.TypeOf(map[string]interface{}{}) -var sliceInterfaceType = reflect.TypeOf([]interface{}{}) +var timeType = reflect.TypeOf((*time.Time)(nil)).Elem() +var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() +var textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() +var mapStringInterfaceType = reflect.TypeOf(map[string]interface{}(nil)) +var sliceInterfaceType = reflect.TypeOf([]interface{}(nil)) var stringType = reflect.TypeOf("") diff --git a/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go b/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go index d0d7a72d..70f6ec57 100644 --- a/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go +++ b/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go @@ -12,16 +12,16 @@ import ( "sync/atomic" "time" - "github.com/pelletier/go-toml/v2/internal/ast" "github.com/pelletier/go-toml/v2/internal/danger" "github.com/pelletier/go-toml/v2/internal/tracker" + "github.com/pelletier/go-toml/v2/unstable" ) // Unmarshal deserializes a TOML document into a Go value. // // It is a shortcut for Decoder.Decode() with the default options. func Unmarshal(data []byte, v interface{}) error { - p := parser{} + p := unstable.Parser{} p.Reset(data) d := decoder{p: &p} @@ -101,7 +101,7 @@ func (d *Decoder) Decode(v interface{}) error { return fmt.Errorf("toml: %w", err) } - p := parser{} + p := unstable.Parser{} p.Reset(b) dec := decoder{ p: &p, @@ -115,7 +115,7 @@ func (d *Decoder) Decode(v interface{}) error { type decoder struct { // Which parser instance in use for this decoding session. - p *parser + p *unstable.Parser // Flag indicating that the current expression is stashed. // If set to true, calling nextExpr will not actually pull a new expression @@ -157,7 +157,7 @@ func (d *decoder) typeMismatchError(toml string, target reflect.Type) error { return fmt.Errorf("toml: cannot decode TOML %s into a Go value of type %s", toml, target) } -func (d *decoder) expr() *ast.Node { +func (d *decoder) expr() *unstable.Node { return d.p.Expression() } @@ -208,12 +208,12 @@ func (d *decoder) FromParser(v interface{}) error { err := d.fromParser(r) if err == nil { - return d.strict.Error(d.p.data) + return d.strict.Error(d.p.Data()) } - var e *decodeError + var e *unstable.ParserError if errors.As(err, &e) { - return wrapDecodeError(d.p.data, e) + return wrapDecodeError(d.p.Data(), e) } return err @@ -234,16 +234,16 @@ func (d *decoder) fromParser(root reflect.Value) error { Rules for the unmarshal code: - The stack is used to keep track of which values need to be set where. -- handle* functions <=> switch on a given ast.Kind. +- handle* functions <=> switch on a given unstable.Kind. - unmarshalX* functions need to unmarshal a node of kind X. - An "object" is either a struct or a map. */ -func (d *decoder) handleRootExpression(expr *ast.Node, v reflect.Value) error { +func (d *decoder) handleRootExpression(expr *unstable.Node, v reflect.Value) error { var x reflect.Value var err error - if !(d.skipUntilTable && expr.Kind == ast.KeyValue) { + if !(d.skipUntilTable && expr.Kind == unstable.KeyValue) { err = d.seen.CheckExpression(expr) if err != nil { return err @@ -251,16 +251,16 @@ func (d *decoder) handleRootExpression(expr *ast.Node, v reflect.Value) error { } switch expr.Kind { - case ast.KeyValue: + case unstable.KeyValue: if d.skipUntilTable { return nil } x, err = d.handleKeyValue(expr, v) - case ast.Table: + case unstable.Table: d.skipUntilTable = false d.strict.EnterTable(expr) x, err = d.handleTable(expr.Key(), v) - case ast.ArrayTable: + case unstable.ArrayTable: d.skipUntilTable = false d.strict.EnterArrayTable(expr) x, err = d.handleArrayTable(expr.Key(), v) @@ -269,7 +269,7 @@ func (d *decoder) handleRootExpression(expr *ast.Node, v reflect.Value) error { } if d.skipUntilTable { - if expr.Kind == ast.Table || expr.Kind == ast.ArrayTable { + if expr.Kind == unstable.Table || expr.Kind == unstable.ArrayTable { d.strict.MissingTable(expr) } } else if err == nil && x.IsValid() { @@ -279,14 +279,14 @@ func (d *decoder) handleRootExpression(expr *ast.Node, v reflect.Value) error { return err } -func (d *decoder) handleArrayTable(key ast.Iterator, v reflect.Value) (reflect.Value, error) { +func (d *decoder) handleArrayTable(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { if key.Next() { return d.handleArrayTablePart(key, v) } return d.handleKeyValues(v) } -func (d *decoder) handleArrayTableCollectionLast(key ast.Iterator, v reflect.Value) (reflect.Value, error) { +func (d *decoder) handleArrayTableCollectionLast(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { switch v.Kind() { case reflect.Interface: elem := v.Elem() @@ -339,13 +339,13 @@ func (d *decoder) handleArrayTableCollectionLast(key ast.Iterator, v reflect.Val case reflect.Array: idx := d.arrayIndex(true, v) if idx >= v.Len() { - return v, fmt.Errorf("toml: cannot decode array table into %s at position %d", v.Type(), idx) + return v, fmt.Errorf("%s at position %d", d.typeMismatchError("array table", v.Type()), idx) } elem := v.Index(idx) _, err := d.handleArrayTable(key, elem) return v, err default: - return reflect.Value{}, fmt.Errorf("toml: cannot decode array table into a %s", v.Type()) + return reflect.Value{}, d.typeMismatchError("array table", v.Type()) } } @@ -353,7 +353,7 @@ func (d *decoder) handleArrayTableCollectionLast(key ast.Iterator, v reflect.Val // evaluated like a normal key, but if it returns a collection, it also needs to // point to the last element of the collection. Unless it is the last part of // the key, then it needs to create a new element at the end. -func (d *decoder) handleArrayTableCollection(key ast.Iterator, v reflect.Value) (reflect.Value, error) { +func (d *decoder) handleArrayTableCollection(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { if key.IsLast() { return d.handleArrayTableCollectionLast(key, v) } @@ -390,7 +390,7 @@ func (d *decoder) handleArrayTableCollection(key ast.Iterator, v reflect.Value) case reflect.Array: idx := d.arrayIndex(false, v) if idx >= v.Len() { - return v, fmt.Errorf("toml: cannot decode array table into %s at position %d", v.Type(), idx) + return v, fmt.Errorf("%s at position %d", d.typeMismatchError("array table", v.Type()), idx) } elem := v.Index(idx) _, err := d.handleArrayTable(key, elem) @@ -400,7 +400,7 @@ func (d *decoder) handleArrayTableCollection(key ast.Iterator, v reflect.Value) return d.handleArrayTable(key, v) } -func (d *decoder) handleKeyPart(key ast.Iterator, v reflect.Value, nextFn handlerFn, makeFn valueMakerFn) (reflect.Value, error) { +func (d *decoder) handleKeyPart(key unstable.Iterator, v reflect.Value, nextFn handlerFn, makeFn valueMakerFn) (reflect.Value, error) { var rv reflect.Value // First, dispatch over v to make sure it is a valid object. @@ -518,7 +518,7 @@ func (d *decoder) handleKeyPart(key ast.Iterator, v reflect.Value, nextFn handle // HandleArrayTablePart navigates the Go structure v using the key v. It is // only used for the prefix (non-last) parts of an array-table. When // encountering a collection, it should go to the last element. -func (d *decoder) handleArrayTablePart(key ast.Iterator, v reflect.Value) (reflect.Value, error) { +func (d *decoder) handleArrayTablePart(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { var makeFn valueMakerFn if key.IsLast() { makeFn = makeSliceInterface @@ -530,10 +530,10 @@ func (d *decoder) handleArrayTablePart(key ast.Iterator, v reflect.Value) (refle // HandleTable returns a reference when it has checked the next expression but // cannot handle it. -func (d *decoder) handleTable(key ast.Iterator, v reflect.Value) (reflect.Value, error) { +func (d *decoder) handleTable(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { if v.Kind() == reflect.Slice { if v.Len() == 0 { - return reflect.Value{}, newDecodeError(key.Node().Data, "cannot store a table in a slice") + return reflect.Value{}, unstable.NewParserError(key.Node().Data, "cannot store a table in a slice") } elem := v.Index(v.Len() - 1) x, err := d.handleTable(key, elem) @@ -560,7 +560,7 @@ func (d *decoder) handleKeyValues(v reflect.Value) (reflect.Value, error) { var rv reflect.Value for d.nextExpr() { expr := d.expr() - if expr.Kind != ast.KeyValue { + if expr.Kind != unstable.KeyValue { // Stash the expression so that fromParser can just loop and use // the right handler. // We could just recurse ourselves here, but at least this gives a @@ -587,7 +587,7 @@ func (d *decoder) handleKeyValues(v reflect.Value) (reflect.Value, error) { } type ( - handlerFn func(key ast.Iterator, v reflect.Value) (reflect.Value, error) + handlerFn func(key unstable.Iterator, v reflect.Value) (reflect.Value, error) valueMakerFn func() reflect.Value ) @@ -599,11 +599,11 @@ func makeSliceInterface() reflect.Value { return reflect.MakeSlice(sliceInterfaceType, 0, 16) } -func (d *decoder) handleTablePart(key ast.Iterator, v reflect.Value) (reflect.Value, error) { +func (d *decoder) handleTablePart(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { return d.handleKeyPart(key, v, d.handleTable, makeMapStringInterface) } -func (d *decoder) tryTextUnmarshaler(node *ast.Node, v reflect.Value) (bool, error) { +func (d *decoder) tryTextUnmarshaler(node *unstable.Node, v reflect.Value) (bool, error) { // Special case for time, because we allow to unmarshal to it from // different kind of AST nodes. if v.Type() == timeType { @@ -613,7 +613,7 @@ func (d *decoder) tryTextUnmarshaler(node *ast.Node, v reflect.Value) (bool, err if v.CanAddr() && v.Addr().Type().Implements(textUnmarshalerType) { err := v.Addr().Interface().(encoding.TextUnmarshaler).UnmarshalText(node.Data) if err != nil { - return false, newDecodeError(d.p.Raw(node.Raw), "%w", err) + return false, unstable.NewParserError(d.p.Raw(node.Raw), "%w", err) } return true, nil @@ -622,7 +622,7 @@ func (d *decoder) tryTextUnmarshaler(node *ast.Node, v reflect.Value) (bool, err return false, nil } -func (d *decoder) handleValue(value *ast.Node, v reflect.Value) error { +func (d *decoder) handleValue(value *unstable.Node, v reflect.Value) error { for v.Kind() == reflect.Ptr { v = initAndDereferencePointer(v) } @@ -633,32 +633,32 @@ func (d *decoder) handleValue(value *ast.Node, v reflect.Value) error { } switch value.Kind { - case ast.String: + case unstable.String: return d.unmarshalString(value, v) - case ast.Integer: + case unstable.Integer: return d.unmarshalInteger(value, v) - case ast.Float: + case unstable.Float: return d.unmarshalFloat(value, v) - case ast.Bool: + case unstable.Bool: return d.unmarshalBool(value, v) - case ast.DateTime: + case unstable.DateTime: return d.unmarshalDateTime(value, v) - case ast.LocalDate: + case unstable.LocalDate: return d.unmarshalLocalDate(value, v) - case ast.LocalTime: + case unstable.LocalTime: return d.unmarshalLocalTime(value, v) - case ast.LocalDateTime: + case unstable.LocalDateTime: return d.unmarshalLocalDateTime(value, v) - case ast.InlineTable: + case unstable.InlineTable: return d.unmarshalInlineTable(value, v) - case ast.Array: + case unstable.Array: return d.unmarshalArray(value, v) default: panic(fmt.Errorf("handleValue not implemented for %s", value.Kind)) } } -func (d *decoder) unmarshalArray(array *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalArray(array *unstable.Node, v reflect.Value) error { switch v.Kind() { case reflect.Slice: if v.IsNil() { @@ -729,7 +729,7 @@ func (d *decoder) unmarshalArray(array *ast.Node, v reflect.Value) error { return nil } -func (d *decoder) unmarshalInlineTable(itable *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalInlineTable(itable *unstable.Node, v reflect.Value) error { // Make sure v is an initialized object. switch v.Kind() { case reflect.Map: @@ -746,7 +746,7 @@ func (d *decoder) unmarshalInlineTable(itable *ast.Node, v reflect.Value) error } return d.unmarshalInlineTable(itable, elem) default: - return newDecodeError(itable.Data, "cannot store inline table in Go type %s", v.Kind()) + return unstable.NewParserError(itable.Data, "cannot store inline table in Go type %s", v.Kind()) } it := itable.Children() @@ -765,7 +765,7 @@ func (d *decoder) unmarshalInlineTable(itable *ast.Node, v reflect.Value) error return nil } -func (d *decoder) unmarshalDateTime(value *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalDateTime(value *unstable.Node, v reflect.Value) error { dt, err := parseDateTime(value.Data) if err != nil { return err @@ -775,7 +775,7 @@ func (d *decoder) unmarshalDateTime(value *ast.Node, v reflect.Value) error { return nil } -func (d *decoder) unmarshalLocalDate(value *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalLocalDate(value *unstable.Node, v reflect.Value) error { ld, err := parseLocalDate(value.Data) if err != nil { return err @@ -792,28 +792,28 @@ func (d *decoder) unmarshalLocalDate(value *ast.Node, v reflect.Value) error { return nil } -func (d *decoder) unmarshalLocalTime(value *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalLocalTime(value *unstable.Node, v reflect.Value) error { lt, rest, err := parseLocalTime(value.Data) if err != nil { return err } if len(rest) > 0 { - return newDecodeError(rest, "extra characters at the end of a local time") + return unstable.NewParserError(rest, "extra characters at the end of a local time") } v.Set(reflect.ValueOf(lt)) return nil } -func (d *decoder) unmarshalLocalDateTime(value *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalLocalDateTime(value *unstable.Node, v reflect.Value) error { ldt, rest, err := parseLocalDateTime(value.Data) if err != nil { return err } if len(rest) > 0 { - return newDecodeError(rest, "extra characters at the end of a local date time") + return unstable.NewParserError(rest, "extra characters at the end of a local date time") } if v.Type() == timeType { @@ -828,7 +828,7 @@ func (d *decoder) unmarshalLocalDateTime(value *ast.Node, v reflect.Value) error return nil } -func (d *decoder) unmarshalBool(value *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalBool(value *unstable.Node, v reflect.Value) error { b := value.Data[0] == 't' switch v.Kind() { @@ -837,13 +837,13 @@ func (d *decoder) unmarshalBool(value *ast.Node, v reflect.Value) error { case reflect.Interface: v.Set(reflect.ValueOf(b)) default: - return newDecodeError(value.Data, "cannot assign boolean to a %t", b) + return unstable.NewParserError(value.Data, "cannot assign boolean to a %t", b) } return nil } -func (d *decoder) unmarshalFloat(value *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalFloat(value *unstable.Node, v reflect.Value) error { f, err := parseFloat(value.Data) if err != nil { return err @@ -854,13 +854,13 @@ func (d *decoder) unmarshalFloat(value *ast.Node, v reflect.Value) error { v.SetFloat(f) case reflect.Float32: if f > math.MaxFloat32 { - return newDecodeError(value.Data, "number %f does not fit in a float32", f) + return unstable.NewParserError(value.Data, "number %f does not fit in a float32", f) } v.SetFloat(f) case reflect.Interface: v.Set(reflect.ValueOf(f)) default: - return newDecodeError(value.Data, "float cannot be assigned to %s", v.Kind()) + return unstable.NewParserError(value.Data, "float cannot be assigned to %s", v.Kind()) } return nil @@ -886,7 +886,7 @@ func init() { } } -func (d *decoder) unmarshalInteger(value *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalInteger(value *unstable.Node, v reflect.Value) error { i, err := parseInteger(value.Data) if err != nil { return err @@ -967,20 +967,20 @@ func (d *decoder) unmarshalInteger(value *ast.Node, v reflect.Value) error { return nil } -func (d *decoder) unmarshalString(value *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalString(value *unstable.Node, v reflect.Value) error { switch v.Kind() { case reflect.String: v.SetString(string(value.Data)) case reflect.Interface: v.Set(reflect.ValueOf(string(value.Data))) default: - return newDecodeError(d.p.Raw(value.Raw), "cannot store TOML string into a Go %s", v.Kind()) + return unstable.NewParserError(d.p.Raw(value.Raw), "cannot store TOML string into a Go %s", v.Kind()) } return nil } -func (d *decoder) handleKeyValue(expr *ast.Node, v reflect.Value) (reflect.Value, error) { +func (d *decoder) handleKeyValue(expr *unstable.Node, v reflect.Value) (reflect.Value, error) { d.strict.EnterKeyValue(expr) v, err := d.handleKeyValueInner(expr.Key(), expr.Value(), v) @@ -994,7 +994,7 @@ func (d *decoder) handleKeyValue(expr *ast.Node, v reflect.Value) (reflect.Value return v, err } -func (d *decoder) handleKeyValueInner(key ast.Iterator, value *ast.Node, v reflect.Value) (reflect.Value, error) { +func (d *decoder) handleKeyValueInner(key unstable.Iterator, value *unstable.Node, v reflect.Value) (reflect.Value, error) { if key.Next() { // Still scoping the key return d.handleKeyValuePart(key, value, v) @@ -1004,7 +1004,7 @@ func (d *decoder) handleKeyValueInner(key ast.Iterator, value *ast.Node, v refle return reflect.Value{}, d.handleValue(value, v) } -func (d *decoder) handleKeyValuePart(key ast.Iterator, value *ast.Node, v reflect.Value) (reflect.Value, error) { +func (d *decoder) handleKeyValuePart(key unstable.Iterator, value *unstable.Node, v reflect.Value) (reflect.Value, error) { // contains the replacement for v var rv reflect.Value diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/ast/ast.go b/vendor/github.com/pelletier/go-toml/v2/unstable/ast.go similarity index 60% rename from vendor/github.com/pelletier/go-toml/v2/internal/ast/ast.go rename to vendor/github.com/pelletier/go-toml/v2/unstable/ast.go index 9dec2e00..b60d9bfd 100644 --- a/vendor/github.com/pelletier/go-toml/v2/internal/ast/ast.go +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/ast.go @@ -1,4 +1,4 @@ -package ast +package unstable import ( "fmt" @@ -7,13 +7,16 @@ import ( "github.com/pelletier/go-toml/v2/internal/danger" ) -// Iterator starts uninitialized, you need to call Next() first. +// Iterator over a sequence of nodes. +// +// Starts uninitialized, you need to call Next() first. // // For example: // // it := n.Children() // for it.Next() { -// it.Node() +// n := it.Node() +// // do something with n // } type Iterator struct { started bool @@ -32,42 +35,31 @@ func (c *Iterator) Next() bool { } // IsLast returns true if the current node of the iterator is the last -// one. Subsequent call to Next() will return false. +// one. Subsequent calls to Next() will return false. func (c *Iterator) IsLast() bool { return c.node.next == 0 } -// Node returns a copy of the node pointed at by the iterator. +// Node returns a pointer to the node pointed at by the iterator. func (c *Iterator) Node() *Node { return c.node } -// Root contains a full AST. +// Node in a TOML expression AST. // -// It is immutable once constructed with Builder. -type Root struct { - nodes []Node -} - -// Iterator over the top level nodes. -func (r *Root) Iterator() Iterator { - it := Iterator{} - if len(r.nodes) > 0 { - it.node = &r.nodes[0] - } - return it -} - -func (r *Root) at(idx Reference) *Node { - return &r.nodes[idx] -} - -// Arrays have one child per element in the array. InlineTables have -// one child per key-value pair in the table. KeyValues have at least -// two children. The first one is the value. The rest make a -// potentially dotted key. Table and Array table have one child per -// element of the key they represent (same as KeyValue, but without -// the last node being the value). +// Depending on Kind, its sequence of children should be interpreted +// differently. +// +// - Array have one child per element in the array. +// - InlineTable have one child per key-value in the table (each of kind +// InlineTable). +// - KeyValue have at least two children. The first one is the value. The rest +// make a potentially dotted key. +// - Table and ArrayTable's children represent a dotted key (same as +// KeyValue, but without the first node being the value). +// +// When relevant, Raw describes the range of bytes this node is refering to in +// the input document. Use Parser.Raw() to retrieve the actual bytes. type Node struct { Kind Kind Raw Range // Raw bytes from the input. @@ -80,13 +72,13 @@ type Node struct { child int // 0 if no child } +// Range of bytes in the document. type Range struct { Offset uint32 Length uint32 } -// Next returns a copy of the next node, or an invalid Node if there -// is no next node. +// Next returns a pointer to the next node, or nil if there is no next node. func (n *Node) Next() *Node { if n.next == 0 { return nil @@ -96,9 +88,9 @@ func (n *Node) Next() *Node { return (*Node)(danger.Stride(ptr, size, n.next)) } -// Child returns a copy of the first child node of this node. Other -// children can be accessed calling Next on the first child. Returns -// an invalid Node if there is none. +// Child returns a pointer to the first child node of this node. Other children +// can be accessed calling Next on the first child. Returns an nil if this Node +// has no child. func (n *Node) Child() *Node { if n.child == 0 { return nil @@ -113,9 +105,9 @@ func (n *Node) Valid() bool { return n != nil } -// Key returns the child nodes making the Key on a supported -// node. Panics otherwise. They are guaranteed to be all be of the -// Kind Key. A simple key would return just one element. +// Key returns the children nodes making the Key on a supported node. Panics +// otherwise. They are guaranteed to be all be of the Kind Key. A simple key +// would return just one element. func (n *Node) Key() Iterator { switch n.Kind { case KeyValue: diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/builder.go b/vendor/github.com/pelletier/go-toml/v2/unstable/builder.go new file mode 100644 index 00000000..9538e30d --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/builder.go @@ -0,0 +1,71 @@ +package unstable + +// root contains a full AST. +// +// It is immutable once constructed with Builder. +type root struct { + nodes []Node +} + +// Iterator over the top level nodes. +func (r *root) Iterator() Iterator { + it := Iterator{} + if len(r.nodes) > 0 { + it.node = &r.nodes[0] + } + return it +} + +func (r *root) at(idx reference) *Node { + return &r.nodes[idx] +} + +type reference int + +const invalidReference reference = -1 + +func (r reference) Valid() bool { + return r != invalidReference +} + +type builder struct { + tree root + lastIdx int +} + +func (b *builder) Tree() *root { + return &b.tree +} + +func (b *builder) NodeAt(ref reference) *Node { + return b.tree.at(ref) +} + +func (b *builder) Reset() { + b.tree.nodes = b.tree.nodes[:0] + b.lastIdx = 0 +} + +func (b *builder) Push(n Node) reference { + b.lastIdx = len(b.tree.nodes) + b.tree.nodes = append(b.tree.nodes, n) + return reference(b.lastIdx) +} + +func (b *builder) PushAndChain(n Node) reference { + newIdx := len(b.tree.nodes) + b.tree.nodes = append(b.tree.nodes, n) + if b.lastIdx >= 0 { + b.tree.nodes[b.lastIdx].next = newIdx - b.lastIdx + } + b.lastIdx = newIdx + return reference(b.lastIdx) +} + +func (b *builder) AttachChild(parent reference, child reference) { + b.tree.nodes[parent].child = int(child) - int(parent) +} + +func (b *builder) Chain(from reference, to reference) { + b.tree.nodes[from].next = int(to) - int(from) +} diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/doc.go b/vendor/github.com/pelletier/go-toml/v2/unstable/doc.go new file mode 100644 index 00000000..7ff26c53 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/doc.go @@ -0,0 +1,3 @@ +// Package unstable provides APIs that do not meet the backward compatibility +// guarantees yet. +package unstable diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/ast/kind.go b/vendor/github.com/pelletier/go-toml/v2/unstable/kind.go similarity index 81% rename from vendor/github.com/pelletier/go-toml/v2/internal/ast/kind.go rename to vendor/github.com/pelletier/go-toml/v2/unstable/kind.go index 2b50c67f..ff9df1be 100644 --- a/vendor/github.com/pelletier/go-toml/v2/internal/ast/kind.go +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/kind.go @@ -1,25 +1,26 @@ -package ast +package unstable import "fmt" +// Kind represents the type of TOML structure contained in a given Node. type Kind int const ( - // meta + // Meta Invalid Kind = iota Comment Key - // top level structures + // Top level structures Table ArrayTable KeyValue - // containers values + // Containers values Array InlineTable - // values + // Values String Bool Float @@ -30,6 +31,7 @@ const ( DateTime ) +// String implementation of fmt.Stringer. func (k Kind) String() string { switch k { case Invalid: diff --git a/vendor/github.com/pelletier/go-toml/v2/parser.go b/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go similarity index 70% rename from vendor/github.com/pelletier/go-toml/v2/parser.go rename to vendor/github.com/pelletier/go-toml/v2/unstable/parser.go index 9859a795..52db88e7 100644 --- a/vendor/github.com/pelletier/go-toml/v2/parser.go +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go @@ -1,50 +1,108 @@ -package toml +package unstable import ( "bytes" + "fmt" "unicode" - "github.com/pelletier/go-toml/v2/internal/ast" + "github.com/pelletier/go-toml/v2/internal/characters" "github.com/pelletier/go-toml/v2/internal/danger" ) -type parser struct { - builder ast.Builder - ref ast.Reference +// ParserError describes an error relative to the content of the document. +// +// It cannot outlive the instance of Parser it refers to, and may cause panics +// if the parser is reset. +type ParserError struct { + Highlight []byte + Message string + Key []string // optional +} + +// Error is the implementation of the error interface. +func (e *ParserError) Error() string { + return e.Message +} + +// NewParserError is a convenience function to create a ParserError +// +// Warning: Highlight needs to be a subslice of Parser.data, so only slices +// returned by Parser.Raw are valid candidates. +func NewParserError(highlight []byte, format string, args ...interface{}) error { + return &ParserError{ + Highlight: highlight, + Message: fmt.Errorf(format, args...).Error(), + } +} + +// Parser scans over a TOML-encoded document and generates an iterative AST. +// +// To prime the Parser, first reset it with the contents of a TOML document. +// Then, process all top-level expressions sequentially. See Example. +// +// Don't forget to check Error() after you're done parsing. +// +// Each top-level expression needs to be fully processed before calling +// NextExpression() again. Otherwise, calls to various Node methods may panic if +// the parser has moved on the next expression. +// +// For performance reasons, go-toml doesn't make a copy of the input bytes to +// the parser. Make sure to copy all the bytes you need to outlive the slice +// given to the parser. +// +// The parser doesn't provide nodes for comments yet, nor for whitespace. +type Parser struct { data []byte + builder builder + ref reference left []byte err error first bool } -func (p *parser) Range(b []byte) ast.Range { - return ast.Range{ +// Data returns the slice provided to the last call to Reset. +func (p *Parser) Data() []byte { + return p.data +} + +// Range returns a range description that corresponds to a given slice of the +// input. If the argument is not a subslice of the parser input, this function +// panics. +func (p *Parser) Range(b []byte) Range { + return Range{ Offset: uint32(danger.SubsliceOffset(p.data, b)), Length: uint32(len(b)), } } -func (p *parser) Raw(raw ast.Range) []byte { +// Raw returns the slice corresponding to the bytes in the given range. +func (p *Parser) Raw(raw Range) []byte { return p.data[raw.Offset : raw.Offset+raw.Length] } -func (p *parser) Reset(b []byte) { +// Reset brings the parser to its initial state for a given input. It wipes an +// reuses internal storage to reduce allocation. +func (p *Parser) Reset(b []byte) { p.builder.Reset() - p.ref = ast.InvalidReference + p.ref = invalidReference p.data = b p.left = b p.err = nil p.first = true } -//nolint:cyclop -func (p *parser) NextExpression() bool { +// NextExpression parses the next top-level expression. If an expression was +// successfully parsed, it returns true. If the parser is at the end of the +// document or an error occurred, it returns false. +// +// Retrieve the parsed expression with Expression(). +func (p *Parser) NextExpression() bool { if len(p.left) == 0 || p.err != nil { return false } p.builder.Reset() - p.ref = ast.InvalidReference + p.ref = invalidReference for { if len(p.left) == 0 || p.err != nil { @@ -73,15 +131,18 @@ func (p *parser) NextExpression() bool { } } -func (p *parser) Expression() *ast.Node { +// Expression returns a pointer to the node representing the last successfully +// parsed expresion. +func (p *Parser) Expression() *Node { return p.builder.NodeAt(p.ref) } -func (p *parser) Error() error { +// Error returns any error that has occured during parsing. +func (p *Parser) Error() error { return p.err } -func (p *parser) parseNewline(b []byte) ([]byte, error) { +func (p *Parser) parseNewline(b []byte) ([]byte, error) { if b[0] == '\n' { return b[1:], nil } @@ -91,14 +152,14 @@ func (p *parser) parseNewline(b []byte) ([]byte, error) { return rest, err } - return nil, newDecodeError(b[0:1], "expected newline but got %#U", b[0]) + return nil, NewParserError(b[0:1], "expected newline but got %#U", b[0]) } -func (p *parser) parseExpression(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseExpression(b []byte) (reference, []byte, error) { // expression = ws [ comment ] // expression =/ ws keyval ws [ comment ] // expression =/ ws table ws [ comment ] - ref := ast.InvalidReference + ref := invalidReference b = p.parseWhitespace(b) @@ -136,7 +197,7 @@ func (p *parser) parseExpression(b []byte) (ast.Reference, []byte, error) { return ref, b, nil } -func (p *parser) parseTable(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseTable(b []byte) (reference, []byte, error) { // table = std-table / array-table if len(b) > 1 && b[1] == '[' { return p.parseArrayTable(b) @@ -145,12 +206,12 @@ func (p *parser) parseTable(b []byte) (ast.Reference, []byte, error) { return p.parseStdTable(b) } -func (p *parser) parseArrayTable(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseArrayTable(b []byte) (reference, []byte, error) { // array-table = array-table-open key array-table-close // array-table-open = %x5B.5B ws ; [[ Double left square bracket // array-table-close = ws %x5D.5D ; ]] Double right square bracket - ref := p.builder.Push(ast.Node{ - Kind: ast.ArrayTable, + ref := p.builder.Push(Node{ + Kind: ArrayTable, }) b = b[2:] @@ -174,12 +235,12 @@ func (p *parser) parseArrayTable(b []byte) (ast.Reference, []byte, error) { return ref, b, err } -func (p *parser) parseStdTable(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseStdTable(b []byte) (reference, []byte, error) { // std-table = std-table-open key std-table-close // std-table-open = %x5B ws ; [ Left square bracket // std-table-close = ws %x5D ; ] Right square bracket - ref := p.builder.Push(ast.Node{ - Kind: ast.Table, + ref := p.builder.Push(Node{ + Kind: Table, }) b = b[1:] @@ -199,15 +260,15 @@ func (p *parser) parseStdTable(b []byte) (ast.Reference, []byte, error) { return ref, b, err } -func (p *parser) parseKeyval(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseKeyval(b []byte) (reference, []byte, error) { // keyval = key keyval-sep val - ref := p.builder.Push(ast.Node{ - Kind: ast.KeyValue, + ref := p.builder.Push(Node{ + Kind: KeyValue, }) key, b, err := p.parseKey(b) if err != nil { - return ast.InvalidReference, nil, err + return invalidReference, nil, err } // keyval-sep = ws %x3D ws ; = @@ -215,12 +276,12 @@ func (p *parser) parseKeyval(b []byte) (ast.Reference, []byte, error) { b = p.parseWhitespace(b) if len(b) == 0 { - return ast.InvalidReference, nil, newDecodeError(b, "expected = after a key, but the document ends there") + return invalidReference, nil, NewParserError(b, "expected = after a key, but the document ends there") } b, err = expect('=', b) if err != nil { - return ast.InvalidReference, nil, err + return invalidReference, nil, err } b = p.parseWhitespace(b) @@ -237,12 +298,12 @@ func (p *parser) parseKeyval(b []byte) (ast.Reference, []byte, error) { } //nolint:cyclop,funlen -func (p *parser) parseVal(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseVal(b []byte) (reference, []byte, error) { // val = string / boolean / array / inline-table / date-time / float / integer - ref := ast.InvalidReference + ref := invalidReference if len(b) == 0 { - return ref, nil, newDecodeError(b, "expected value, not eof") + return ref, nil, NewParserError(b, "expected value, not eof") } var err error @@ -259,8 +320,8 @@ func (p *parser) parseVal(b []byte) (ast.Reference, []byte, error) { } if err == nil { - ref = p.builder.Push(ast.Node{ - Kind: ast.String, + ref = p.builder.Push(Node{ + Kind: String, Raw: p.Range(raw), Data: v, }) @@ -277,8 +338,8 @@ func (p *parser) parseVal(b []byte) (ast.Reference, []byte, error) { } if err == nil { - ref = p.builder.Push(ast.Node{ - Kind: ast.String, + ref = p.builder.Push(Node{ + Kind: String, Raw: p.Range(raw), Data: v, }) @@ -287,22 +348,22 @@ func (p *parser) parseVal(b []byte) (ast.Reference, []byte, error) { return ref, b, err case 't': if !scanFollowsTrue(b) { - return ref, nil, newDecodeError(atmost(b, 4), "expected 'true'") + return ref, nil, NewParserError(atmost(b, 4), "expected 'true'") } - ref = p.builder.Push(ast.Node{ - Kind: ast.Bool, + ref = p.builder.Push(Node{ + Kind: Bool, Data: b[:4], }) return ref, b[4:], nil case 'f': if !scanFollowsFalse(b) { - return ref, nil, newDecodeError(atmost(b, 5), "expected 'false'") + return ref, nil, NewParserError(atmost(b, 5), "expected 'false'") } - ref = p.builder.Push(ast.Node{ - Kind: ast.Bool, + ref = p.builder.Push(Node{ + Kind: Bool, Data: b[:5], }) @@ -324,7 +385,7 @@ func atmost(b []byte, n int) []byte { return b[:n] } -func (p *parser) parseLiteralString(b []byte) ([]byte, []byte, []byte, error) { +func (p *Parser) parseLiteralString(b []byte) ([]byte, []byte, []byte, error) { v, rest, err := scanLiteralString(b) if err != nil { return nil, nil, nil, err @@ -333,19 +394,19 @@ func (p *parser) parseLiteralString(b []byte) ([]byte, []byte, []byte, error) { return v, v[1 : len(v)-1], rest, nil } -func (p *parser) parseInlineTable(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseInlineTable(b []byte) (reference, []byte, error) { // inline-table = inline-table-open [ inline-table-keyvals ] inline-table-close // inline-table-open = %x7B ws ; { // inline-table-close = ws %x7D ; } // inline-table-sep = ws %x2C ws ; , Comma // inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ] - parent := p.builder.Push(ast.Node{ - Kind: ast.InlineTable, + parent := p.builder.Push(Node{ + Kind: InlineTable, }) first := true - var child ast.Reference + var child reference b = b[1:] @@ -356,7 +417,7 @@ func (p *parser) parseInlineTable(b []byte) (ast.Reference, []byte, error) { b = p.parseWhitespace(b) if len(b) == 0 { - return parent, nil, newDecodeError(previousB[:1], "inline table is incomplete") + return parent, nil, NewParserError(previousB[:1], "inline table is incomplete") } if b[0] == '}' { @@ -371,7 +432,7 @@ func (p *parser) parseInlineTable(b []byte) (ast.Reference, []byte, error) { b = p.parseWhitespace(b) } - var kv ast.Reference + var kv reference kv, b, err = p.parseKeyval(b) if err != nil { @@ -394,7 +455,7 @@ func (p *parser) parseInlineTable(b []byte) (ast.Reference, []byte, error) { } //nolint:funlen,cyclop -func (p *parser) parseValArray(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseValArray(b []byte) (reference, []byte, error) { // array = array-open [ array-values ] ws-comment-newline array-close // array-open = %x5B ; [ // array-close = %x5D ; ] @@ -405,13 +466,13 @@ func (p *parser) parseValArray(b []byte) (ast.Reference, []byte, error) { arrayStart := b b = b[1:] - parent := p.builder.Push(ast.Node{ - Kind: ast.Array, + parent := p.builder.Push(Node{ + Kind: Array, }) first := true - var lastChild ast.Reference + var lastChild reference var err error for len(b) > 0 { @@ -421,7 +482,7 @@ func (p *parser) parseValArray(b []byte) (ast.Reference, []byte, error) { } if len(b) == 0 { - return parent, nil, newDecodeError(arrayStart[:1], "array is incomplete") + return parent, nil, NewParserError(arrayStart[:1], "array is incomplete") } if b[0] == ']' { @@ -430,7 +491,7 @@ func (p *parser) parseValArray(b []byte) (ast.Reference, []byte, error) { if b[0] == ',' { if first { - return parent, nil, newDecodeError(b[0:1], "array cannot start with comma") + return parent, nil, NewParserError(b[0:1], "array cannot start with comma") } b = b[1:] @@ -439,7 +500,7 @@ func (p *parser) parseValArray(b []byte) (ast.Reference, []byte, error) { return parent, nil, err } } else if !first { - return parent, nil, newDecodeError(b[0:1], "array elements must be separated by commas") + return parent, nil, NewParserError(b[0:1], "array elements must be separated by commas") } // TOML allows trailing commas in arrays. @@ -447,7 +508,7 @@ func (p *parser) parseValArray(b []byte) (ast.Reference, []byte, error) { break } - var valueRef ast.Reference + var valueRef reference valueRef, b, err = p.parseVal(b) if err != nil { return parent, nil, err @@ -472,7 +533,7 @@ func (p *parser) parseValArray(b []byte) (ast.Reference, []byte, error) { return parent, rest, err } -func (p *parser) parseOptionalWhitespaceCommentNewline(b []byte) ([]byte, error) { +func (p *Parser) parseOptionalWhitespaceCommentNewline(b []byte) ([]byte, error) { for len(b) > 0 { var err error b = p.parseWhitespace(b) @@ -501,7 +562,7 @@ func (p *parser) parseOptionalWhitespaceCommentNewline(b []byte) ([]byte, error) return b, nil } -func (p *parser) parseMultilineLiteralString(b []byte) ([]byte, []byte, []byte, error) { +func (p *Parser) parseMultilineLiteralString(b []byte) ([]byte, []byte, []byte, error) { token, rest, err := scanMultilineLiteralString(b) if err != nil { return nil, nil, nil, err @@ -520,7 +581,7 @@ func (p *parser) parseMultilineLiteralString(b []byte) ([]byte, []byte, []byte, } //nolint:funlen,gocognit,cyclop -func (p *parser) parseMultilineBasicString(b []byte) ([]byte, []byte, []byte, error) { +func (p *Parser) parseMultilineBasicString(b []byte) ([]byte, []byte, []byte, error) { // ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body // ml-basic-string-delim // ml-basic-string-delim = 3quotation-mark @@ -551,11 +612,11 @@ func (p *parser) parseMultilineBasicString(b []byte) ([]byte, []byte, []byte, er if !escaped { str := token[startIdx:endIdx] - verr := utf8TomlValidAlreadyEscaped(str) + verr := characters.Utf8TomlValidAlreadyEscaped(str) if verr.Zero() { return token, str, rest, nil } - return nil, nil, nil, newDecodeError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8") + return nil, nil, nil, NewParserError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8") } var builder bytes.Buffer @@ -635,13 +696,13 @@ func (p *parser) parseMultilineBasicString(b []byte) ([]byte, []byte, []byte, er builder.WriteRune(x) i += 8 default: - return nil, nil, nil, newDecodeError(token[i:i+1], "invalid escaped character %#U", c) + return nil, nil, nil, NewParserError(token[i:i+1], "invalid escaped character %#U", c) } i++ } else { - size := utf8ValidNext(token[i:]) + size := characters.Utf8ValidNext(token[i:]) if size == 0 { - return nil, nil, nil, newDecodeError(token[i:i+1], "invalid character %#U", c) + return nil, nil, nil, NewParserError(token[i:i+1], "invalid character %#U", c) } builder.Write(token[i : i+size]) i += size @@ -651,7 +712,7 @@ func (p *parser) parseMultilineBasicString(b []byte) ([]byte, []byte, []byte, er return token, builder.Bytes(), rest, nil } -func (p *parser) parseKey(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseKey(b []byte) (reference, []byte, error) { // key = simple-key / dotted-key // simple-key = quoted-key / unquoted-key // @@ -662,11 +723,11 @@ func (p *parser) parseKey(b []byte) (ast.Reference, []byte, error) { // dot-sep = ws %x2E ws ; . Period raw, key, b, err := p.parseSimpleKey(b) if err != nil { - return ast.InvalidReference, nil, err + return invalidReference, nil, err } - ref := p.builder.Push(ast.Node{ - Kind: ast.Key, + ref := p.builder.Push(Node{ + Kind: Key, Raw: p.Range(raw), Data: key, }) @@ -681,8 +742,8 @@ func (p *parser) parseKey(b []byte) (ast.Reference, []byte, error) { return ref, nil, err } - p.builder.PushAndChain(ast.Node{ - Kind: ast.Key, + p.builder.PushAndChain(Node{ + Kind: Key, Raw: p.Range(raw), Data: key, }) @@ -694,9 +755,9 @@ func (p *parser) parseKey(b []byte) (ast.Reference, []byte, error) { return ref, b, nil } -func (p *parser) parseSimpleKey(b []byte) (raw, key, rest []byte, err error) { +func (p *Parser) parseSimpleKey(b []byte) (raw, key, rest []byte, err error) { if len(b) == 0 { - return nil, nil, nil, newDecodeError(b, "expected key but found none") + return nil, nil, nil, NewParserError(b, "expected key but found none") } // simple-key = quoted-key / unquoted-key @@ -711,12 +772,12 @@ func (p *parser) parseSimpleKey(b []byte) (raw, key, rest []byte, err error) { key, rest = scanUnquotedKey(b) return key, key, rest, nil default: - return nil, nil, nil, newDecodeError(b[0:1], "invalid character at start of key: %c", b[0]) + return nil, nil, nil, NewParserError(b[0:1], "invalid character at start of key: %c", b[0]) } } //nolint:funlen,cyclop -func (p *parser) parseBasicString(b []byte) ([]byte, []byte, []byte, error) { +func (p *Parser) parseBasicString(b []byte) ([]byte, []byte, []byte, error) { // basic-string = quotation-mark *basic-char quotation-mark // quotation-mark = %x22 ; " // basic-char = basic-unescaped / escaped @@ -744,11 +805,11 @@ func (p *parser) parseBasicString(b []byte) ([]byte, []byte, []byte, error) { // validate the string and return a direct reference to the buffer. if !escaped { str := token[startIdx:endIdx] - verr := utf8TomlValidAlreadyEscaped(str) + verr := characters.Utf8TomlValidAlreadyEscaped(str) if verr.Zero() { return token, str, rest, nil } - return nil, nil, nil, newDecodeError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8") + return nil, nil, nil, NewParserError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8") } i := startIdx @@ -795,13 +856,13 @@ func (p *parser) parseBasicString(b []byte) ([]byte, []byte, []byte, error) { builder.WriteRune(x) i += 8 default: - return nil, nil, nil, newDecodeError(token[i:i+1], "invalid escaped character %#U", c) + return nil, nil, nil, NewParserError(token[i:i+1], "invalid escaped character %#U", c) } i++ } else { - size := utf8ValidNext(token[i:]) + size := characters.Utf8ValidNext(token[i:]) if size == 0 { - return nil, nil, nil, newDecodeError(token[i:i+1], "invalid character %#U", c) + return nil, nil, nil, NewParserError(token[i:i+1], "invalid character %#U", c) } builder.Write(token[i : i+size]) i += size @@ -813,7 +874,7 @@ func (p *parser) parseBasicString(b []byte) ([]byte, []byte, []byte, error) { func hexToRune(b []byte, length int) (rune, error) { if len(b) < length { - return -1, newDecodeError(b, "unicode point needs %d character, not %d", length, len(b)) + return -1, NewParserError(b, "unicode point needs %d character, not %d", length, len(b)) } b = b[:length] @@ -828,19 +889,19 @@ func hexToRune(b []byte, length int) (rune, error) { case 'A' <= c && c <= 'F': d = uint32(c - 'A' + 10) default: - return -1, newDecodeError(b[i:i+1], "non-hex character") + return -1, NewParserError(b[i:i+1], "non-hex character") } r = r*16 + d } if r > unicode.MaxRune || 0xD800 <= r && r < 0xE000 { - return -1, newDecodeError(b, "escape sequence is invalid Unicode code point") + return -1, NewParserError(b, "escape sequence is invalid Unicode code point") } return rune(r), nil } -func (p *parser) parseWhitespace(b []byte) []byte { +func (p *Parser) parseWhitespace(b []byte) []byte { // ws = *wschar // wschar = %x20 ; Space // wschar =/ %x09 ; Horizontal tab @@ -850,24 +911,24 @@ func (p *parser) parseWhitespace(b []byte) []byte { } //nolint:cyclop -func (p *parser) parseIntOrFloatOrDateTime(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseIntOrFloatOrDateTime(b []byte) (reference, []byte, error) { switch b[0] { case 'i': if !scanFollowsInf(b) { - return ast.InvalidReference, nil, newDecodeError(atmost(b, 3), "expected 'inf'") + return invalidReference, nil, NewParserError(atmost(b, 3), "expected 'inf'") } - return p.builder.Push(ast.Node{ - Kind: ast.Float, + return p.builder.Push(Node{ + Kind: Float, Data: b[:3], }), b[3:], nil case 'n': if !scanFollowsNan(b) { - return ast.InvalidReference, nil, newDecodeError(atmost(b, 3), "expected 'nan'") + return invalidReference, nil, NewParserError(atmost(b, 3), "expected 'nan'") } - return p.builder.Push(ast.Node{ - Kind: ast.Float, + return p.builder.Push(Node{ + Kind: Float, Data: b[:3], }), b[3:], nil case '+', '-': @@ -898,7 +959,7 @@ func (p *parser) parseIntOrFloatOrDateTime(b []byte) (ast.Reference, []byte, err return p.scanIntOrFloat(b) } -func (p *parser) scanDateTime(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) scanDateTime(b []byte) (reference, []byte, error) { // scans for contiguous characters in [0-9T:Z.+-], and up to one space if // followed by a digit. hasDate := false @@ -941,30 +1002,30 @@ byteLoop: } } - var kind ast.Kind + var kind Kind if hasTime { if hasDate { if hasTz { - kind = ast.DateTime + kind = DateTime } else { - kind = ast.LocalDateTime + kind = LocalDateTime } } else { - kind = ast.LocalTime + kind = LocalTime } } else { - kind = ast.LocalDate + kind = LocalDate } - return p.builder.Push(ast.Node{ + return p.builder.Push(Node{ Kind: kind, Data: b[:i], }), b[i:], nil } //nolint:funlen,gocognit,cyclop -func (p *parser) scanIntOrFloat(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) scanIntOrFloat(b []byte) (reference, []byte, error) { i := 0 if len(b) > 2 && b[0] == '0' && b[1] != '.' && b[1] != 'e' && b[1] != 'E' { @@ -990,8 +1051,8 @@ func (p *parser) scanIntOrFloat(b []byte) (ast.Reference, []byte, error) { } } - return p.builder.Push(ast.Node{ - Kind: ast.Integer, + return p.builder.Push(Node{ + Kind: Integer, Data: b[:i], }), b[i:], nil } @@ -1013,40 +1074,40 @@ func (p *parser) scanIntOrFloat(b []byte) (ast.Reference, []byte, error) { if c == 'i' { if scanFollowsInf(b[i:]) { - return p.builder.Push(ast.Node{ - Kind: ast.Float, + return p.builder.Push(Node{ + Kind: Float, Data: b[:i+3], }), b[i+3:], nil } - return ast.InvalidReference, nil, newDecodeError(b[i:i+1], "unexpected character 'i' while scanning for a number") + return invalidReference, nil, NewParserError(b[i:i+1], "unexpected character 'i' while scanning for a number") } if c == 'n' { if scanFollowsNan(b[i:]) { - return p.builder.Push(ast.Node{ - Kind: ast.Float, + return p.builder.Push(Node{ + Kind: Float, Data: b[:i+3], }), b[i+3:], nil } - return ast.InvalidReference, nil, newDecodeError(b[i:i+1], "unexpected character 'n' while scanning for a number") + return invalidReference, nil, NewParserError(b[i:i+1], "unexpected character 'n' while scanning for a number") } break } if i == 0 { - return ast.InvalidReference, b, newDecodeError(b, "incomplete number") + return invalidReference, b, NewParserError(b, "incomplete number") } - kind := ast.Integer + kind := Integer if isFloat { - kind = ast.Float + kind = Float } - return p.builder.Push(ast.Node{ + return p.builder.Push(Node{ Kind: kind, Data: b[:i], }), b[i:], nil @@ -1075,11 +1136,11 @@ func isValidBinaryRune(r byte) bool { func expect(x byte, b []byte) ([]byte, error) { if len(b) == 0 { - return nil, newDecodeError(b, "expected character %c but the document ended here", x) + return nil, NewParserError(b, "expected character %c but the document ended here", x) } if b[0] != x { - return nil, newDecodeError(b[0:1], "expected character %c", x) + return nil, NewParserError(b[0:1], "expected character %c", x) } return b[1:], nil diff --git a/vendor/github.com/pelletier/go-toml/v2/scanner.go b/vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go similarity index 79% rename from vendor/github.com/pelletier/go-toml/v2/scanner.go rename to vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go index bb445fab..af22ebbe 100644 --- a/vendor/github.com/pelletier/go-toml/v2/scanner.go +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go @@ -1,4 +1,6 @@ -package toml +package unstable + +import "github.com/pelletier/go-toml/v2/internal/characters" func scanFollows(b []byte, pattern string) bool { n := len(pattern) @@ -54,16 +56,16 @@ func scanLiteralString(b []byte) ([]byte, []byte, error) { case '\'': return b[:i+1], b[i+1:], nil case '\n', '\r': - return nil, nil, newDecodeError(b[i:i+1], "literal strings cannot have new lines") + return nil, nil, NewParserError(b[i:i+1], "literal strings cannot have new lines") } - size := utf8ValidNext(b[i:]) + size := characters.Utf8ValidNext(b[i:]) if size == 0 { - return nil, nil, newDecodeError(b[i:i+1], "invalid character") + return nil, nil, NewParserError(b[i:i+1], "invalid character") } i += size } - return nil, nil, newDecodeError(b[len(b):], "unterminated literal string") + return nil, nil, NewParserError(b[len(b):], "unterminated literal string") } func scanMultilineLiteralString(b []byte) ([]byte, []byte, error) { @@ -98,39 +100,39 @@ func scanMultilineLiteralString(b []byte) ([]byte, []byte, error) { i++ if i < len(b) && b[i] == '\'' { - return nil, nil, newDecodeError(b[i-3:i+1], "''' not allowed in multiline literal string") + return nil, nil, NewParserError(b[i-3:i+1], "''' not allowed in multiline literal string") } return b[:i], b[i:], nil } case '\r': if len(b) < i+2 { - return nil, nil, newDecodeError(b[len(b):], `need a \n after \r`) + return nil, nil, NewParserError(b[len(b):], `need a \n after \r`) } if b[i+1] != '\n' { - return nil, nil, newDecodeError(b[i:i+2], `need a \n after \r`) + return nil, nil, NewParserError(b[i:i+2], `need a \n after \r`) } i += 2 // skip the \n continue } - size := utf8ValidNext(b[i:]) + size := characters.Utf8ValidNext(b[i:]) if size == 0 { - return nil, nil, newDecodeError(b[i:i+1], "invalid character") + return nil, nil, NewParserError(b[i:i+1], "invalid character") } i += size } - return nil, nil, newDecodeError(b[len(b):], `multiline literal string not terminated by '''`) + return nil, nil, NewParserError(b[len(b):], `multiline literal string not terminated by '''`) } func scanWindowsNewline(b []byte) ([]byte, []byte, error) { const lenCRLF = 2 if len(b) < lenCRLF { - return nil, nil, newDecodeError(b, "windows new line expected") + return nil, nil, NewParserError(b, "windows new line expected") } if b[1] != '\n' { - return nil, nil, newDecodeError(b, `windows new line should be \r\n`) + return nil, nil, NewParserError(b, `windows new line should be \r\n`) } return b[:lenCRLF], b[lenCRLF:], nil @@ -165,11 +167,11 @@ func scanComment(b []byte) ([]byte, []byte, error) { if i+1 < len(b) && b[i+1] == '\n' { return b[:i+1], b[i+1:], nil } - return nil, nil, newDecodeError(b[i:i+1], "invalid character in comment") + return nil, nil, NewParserError(b[i:i+1], "invalid character in comment") } - size := utf8ValidNext(b[i:]) + size := characters.Utf8ValidNext(b[i:]) if size == 0 { - return nil, nil, newDecodeError(b[i:i+1], "invalid character in comment") + return nil, nil, NewParserError(b[i:i+1], "invalid character in comment") } i += size @@ -192,17 +194,17 @@ func scanBasicString(b []byte) ([]byte, bool, []byte, error) { case '"': return b[:i+1], escaped, b[i+1:], nil case '\n', '\r': - return nil, escaped, nil, newDecodeError(b[i:i+1], "basic strings cannot have new lines") + return nil, escaped, nil, NewParserError(b[i:i+1], "basic strings cannot have new lines") case '\\': if len(b) < i+2 { - return nil, escaped, nil, newDecodeError(b[i:i+1], "need a character after \\") + return nil, escaped, nil, NewParserError(b[i:i+1], "need a character after \\") } escaped = true i++ // skip the next character } } - return nil, escaped, nil, newDecodeError(b[len(b):], `basic string not terminated by "`) + return nil, escaped, nil, NewParserError(b[len(b):], `basic string not terminated by "`) } func scanMultilineBasicString(b []byte) ([]byte, bool, []byte, error) { @@ -243,27 +245,27 @@ func scanMultilineBasicString(b []byte) ([]byte, bool, []byte, error) { i++ if i < len(b) && b[i] == '"' { - return nil, escaped, nil, newDecodeError(b[i-3:i+1], `""" not allowed in multiline basic string`) + return nil, escaped, nil, NewParserError(b[i-3:i+1], `""" not allowed in multiline basic string`) } return b[:i], escaped, b[i:], nil } case '\\': if len(b) < i+2 { - return nil, escaped, nil, newDecodeError(b[len(b):], "need a character after \\") + return nil, escaped, nil, NewParserError(b[len(b):], "need a character after \\") } escaped = true i++ // skip the next character case '\r': if len(b) < i+2 { - return nil, escaped, nil, newDecodeError(b[len(b):], `need a \n after \r`) + return nil, escaped, nil, NewParserError(b[len(b):], `need a \n after \r`) } if b[i+1] != '\n' { - return nil, escaped, nil, newDecodeError(b[i:i+2], `need a \n after \r`) + return nil, escaped, nil, NewParserError(b[i:i+2], `need a \n after \r`) } i++ // skip the \n } } - return nil, escaped, nil, newDecodeError(b[len(b):], `multiline basic string not terminated by """`) + return nil, escaped, nil, NewParserError(b[len(b):], `multiline basic string not terminated by """`) } diff --git a/vendor/github.com/rivo/uniseg/README.md b/vendor/github.com/rivo/uniseg/README.md index 7e3d12e7..25e93468 100644 --- a/vendor/github.com/rivo/uniseg/README.md +++ b/vendor/github.com/rivo/uniseg/README.md @@ -133,6 +133,13 @@ Similarly, use - [`FirstSentence`](https://pkg.go.dev/github.com/rivo/uniseg#FirstSentence) or [`FirstSentenceInString`](https://pkg.go.dev/github.com/rivo/uniseg#FirstSentenceInString) for sentence segmentation only, and - [`FirstLineSegment`](https://pkg.go.dev/github.com/rivo/uniseg#FirstLineSegment) or [`FirstLineSegmentInString`](https://pkg.go.dev/github.com/rivo/uniseg#FirstLineSegmentInString) for line breaking / word wrapping (although using [`Step`](https://pkg.go.dev/github.com/rivo/uniseg#Step) or [`StepString`](https://pkg.go.dev/github.com/rivo/uniseg#StepString) is preferred as it will observe grapheme cluster boundaries). +Finally, if you need to reverse a string while preserving grapheme clusters, use [`ReverseString`](https://pkg.go.dev/github.com/rivo/uniseg#ReverseString): + +```go +fmt.Println(uniseg.ReverseString("🇩🇪🏳️‍🌈")) +// 🏳️‍🌈🇩🇪 +``` + ## Documentation Refer to https://pkg.go.dev/github.com/rivo/uniseg for the package's documentation. diff --git a/vendor/github.com/rivo/uniseg/doc.go b/vendor/github.com/rivo/uniseg/doc.go index 0fc2d8b4..11224ae2 100644 --- a/vendor/github.com/rivo/uniseg/doc.go +++ b/vendor/github.com/rivo/uniseg/doc.go @@ -70,10 +70,10 @@ broken. Monospace width, as referred to in this package, is the width of a string in a monospace font. This is commonly used in terminal user interfaces or text displays or editors that don't support proportional fonts. A width of 1 -corresponds to a single character cell. The C function [wcwidth()] and its +corresponds to a single character cell. The C function [wcswidth()] and its implementation in other programming languages is in widespread use for the same purpose. However, there is no standard for the calculation of such widths, and -this package differs from wcwidth() in a number of ways, presumably to generate +this package differs from wcswidth() in a number of ways, presumably to generate more visually pleasing results. To start, we assume that every code point has a width of 1, with the following @@ -103,6 +103,6 @@ Note that whether these widths appear correct depends on your application's render engine, to which extent it conforms to the Unicode Standard, and its choice of font. -[wcwidth()]: https://man7.org/linux/man-pages/man3/wcwidth.3.html +[wcswidth()]: https://man7.org/linux/man-pages/man3/wcswidth.3.html */ package uniseg diff --git a/vendor/github.com/rivo/uniseg/grapheme.go b/vendor/github.com/rivo/uniseg/grapheme.go index 997abbef..d5d4c09e 100644 --- a/vendor/github.com/rivo/uniseg/grapheme.go +++ b/vendor/github.com/rivo/uniseg/grapheme.go @@ -163,6 +163,25 @@ func GraphemeClusterCount(s string) (n int) { return } +// ReverseString reverses the given string while observing grapheme cluster +// boundaries. +func ReverseString(s string) string { + str := []byte(s) + reversed := make([]byte, len(str)) + state := -1 + index := len(str) + for len(str) > 0 { + var cluster []byte + cluster, str, _, state = FirstGraphemeCluster(str, state) + index -= len(cluster) + copy(reversed[index:], cluster) + if index <= len(str)/2 { + break + } + } + return string(reversed) +} + // The number of bits the grapheme property must be shifted to make place for // grapheme states. const shiftGraphemePropState = 4 diff --git a/vendor/github.com/samber/lo/CHANGELOG.md b/vendor/github.com/samber/lo/CHANGELOG.md index 5665cc8a..ea3ef7a5 100644 --- a/vendor/github.com/samber/lo/CHANGELOG.md +++ b/vendor/github.com/samber/lo/CHANGELOG.md @@ -2,6 +2,50 @@ @samber: I sometimes forget to update this file. Ping me on [Twitter](https://twitter.com/samuelberthe) or open an issue in case of error. We need to keep a clear changelog for easier lib upgrade. +## 1.37.0 (2022-12-15) + +Adding: +- lo.PartialX +- lo.Transaction + +Improvement: +- lo.Associate / lo.SliceToMap: faster memory allocation + +Chore: +- Remove *_test.go files from releases, in order to cleanup dev dependencies + +## 1.36.0 (2022-11-28) + +Adding: +- lo.AttemptWhile +- lo.AttemptWhileWithDelay + +## 1.35.0 (2022-11-15) + +Adding: +- lo.RandomString +- lo.BufferWithTimeout (alias to lo.BatchWithTimeout) +- lo.Buffer (alias to lo.Batch) + +Change: +- lo.Slice: avoid panic caused by out-of-bounds + +Deprecation: +- lo.BatchWithTimeout +- lo.Batch + +## 1.34.0 (2022-11-12) + +Improving: +- lo.Union: faster and can receive more than 2 lists + +Adding: +- lo.FanIn (alias to lo.ChannelMerge) +- lo.FanOut + +Deprecation: +- lo.ChannelMerge + ## 1.33.0 (2022-10-14) Adding: diff --git a/vendor/github.com/samber/lo/README.md b/vendor/github.com/samber/lo/README.md index 06803933..ffb7c438 100644 --- a/vendor/github.com/samber/lo/README.md +++ b/vendor/github.com/samber/lo/README.md @@ -1,16 +1,19 @@ # lo [![tag](https://img.shields.io/github/tag/samber/lo.svg)](https://github.com/samber/lo/releases) +![Go Version](https://img.shields.io/badge/Go-%3E%3D%201.18-%23007d9c) [![GoDoc](https://godoc.org/github.com/samber/lo?status.svg)](https://pkg.go.dev/github.com/samber/lo) ![Build Status](https://github.com/samber/lo/actions/workflows/go.yml/badge.svg) [![Go report](https://goreportcard.com/badge/github.com/samber/lo)](https://goreportcard.com/report/github.com/samber/lo) -[![codecov](https://codecov.io/gh/samber/lo/branch/master/graph/badge.svg)](https://codecov.io/gh/samber/lo) +[![Coverage](https://img.shields.io/codecov/c/github/samber/lo)](https://codecov.io/gh/samber/lo) +[![Contributors](https://img.shields.io/github/contributors/samber/lo)](https://github.com/samber/lo/graphs/contributors) +[![License](https://img.shields.io/github/license/samber/lo)](./LICENSE) ✨ **`samber/lo` is a Lodash-style Go library based on Go 1.18+ Generics.** This project started as an experiment with the new generics implementation. It may look like [Lodash](https://github.com/lodash/lodash) in some aspects. I used to code with the fantastic ["go-funk"](https://github.com/thoas/go-funk) package, but "go-funk" uses reflection and therefore is not typesafe. -As expected, benchmarks demonstrate that generics will be much faster than implementations based on the "reflect" package. Benchmarks also show similar performance gains compared to pure `for` loops. [See below](#-benchmark). +As expected, benchmarks demonstrate that generics are much faster than implementations based on the "reflect" package. Benchmarks also show similar performance gains compared to pure `for` loops. [See below](#-benchmark). In the future, 5 to 10 helpers will overlap with those coming into the Go standard library (under package names `slices` and `maps`). I feel this library is legitimate and offers many more valuable abstractions. @@ -23,6 +26,8 @@ In the future, 5 to 10 helpers will overlap with those coming into the Go standa I wanted a **short name**, similar to "Lodash" and no Go package currently uses this name. +![](img/logo-full.png) + ## 🚀 Install ```sh @@ -138,6 +143,7 @@ Supported math helpers: Supported helpers for strings: +- [RandomString](#randomstring) - [Substring](#substring) - [ChunkString](#chunkstring) - [RuneLength](#runelength) @@ -154,9 +160,10 @@ Supported helpers for channels: - [ChannelDispatcher](#channeldispatcher) - [SliceToChannel](#slicetochannel) - [Generator](#generator) -- [Batch](#batch) -- [BatchWithTimeout](#batchwithtimeout) -- [ChannelMerge](#channelmerge) +- [Buffer](#buffer) +- [BufferWithTimeout](#bufferwithtimeout) +- [FanIn](#fanin) +- [FanOut](#fanout) Supported intersection helpers: @@ -219,14 +226,18 @@ Type manipulation helpers: Function helpers: - [Partial](#partial) +- [Partial2 -> Partial5](#partial2---partial5) Concurrency helpers: - [Attempt](#attempt) +- [AttemptWhile](#attemptwhile) - [AttemptWithDelay](#attemptwithdelay) +- [AttemptWhileWithDelay](#attemptwhilewithdelay) - [Debounce](#debounce) - [Synchronize](#synchronize) - [Async](#async) +- [Transaction](#transaction) Error handling: @@ -515,7 +526,7 @@ flat := lo.Flatten[int]([][]int{{0, 1}, {2, 3, 4, 5}}) ### Interleave -Round-robbin alternating input slices and sequentially appending value at index into result. +Round-robin alternating input slices and sequentially appending value at index into result. ```go interleaved := lo.Interleave[int]([]int{1, 4, 7}, []int{2, 5, 8}, []int{3, 6, 9}) @@ -1201,6 +1212,17 @@ sum := lo.SumBy(strings, func(item string) int { [[play](https://go.dev/play/p/Dz_a_7jN_ca)] +### RandomString + +Returns a random string of the specified length and made of the specified charset. + +```go +str := lo.RandomString(5, lo.LettersCharset) +// example: "eIGbt" +``` + +[[play](https://go.dev/play/p/rRseOQVVum4)] + ### Substring Return part of a string. @@ -1432,16 +1454,16 @@ for v := range lo.Generator(2, generator) { // prints 1, then 2, then 3 ``` -### Batch +### Buffer Creates a slice of n elements from a channel. Returns the slice, the slice length, the read time and the channel status (opened/closed). ```go ch := lo.SliceToChannel(2, []int{1, 2, 3, 4, 5}) -items1, length1, duration1, ok1 := lo.Batch(ch, 3) +items1, length1, duration1, ok1 := lo.Buffer(ch, 3) // []int{1, 2, 3}, 3, 0s, true -items2, length2, duration2, ok2 := lo.Batch(ch, 3) +items2, length2, duration2, ok2 := lo.Buffer(ch, 3) // []int{4, 5}, 2, 0s, false ``` @@ -1452,7 +1474,7 @@ ch := readFromQueue() for { // read 1k items - items, length, _, ok := lo.Batch(ch, 1000) + items, length, _, ok := lo.Buffer(ch, 1000) // do batching stuff @@ -1462,7 +1484,7 @@ for { } ``` -### BatchWithTimeout +### BufferWithTimeout Creates a slice of n elements from a channel, with timeout. Returns the slice, the slice length, the read time and the channel status (opened/closed). @@ -1476,11 +1498,11 @@ generator := func(yield func(int)) { ch := lo.Generator(0, generator) -items1, length1, duration1, ok1 := lo.BatchWithTimeout(ch, 3, 100*time.Millisecond) +items1, length1, duration1, ok1 := lo.BufferWithTimeout(ch, 3, 100*time.Millisecond) // []int{1, 2}, 2, 100ms, true -items2, length2, duration2, ok2 := lo.BatchWithTimeout(ch, 3, 100*time.Millisecond) +items2, length2, duration2, ok2 := lo.BufferWithTimeout(ch, 3, 100*time.Millisecond) // []int{3, 4, 5}, 3, 75ms, true -items3, length3, duration2, ok3 := lo.BatchWithTimeout(ch, 3, 100*time.Millisecond) +items3, length3, duration2, ok3 := lo.BufferWithTimeout(ch, 3, 100*time.Millisecond) // []int{}, 0, 10ms, false ``` @@ -1492,7 +1514,7 @@ ch := readFromQueue() for { // read 1k items // wait up to 1 second - items, length, _, ok := lo.BatchWithTimeout(ch, 1000, 1*time.Second) + items, length, _, ok := lo.BufferWithTimeout(ch, 1000, 1*time.Second) // do batching stuff @@ -1515,7 +1537,7 @@ consumer := func(c <-chan int) { for { // read 1k items // wait up to 1 second - items, length, _, ok := lo.BatchWithTimeout(ch, 1000, 1*time.Second) + items, length, _, ok := lo.BufferWithTimeout(ch, 1000, 1*time.Second) // do batching stuff @@ -1530,16 +1552,28 @@ for i := range children { } ``` -### ChannelMerge +### FanIn -Collects messages from multiple input channels into a single buffered channel. Output messages has no priority. +Merge messages from multiple input channels into a single buffered channel. Output messages has no priority. When all upstream channels reach EOF, downstream channel closes. ```go stream1 := make(chan int, 42) stream2 := make(chan int, 42) stream3 := make(chan int, 42) -all := lo.ChannelMerge(100, stream1, stream2, stream3) +all := lo.FanIn(100, stream1, stream2, stream3) +// <-chan int +``` + +### FanOut + +Broadcasts all the upstream messages to multiple downstream channels. When upstream channel reach EOF, downstream channels close. If any downstream channels is full, broadcasting is paused. + +```go +stream := make(chan int, 42) + +all := lo.FanOut(5, 100, stream) +// [5]<-chan int ``` ### Contains @@ -1664,10 +1698,10 @@ left, right := lo.Difference[int]([]int{0, 1, 2, 3, 4, 5}, []int{0, 1, 2, 3, 4, ### Union -Returns all distinct elements from both collections. Result will not change the order of elements relatively. +Returns all distinct elements from given collections. Result will not change the order of elements relatively. ```go -union := lo.Union[int]([]int{0, 1, 2, 3, 4, 5}, []int{0, 2, 10}) +union := lo.Union[int]([]int{0, 1, 2, 3, 4, 5}, []int{0, 2}, []int{0, 10}) // []int{0, 1, 2, 3, 4, 5, 10} ``` @@ -2242,6 +2276,21 @@ f(42) // 47 ``` +### Partial2 -> Partial5 + +Returns new function that, when called, has its first argument set to the provided value. + +```go +add := func(x, y, z int) int { return x + y + z } +f := lo.Partial2(add, 42) + +f(10, 5) +// 57 + +f(42, -4) +// 80 +``` + ### Attempt Invokes a function N times until it returns valid output. Returning either the caught error or nil. When first argument is less than `1`, the function runs until a successful response is returned. @@ -2305,6 +2354,56 @@ For more advanced retry strategies (delay, exponential backoff...), please take [[play](https://go.dev/play/p/tVs6CygC7m1)] +### AttemptWhile + +Invokes a function N times until it returns valid output. Returning either the caught error or nil, and along with a bool value to identifying whether it needs invoke function continuously. It will terminate the invoke immediately if second bool value is returned with falsy value. + +When first argument is less than `1`, the function runs until a successful response is returned. + +```go +count1, err1 := lo.AttemptWhile(5, func(i int) (error, bool) { + err := doMockedHTTPRequest(i) + if err != nil { + if errors.Is(err, ErrBadRequest) { // lets assume ErrBadRequest is a critical error that needs to terminate the invoke + return err, false // flag the second return value as false to terminate the invoke + } + + return err, true + } + + return nil, false +}) +``` + +For more advanced retry strategies (delay, exponential backoff...), please take a look on [cenkalti/backoff](https://github.com/cenkalti/backoff). + +[[play](https://go.dev/play/p/M2wVq24PaZM)] + +### AttemptWhileWithDelay + +Invokes a function N times until it returns valid output, with a pause between each call. Returning either the caught error or nil, and along with a bool value to identifying whether it needs to invoke function continuously. It will terminate the invoke immediately if second bool value is returned with falsy value. + +When first argument is less than `1`, the function runs until a successful response is returned. + +```go +count1, time1, err1 := lo.AttemptWhileWithDelay(5, time.Millisecond, func(i int, d time.Duration) (error, bool) { + err := doMockedHTTPRequest(i) + if err != nil { + if errors.Is(err, ErrBadRequest) { // lets assume ErrBadRequest is a critical error that needs to terminate the invoke + return err, false // flag the second return value as false to terminate the invoke + } + + return err, true + } + + return nil, false +}) +``` + +For more advanced retry strategies (delay, exponential backoff...), please take a look on [cenkalti/backoff](https://github.com/cenkalti/backoff). + +[[play](https://go.dev/play/p/cfcmhvLO-nv)] + ### Debounce `NewDebounce` creates a debounced instance that delays invoking functions given until after wait milliseconds have elapsed, until `cancel` is called. @@ -2384,6 +2483,58 @@ ch := lo.Async2(func() (int, string) { // chan lo.Tuple2[int, string] ({42, "Hello"}) ``` +### Transaction + +Implements a Saga pattern. + +```go +transaction := NewTransaction[int](). + Then( + func(state int) (int, error) { + fmt.Println("step 1") + return state + 10, nil + }, + func(state int) int { + fmt.Println("rollback 1") + return state - 10 + }, + ). + Then( + func(state int) (int, error) { + fmt.Println("step 2") + return state + 15, nil + }, + func(state int) int { + fmt.Println("rollback 2") + return state - 15 + }, + ). + Then( + func(state int) (int, error) { + fmt.Println("step 3") + + if true { + return state, fmt.Errorf("error") + } + + return state + 42, nil + }, + func(state int) int { + fmt.Println("rollback 3") + return state - 42 + }, + ) + +_, _ = transaction.Process(-5) + +// Output: +// step 1 +// step 2 +// step 3 +// rollback 2 +// rollback 1 +``` + ### Validate Helper function that creates an error when a condition is not met. @@ -2683,9 +2834,9 @@ make test make watch-test ``` -## 👤 Authors +## 👤 Contributors -- Samuel Berthe +![Contributors](https://contrib.rocks/image?repo=samber/lo) ## 💫 Show your support diff --git a/vendor/github.com/samber/lo/channel.go b/vendor/github.com/samber/lo/channel.go index 41c5bfa8..e5af2504 100644 --- a/vendor/github.com/samber/lo/channel.go +++ b/vendor/github.com/samber/lo/channel.go @@ -193,9 +193,9 @@ func Generator[T any](bufferSize int, generator func(yield func(T))) <-chan T { return ch } -// Batch creates a slice of n elements from a channel. Returns the slice and the slice length. +// Buffer creates a slice of n elements from a channel. Returns the slice and the slice length. // @TODO: we should probably provide an helper that reuse the same buffer. -func Batch[T any](ch <-chan T, size int) (collection []T, length int, readTime time.Duration, ok bool) { +func Buffer[T any](ch <-chan T, size int) (collection []T, length int, readTime time.Duration, ok bool) { buffer := make([]T, 0, size) index := 0 now := time.Now() @@ -212,9 +212,15 @@ func Batch[T any](ch <-chan T, size int) (collection []T, length int, readTime t return buffer, index, time.Since(now), true } -// BatchWithTimeout creates a slice of n elements from a channel, with timeout. Returns the slice and the slice length. +// Buffer creates a slice of n elements from a channel. Returns the slice and the slice length. +// Deprecated: Use lo.Buffer instead. +func Batch[T any](ch <-chan T, size int) (collection []T, length int, readTime time.Duration, ok bool) { + return Buffer(ch, size) +} + +// BufferWithTimeout creates a slice of n elements from a channel, with timeout. Returns the slice and the slice length. // @TODO: we should probably provide an helper that reuse the same buffer. -func BatchWithTimeout[T any](ch <-chan T, size int, timeout time.Duration) (collection []T, length int, readTime time.Duration, ok bool) { +func BufferWithTimeout[T any](ch <-chan T, size int, timeout time.Duration) (collection []T, length int, readTime time.Duration, ok bool) { expire := time.NewTimer(timeout) defer expire.Stop() @@ -239,9 +245,15 @@ func BatchWithTimeout[T any](ch <-chan T, size int, timeout time.Duration) (coll return buffer, index, time.Since(now), true } -// ChannelMerge collects messages from multiple input channels into a single buffered channel. -// Output messages has no priority. -func ChannelMerge[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T { +// BufferWithTimeout creates a slice of n elements from a channel, with timeout. Returns the slice and the slice length. +// Deprecated: Use lo.BufferWithTimeout instead. +func BatchWithTimeout[T any](ch <-chan T, size int, timeout time.Duration) (collection []T, length int, readTime time.Duration, ok bool) { + return BufferWithTimeout(ch, size, timeout) +} + +// FanIn collects messages from multiple input channels into a single buffered channel. +// Output messages has no priority. When all upstream channels reach EOF, downstream channel closes. +func FanIn[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T { out := make(chan T, channelBufferCap) var wg sync.WaitGroup @@ -263,3 +275,32 @@ func ChannelMerge[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T { }() return out } + +// ChannelMerge collects messages from multiple input channels into a single buffered channel. +// Output messages has no priority. When all upstream channels reach EOF, downstream channel closes. +// Deprecated: Use lo.FanIn instead. +func ChannelMerge[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T { + return FanIn(channelBufferCap, upstreams...) +} + +// FanOut broadcasts all the upstream messages to multiple downstream channels. +// When upstream channel reach EOF, downstream channels close. If any downstream +// channels is full, broadcasting is paused. +func FanOut[T any](count int, channelsBufferCap int, upstream <-chan T) []<-chan T { + downstreams := createChannels[T](count, channelsBufferCap) + + go func() { + for msg := range upstream { + for i := range downstreams { + downstreams[i] <- msg + } + } + + // Close out once all the output goroutines are done. + for i := range downstreams { + close(downstreams[i]) + } + }() + + return channelsToReadOnly(downstreams) +} diff --git a/vendor/github.com/samber/lo/docker-compose.yml b/vendor/github.com/samber/lo/docker-compose.yml deleted file mode 100644 index 511e85fd..00000000 --- a/vendor/github.com/samber/lo/docker-compose.yml +++ /dev/null @@ -1,9 +0,0 @@ -version: '3' - -services: - dev: - image: golang:1.18-bullseye - volumes: - - ./:/go/src/github.com/samber/lo - working_dir: /go/src/github.com/samber/lo - command: make watch-test diff --git a/vendor/github.com/samber/lo/find.go b/vendor/github.com/samber/lo/find.go index 751410a0..f8caeb89 100644 --- a/vendor/github.com/samber/lo/find.go +++ b/vendor/github.com/samber/lo/find.go @@ -352,7 +352,7 @@ func Sample[T any](collection []T) T { func Samples[T any](collection []T, count int) []T { size := len(collection) - cOpy := append([]T{}, collection...) + copy := append([]T{}, collection...) results := []T{} @@ -360,12 +360,12 @@ func Samples[T any](collection []T, count int) []T { copyLength := size - i index := rand.Intn(size - i) - results = append(results, cOpy[index]) + results = append(results, copy[index]) // Removes element. // It is faster to swap with last element and remove it. - cOpy[index] = cOpy[copyLength-1] - cOpy = cOpy[:copyLength-1] + copy[index] = copy[copyLength-1] + copy = copy[:copyLength-1] } return results diff --git a/vendor/github.com/samber/lo/func.go b/vendor/github.com/samber/lo/func.go index 5a51f2d6..5fa1cbc8 100644 --- a/vendor/github.com/samber/lo/func.go +++ b/vendor/github.com/samber/lo/func.go @@ -6,3 +6,36 @@ func Partial[T1, T2, R any](f func(a T1, b T2) R, arg1 T1) func(T2) R { return f(arg1, t2) } } + +// Partial1 returns new function that, when called, has its first argument set to the provided value. +func Partial1[T1, T2, R any](f func(T1, T2) R, arg1 T1) func(T2) R { + return Partial(f, arg1) +} + +// Partial2 returns new function that, when called, has its first argument set to the provided value. +func Partial2[T1, T2, T3, R any](f func(T1, T2, T3) R, arg1 T1) func(T2, T3) R { + return func(t2 T2, t3 T3) R { + return f(arg1, t2, t3) + } +} + +// Partial3 returns new function that, when called, has its first argument set to the provided value. +func Partial3[T1, T2, T3, T4, R any](f func(T1, T2, T3, T4) R, arg1 T1) func(T2, T3, T4) R { + return func(t2 T2, t3 T3, t4 T4) R { + return f(arg1, t2, t3, t4) + } +} + +// Partial4 returns new function that, when called, has its first argument set to the provided value. +func Partial4[T1, T2, T3, T4, T5, R any](f func(T1, T2, T3, T4, T5) R, arg1 T1) func(T2, T3, T4, T5) R { + return func(t2 T2, t3 T3, t4 T4, t5 T5) R { + return f(arg1, t2, t3, t4, t5) + } +} + +// Partial5 returns new function that, when called, has its first argument set to the provided value +func Partial5[T1, T2, T3, T4, T5, T6, R any](f func(T1, T2, T3, T4, T5, T6) R, arg1 T1) func(T2, T3, T4, T5, T6) R { + return func(t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) R { + return f(arg1, t2, t3, t4, t5, t6) + } +} diff --git a/vendor/github.com/samber/lo/intersect.go b/vendor/github.com/samber/lo/intersect.go index 38a0052b..cf6cab3d 100644 --- a/vendor/github.com/samber/lo/intersect.go +++ b/vendor/github.com/samber/lo/intersect.go @@ -141,35 +141,18 @@ func Difference[T comparable](list1 []T, list2 []T) ([]T, []T) { return left, right } -// Union returns all distinct elements from both collections. +// Union returns all distinct elements from given collections. // result returns will not change the order of elements relatively. -func Union[T comparable](list1 []T, list2 []T) []T { +func Union[T comparable](lists ...[]T) []T { result := []T{} - seen := map[T]struct{}{} - hasAdd := map[T]struct{}{} - - for _, e := range list1 { - seen[e] = struct{}{} - } - for _, e := range list2 { - seen[e] = struct{}{} - } - - for _, e := range list1 { - if _, ok := seen[e]; ok { - result = append(result, e) - hasAdd[e] = struct{}{} - } - } - - for _, e := range list2 { - if _, ok := hasAdd[e]; ok { - continue - } - if _, ok := seen[e]; ok { - result = append(result, e) + for _, list := range lists { + for _, e := range list { + if _, ok := seen[e]; !ok { + seen[e] = struct{}{} + result = append(result, e) + } } } diff --git a/vendor/github.com/samber/lo/retry.go b/vendor/github.com/samber/lo/retry.go index 0303f84a..0d46f265 100644 --- a/vendor/github.com/samber/lo/retry.go +++ b/vendor/github.com/samber/lo/retry.go @@ -101,4 +101,110 @@ func AttemptWithDelay(maxIteration int, delay time.Duration, f func(index int, d return maxIteration, time.Since(start), err } +// AttemptWhile invokes a function N times until it returns valid output. +// Returning either the caught error or nil, and along with a bool value to identify +// whether it needs invoke function continuously. It will terminate the invoke +// immediately if second bool value is returned with falsy value. When first +// argument is less than `1`, the function runs until a successful response is +// returned. +func AttemptWhile(maxIteration int, f func(int) (error, bool)) (int, error) { + var err error + var shouldContinueInvoke bool + + for i := 0; maxIteration <= 0 || i < maxIteration; i++ { + // for retries >= 0 { + err, shouldContinueInvoke = f(i) + if !shouldContinueInvoke { // if shouldContinueInvoke is false, then return immediately + return i + 1, err + } + if err == nil { + return i + 1, nil + } + } + + return maxIteration, err +} + +// AttemptWhileWithDelay invokes a function N times until it returns valid output, +// with a pause between each call. Returning either the caught error or nil, and along +// with a bool value to identify whether it needs to invoke function continuously. +// It will terminate the invoke immediately if second bool value is returned with falsy +// value. When first argument is less than `1`, the function runs until a successful +// response is returned. +func AttemptWhileWithDelay(maxIteration int, delay time.Duration, f func(int, time.Duration) (error, bool)) (int, time.Duration, error) { + var err error + var shouldContinueInvoke bool + + start := time.Now() + + for i := 0; maxIteration <= 0 || i < maxIteration; i++ { + err, shouldContinueInvoke = f(i, time.Since(start)) + if !shouldContinueInvoke { // if shouldContinueInvoke is false, then return immediately + return i + 1, time.Since(start), err + } + if err == nil { + return i + 1, time.Since(start), nil + } + + if maxIteration <= 0 || i+1 < maxIteration { + time.Sleep(delay) + } + } + + return maxIteration, time.Since(start), err +} + +type transactionStep[T any] struct { + exec func(T) (T, error) + onRollback func(T) T +} + +// NewTransaction instanciate a new transaction. +func NewTransaction[T any]() *Transaction[T] { + return &Transaction[T]{ + steps: []transactionStep[T]{}, + } +} + +// Transaction implements a Saga pattern +type Transaction[T any] struct { + steps []transactionStep[T] +} + +// Then adds a step to the chain of callbacks. It returns the same Transaction. +func (t *Transaction[T]) Then(exec func(T) (T, error), onRollback func(T) T) *Transaction[T] { + t.steps = append(t.steps, transactionStep[T]{ + exec: exec, + onRollback: onRollback, + }) + + return t +} + +// Process runs the Transaction steps and rollbacks in case of errors. +func (t *Transaction[T]) Process(state T) (T, error) { + var i int + var err error + + for i < len(t.steps) { + state, err = t.steps[i].exec(state) + if err != nil { + break + } + + i++ + } + + if err == nil { + return state, nil + } + + for i > 0 { + i-- + state = t.steps[i].onRollback(state) + } + + return state, err +} + // throttle ? diff --git a/vendor/github.com/samber/lo/slice.go b/vendor/github.com/samber/lo/slice.go index 8ef4addc..b2e92ad2 100644 --- a/vendor/github.com/samber/lo/slice.go +++ b/vendor/github.com/samber/lo/slice.go @@ -229,7 +229,7 @@ func Flatten[T any](collection [][]T) []T { return result } -// Interleave round-robbin alternating input slices and sequentially appending value at index into result +// Interleave round-robin alternating input slices and sequentially appending value at index into result // Play: https://go.dev/play/p/DDhlwrShbwe func Interleave[T any](collections ...[]T) []T { if len(collections) == 0 { @@ -345,7 +345,7 @@ func KeyBy[K comparable, V any](collection []V, iteratee func(item V) K) map[K]V // The order of keys in returned map is not specified and is not guaranteed to be the same from the original array. // Play: https://go.dev/play/p/WHa2CfMO3Lr func Associate[T any, K comparable, V any](collection []T, transform func(item T) (K, V)) map[K]V { - result := make(map[K]V) + result := make(map[K]V, len(collection)) for _, t := range collection { k, v := transform(t) @@ -513,10 +513,16 @@ func Slice[T any](collection []T, start int, end int) []T { if start > size { start = size } + if start < 0 { + start = 0 + } if end > size { end = size } + if end < 0 { + end = 0 + } return collection[start:end] } diff --git a/vendor/github.com/samber/lo/string.go b/vendor/github.com/samber/lo/string.go index a63167cb..dfe1050b 100644 --- a/vendor/github.com/samber/lo/string.go +++ b/vendor/github.com/samber/lo/string.go @@ -1,9 +1,38 @@ package lo import ( + "math/rand" "unicode/utf8" ) +var ( + LowerCaseLettersCharset = []rune("abcdefghijklmnopqrstuvwxyz") + UpperCaseLettersCharset = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ") + LettersCharset = append(LowerCaseLettersCharset, UpperCaseLettersCharset...) + NumbersCharset = []rune("0123456789") + AlphanumericCharset = append(LettersCharset, NumbersCharset...) + SpecialCharset = []rune("!@#$%^&*()_+-=[]{}|;':\",./<>?") + AllCharset = append(AlphanumericCharset, SpecialCharset...) +) + +// RandomString return a random string. +// Play: https://go.dev/play/p/rRseOQVVum4 +func RandomString(size int, charset []rune) string { + if size <= 0 { + panic("lo.RandomString: Size parameter must be greater than 0") + } + if len(charset) <= 0 { + panic("lo.RandomString: Charset parameter must not be empty") + } + + b := make([]rune, size) + possibleCharactersCount := len(charset) + for i := range b { + b[i] = charset[rand.Intn(possibleCharactersCount)] + } + return string(b) +} + // Substring return part of a string. // Play: https://go.dev/play/p/TQlxQi82Lu1 func Substring[T ~string](str T, offset int, length uint) T { diff --git a/vendor/github.com/samber/lo/test.go b/vendor/github.com/samber/lo/test.go deleted file mode 100644 index 26db4c25..00000000 --- a/vendor/github.com/samber/lo/test.go +++ /dev/null @@ -1,32 +0,0 @@ -package lo - -import ( - "os" - "testing" - "time" -) - -// https://github.com/stretchr/testify/issues/1101 -func testWithTimeout(t *testing.T, timeout time.Duration) { - t.Helper() - - testFinished := make(chan struct{}) - t.Cleanup(func() { close(testFinished) }) - - go func() { - select { - case <-testFinished: - case <-time.After(timeout): - t.Errorf("test timed out after %s", timeout) - os.Exit(1) - } - }() -} - -type foo struct { - bar string -} - -func (f foo) Clone() foo { - return foo{f.bar} -} diff --git a/vendor/github.com/samber/mo/README.md b/vendor/github.com/samber/mo/README.md index b8e2e162..6875ca28 100644 --- a/vendor/github.com/samber/mo/README.md +++ b/vendor/github.com/samber/mo/README.md @@ -1,10 +1,12 @@ # mo - Monads [![tag](https://img.shields.io/github/tag/samber/mo.svg)](https://github.com/samber/mo/releases) +![Go Version](https://img.shields.io/badge/Go-%3E%3D%201.18-%23007d9c) [![GoDoc](https://godoc.org/github.com/samber/mo?status.svg)](https://pkg.go.dev/github.com/samber/mo) ![Build Status](https://github.com/samber/mo/actions/workflows/go.yml/badge.svg) [![Go report](https://goreportcard.com/badge/github.com/samber/mo)](https://goreportcard.com/report/github.com/samber/mo) -[![codecov](https://codecov.io/gh/samber/mo/branch/master/graph/badge.svg)](https://codecov.io/gh/samber/mo) +[![Coverage](https://img.shields.io/codecov/c/github/samber/do)](https://codecov.io/gh/samber/mo) +[![License](https://img.shields.io/github/license/samber/mo)](./LICENSE) 🦄 **`samber/mo` brings monads and popular FP abstractions to Go projects. `samber/mo` uses the recent Go 1.18+ Generics.** @@ -30,7 +32,7 @@ We currently support the following data types: - `Option[T]` (Maybe) - `Result[T]` - `Either[A, B]` -- `EitherX[T1, ..., TX]` (With X between 3 and 5) +- `EitherX[T1, ..., TX]` (With X between 3 and 5) - `Future[T]` - `IO[T]` - `IOEither[T]` @@ -98,6 +100,18 @@ option3 := option1.Match( More examples in [documentation](https://godoc.org/github.com/samber/mo). +### Tips for lazy developers + +I cannot recommend it, but in case you are too lazy for repeating `mo.` everywhere, you can import the entire library into the namespace. + +```go +import ( + . "github.com/samber/mo" +) +``` + +I take no responsibility on this junk. 😁 💩 + ## 🤠 Documentation and examples [GoDoc: https://godoc.org/github.com/samber/mo](https://godoc.org/github.com/samber/mo) @@ -182,6 +196,7 @@ Methods: - `.Right()` [doc](https://pkg.go.dev/github.com/samber/mo#Either.Right) - `.MustLeft()` [doc](https://pkg.go.dev/github.com/samber/mo#Either.MustLeft) - `.MustRight()` [doc](https://pkg.go.dev/github.com/samber/mo#Either.MustRight) +- `.Unpack()` [doc](https://pkg.go.dev/github.com/samber/mo#Either.Unpack) - `.LeftOrElse()` [doc](https://pkg.go.dev/github.com/samber/mo#Either.LeftOrElse) - `.RightOrElse()` [doc](https://pkg.go.dev/github.com/samber/mo#Either.RightOrElse) - `.LeftOrEmpty()` [doc](https://pkg.go.dev/github.com/samber/mo#Either.LeftOrEmpty) @@ -211,6 +226,7 @@ Methods: - `.IsArgX()` [doc](https://pkg.go.dev/github.com/samber/mo#Either5.IsArg1) - `.ArgX()` [doc](https://pkg.go.dev/github.com/samber/mo#Either5.Arg1) - `.MustArgX()` [doc](https://pkg.go.dev/github.com/samber/mo#Either5.MustArg1) +- `.Unpack()` [doc](https://pkg.go.dev/github.com/samber/mo#Either5.Unpack) - `.ArgXOrElse()` [doc](https://pkg.go.dev/github.com/samber/mo#Either5.Arg1OrElse) - `.ArgXOrEmpty()` [doc](https://pkg.go.dev/github.com/samber/mo#Either5.Arg1OrEmpty) - `.ForEach()` [doc](https://pkg.go.dev/github.com/samber/mo#Either5.ForEach) @@ -357,9 +373,9 @@ make test make watch-test ``` -## 👤 Authors +## 👤 Contributors -- Samuel Berthe +![Contributors](https://contrib.rocks/image?repo=samber/mo) ## 💫 Show your support diff --git a/vendor/github.com/samber/mo/docker-compose.yml b/vendor/github.com/samber/mo/docker-compose.yml deleted file mode 100644 index 51393361..00000000 --- a/vendor/github.com/samber/mo/docker-compose.yml +++ /dev/null @@ -1,9 +0,0 @@ -version: '3' - -services: - dev: - build: . - volumes: - - ./:/go/src/github.com/samber/mo - working_dir: /go/src/github.com/samber/mo - command: bash -c 'make tools ; make watch-test' diff --git a/vendor/github.com/samber/mo/either.go b/vendor/github.com/samber/mo/either.go index f3aadeb8..8fa69874 100644 --- a/vendor/github.com/samber/mo/either.go +++ b/vendor/github.com/samber/mo/either.go @@ -75,6 +75,11 @@ func (e Either[L, R]) MustRight() R { return e.right } +// Unpack returns all values +func (e Either[L, R]) Unpack() (L, R) { + return e.left, e.right +} + // LeftOrElse returns left value of a Either struct or fallback. func (e Either[L, R]) LeftOrElse(fallback L) L { if e.IsLeft() { diff --git a/vendor/github.com/samber/mo/either3.go b/vendor/github.com/samber/mo/either3.go index 6660a964..131b8bab 100644 --- a/vendor/github.com/samber/mo/either3.go +++ b/vendor/github.com/samber/mo/either3.go @@ -112,6 +112,11 @@ func (e Either3[T1, T2, T3]) MustArg3() T3 { return e.arg3 } +// Unpack returns all values +func (e Either3[T1, T2, T3]) Unpack() (T1, T2, T3) { + return e.arg1, e.arg2, e.arg3 +} + // Arg1OrElse returns the first argument of a Either3 struct or fallback. func (e Either3[T1, T2, T3]) Arg1OrElse(fallback T1) T1 { if e.IsArg1() { diff --git a/vendor/github.com/samber/mo/either4.go b/vendor/github.com/samber/mo/either4.go index fb4cf812..16e207a8 100644 --- a/vendor/github.com/samber/mo/either4.go +++ b/vendor/github.com/samber/mo/either4.go @@ -144,6 +144,11 @@ func (e Either4[T1, T2, T3, T4]) MustArg4() T4 { return e.arg4 } +// Unpack returns all values +func (e Either4[T1, T2, T3, T4]) Unpack() (T1, T2, T3, T4) { + return e.arg1, e.arg2, e.arg3, e.arg4 +} + // Arg1OrElse returns the first argument of a Either4 struct or fallback. func (e Either4[T1, T2, T3, T4]) Arg1OrElse(fallback T1) T1 { if e.IsArg1() { diff --git a/vendor/github.com/samber/mo/either5.go b/vendor/github.com/samber/mo/either5.go index 1eadc073..bc8e3c5c 100644 --- a/vendor/github.com/samber/mo/either5.go +++ b/vendor/github.com/samber/mo/either5.go @@ -176,6 +176,11 @@ func (e Either5[T1, T2, T3, T4, T5]) MustArg5() T5 { return e.arg5 } +// Unpack returns all values +func (e Either5[T1, T2, T3, T4, T5]) Unpack() (T1, T2, T3, T4, T5) { + return e.arg1, e.arg2, e.arg3, e.arg4, e.arg5 +} + // Arg1OrElse returns the first argument of a Either5 struct or fallback. func (e Either5[T1, T2, T3, T4, T5]) Arg1OrElse(fallback T1) T1 { if e.IsArg1() { diff --git a/vendor/github.com/samber/mo/future.go b/vendor/github.com/samber/mo/future.go index faf2345d..f8e14071 100644 --- a/vendor/github.com/samber/mo/future.go +++ b/vendor/github.com/samber/mo/future.go @@ -7,14 +7,12 @@ import ( // NewFuture instanciate a new future. func NewFuture[T any](cb func(resolve func(T), reject func(error))) *Future[T] { future := Future[T]{ - mu: sync.RWMutex{}, - next: nil, + cb: cb, cancelCb: func() {}, + done: make(chan struct{}), } - go func() { - cb(future.resolve, future.reject) - }() + future.active() return &future } @@ -22,28 +20,43 @@ func NewFuture[T any](cb func(resolve func(T), reject func(error))) *Future[T] { // Future represents a value which may or may not currently be available, but will be // available at some point, or an exception if that value could not be made available. type Future[T any] struct { - mu sync.RWMutex + mu sync.Mutex - next func(T, error) + cb func(func(T), func(error)) cancelCb func() + next *Future[T] + done chan struct{} + result Result[T] +} + +func (f *Future[T]) active() { + go f.cb(f.resolve, f.reject) +} + +func (f *Future[T]) activeSync() { + f.cb(f.resolve, f.reject) } func (f *Future[T]) resolve(value T) { - f.mu.RLock() - defer f.mu.RUnlock() + f.mu.Lock() + defer f.mu.Unlock() + f.result = Ok(value) if f.next != nil { - f.next(value, nil) + f.next.activeSync() } + close(f.done) } func (f *Future[T]) reject(err error) { - f.mu.RLock() - defer f.mu.RUnlock() + f.mu.Lock() + defer f.mu.Unlock() + f.result = Err[T](err) if f.next != nil { - f.next(empty[T](), err) + f.next.activeSync() } + close(f.done) } // Then is called when Future is resolved. It returns a new Future. @@ -51,30 +64,31 @@ func (f *Future[T]) Then(cb func(T) (T, error)) *Future[T] { f.mu.Lock() defer f.mu.Unlock() - future := &Future[T]{ - mu: sync.RWMutex{}, - next: nil, + f.next = &Future[T]{ + cb: func(resolve func(T), reject func(error)) { + if f.result.IsError() { + reject(f.result.Error()) + return + } + newValue, err := cb(f.result.MustGet()) + if err != nil { + reject(err) + return + } + resolve(newValue) + }, cancelCb: func() { f.Cancel() }, + done: make(chan struct{}), } - f.next = func(value T, err error) { - if err != nil { - future.reject(err) - return - } - - newValue, err := cb(value) - if err != nil { - future.reject(err) - return - } - - future.resolve(newValue) + select { + case <-f.done: + f.next.active() + default: } - - return future + return f.next } // Catch is called when Future is rejected. It returns a new Future. @@ -82,30 +96,31 @@ func (f *Future[T]) Catch(cb func(error) (T, error)) *Future[T] { f.mu.Lock() defer f.mu.Unlock() - future := &Future[T]{ - mu: sync.RWMutex{}, - next: nil, + f.next = &Future[T]{ + cb: func(resolve func(T), reject func(error)) { + if f.result.IsOk() { + resolve(f.result.MustGet()) + return + } + newValue, err := cb(f.result.Error()) + if err != nil { + reject(err) + return + } + resolve(newValue) + }, cancelCb: func() { f.Cancel() }, + done: make(chan struct{}), } - f.next = func(value T, err error) { - if err == nil { - future.resolve(value) - return - } - - newValue, err := cb(err) - if err != nil { - future.reject(err) - return - } - - future.resolve(newValue) + select { + case <-f.done: + f.next.active() + default: } - - return future + return f.next } // Finally is called when Future is processed either resolved or rejected. It returns a new Future. @@ -113,25 +128,27 @@ func (f *Future[T]) Finally(cb func(T, error) (T, error)) *Future[T] { f.mu.Lock() defer f.mu.Unlock() - future := &Future[T]{ - mu: sync.RWMutex{}, - next: nil, + f.next = &Future[T]{ + cb: func(resolve func(T), reject func(error)) { + newValue, err := cb(f.result.Get()) + if err != nil { + reject(err) + return + } + resolve(newValue) + }, cancelCb: func() { f.Cancel() }, + done: make(chan struct{}), } - f.next = func(value T, err error) { - newValue, err := cb(value, err) - if err != nil { - future.reject(err) - return - } - - future.resolve(newValue) + select { + case <-f.done: + f.next.active() + default: } - - return future + return f.next } // Cancel cancels the Future chain. @@ -147,23 +164,8 @@ func (f *Future[T]) Cancel() { // Collect awaits and return result of the Future. func (f *Future[T]) Collect() (T, error) { - done := make(chan struct{}) - - var a T - var b error - - f.mu.Lock() - f.next = func(value T, err error) { - a = value - b = err - - done <- struct{}{} - } - f.mu.Unlock() - - <-done - - return a, b + <-f.done + return f.result.Get() } // Result wraps Collect and returns a Result. diff --git a/vendor/github.com/spf13/afero/memmap.go b/vendor/github.com/spf13/afero/memmap.go index ea0798d8..d06975e7 100644 --- a/vendor/github.com/spf13/afero/memmap.go +++ b/vendor/github.com/spf13/afero/memmap.go @@ -142,6 +142,11 @@ func (m *MemMapFs) Mkdir(name string, perm os.FileMode) error { } m.mu.Lock() + // Dobule check that it doesn't exist. + if _, ok := m.getData()[name]; ok { + m.mu.Unlock() + return &os.PathError{Op: "mkdir", Path: name, Err: ErrFileExists} + } item := mem.CreateDir(name) mem.SetMode(item, os.ModeDir|perm) m.getData()[name] = item diff --git a/vendor/github.com/spf13/viper/README.md b/vendor/github.com/spf13/viper/README.md index 5701422c..63413a7d 100644 --- a/vendor/github.com/spf13/viper/README.md +++ b/vendor/github.com/spf13/viper/README.md @@ -11,7 +11,7 @@ [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/spf13/viper/CI?style=flat-square)](https://github.com/spf13/viper/actions?query=workflow%3ACI) [![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Go Report Card](https://goreportcard.com/badge/github.com/spf13/viper?style=flat-square)](https://goreportcard.com/report/github.com/spf13/viper) -![Go Version](https://img.shields.io/badge/go%20version-%3E=1.15-61CFDD.svg?style=flat-square) +![Go Version](https://img.shields.io/badge/go%20version-%3E=1.16-61CFDD.svg?style=flat-square) [![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/viper)](https://pkg.go.dev/mod/github.com/spf13/viper) **Go configuration with fangs!** diff --git a/vendor/github.com/spf13/viper/viper.go b/vendor/github.com/spf13/viper/viper.go index 5f76cc09..5c12529b 100644 --- a/vendor/github.com/spf13/viper/viper.go +++ b/vendor/github.com/spf13/viper/viper.go @@ -463,9 +463,8 @@ func (v *Viper) WatchConfig() { // we only care about the config file with the following cases: // 1 - if the config file was modified or created // 2 - if the real path to the config file changed (eg: k8s ConfigMap replacement) - const writeOrCreateMask = fsnotify.Write | fsnotify.Create if (filepath.Clean(event.Name) == configFile && - event.Op&writeOrCreateMask != 0) || + (event.Has(fsnotify.Write) || event.Has(fsnotify.Create))) || (currentConfigFile != "" && currentConfigFile != realConfigFile) { realConfigFile = currentConfigFile err := v.ReadInConfig() @@ -475,8 +474,7 @@ func (v *Viper) WatchConfig() { if v.onConfigChange != nil { v.onConfigChange(event) } - } else if filepath.Clean(event.Name) == configFile && - event.Op&fsnotify.Remove != 0 { + } else if filepath.Clean(event.Name) == configFile && event.Has(fsnotify.Remove) { eventsWG.Done() return } diff --git a/vendor/github.com/spf13/viper/watch.go b/vendor/github.com/spf13/viper/watch.go index b5523b8f..1ce84eaf 100644 --- a/vendor/github.com/spf13/viper/watch.go +++ b/vendor/github.com/spf13/viper/watch.go @@ -1,5 +1,5 @@ -//go:build !js -// +build !js +//go:build darwin || dragonfly || freebsd || openbsd || linux || netbsd || solaris || windows +// +build darwin dragonfly freebsd openbsd linux netbsd solaris windows package viper diff --git a/vendor/github.com/spf13/viper/watch_wasm.go b/vendor/github.com/spf13/viper/watch_unsupported.go similarity index 52% rename from vendor/github.com/spf13/viper/watch_wasm.go rename to vendor/github.com/spf13/viper/watch_unsupported.go index 8e47e6a9..7e271537 100644 --- a/vendor/github.com/spf13/viper/watch_wasm.go +++ b/vendor/github.com/spf13/viper/watch_unsupported.go @@ -1,13 +1,19 @@ -// +build js,wasm +//go:build appengine || (!darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows) +// +build appengine !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows package viper import ( - "errors" + "fmt" + "runtime" "github.com/fsnotify/fsnotify" ) +func newWatcher() (*watcher, error) { + return &watcher{}, fmt.Errorf("fsnotify not supported on %s", runtime.GOOS) +} + type watcher struct { Events chan fsnotify.Event Errors chan error @@ -24,7 +30,3 @@ func (*watcher) Add(name string) error { func (*watcher) Remove(name string) error { return nil } - -func newWatcher() (*watcher, error) { - return &watcher{}, errors.New("fsnotify is not supported on WASM") -} diff --git a/vendor/github.com/ysmood/gson/read.go b/vendor/github.com/ysmood/gson/read.go index f1e6ae2c..8843d95e 100644 --- a/vendor/github.com/ysmood/gson/read.go +++ b/vendor/github.com/ysmood/gson/read.go @@ -1,3 +1,4 @@ +// Package gson A tiny JSON lib to read and alter a JSON value. package gson import ( @@ -22,6 +23,24 @@ func (j JSON) MarshalJSON() ([]byte, error) { return json.Marshal(j.Val()) } +// Unmarshal is the same as [json.Unmarshal] for the underlying raw value. +// It should be called before other operations. +func (j JSON) Unmarshal(v interface{}) error { + if j.value == nil { + return fmt.Errorf("gson: no value to unmarshal") + } + + j.lock.Lock() + defer j.lock.Unlock() + + b, ok := (*j.value).([]byte) + if !ok { + return fmt.Errorf("gson: value has been parsed") + } + + return json.Unmarshal(b, v) +} + // JSON string func (j JSON) JSON(prefix, indent string) string { buf := bytes.NewBuffer(nil) @@ -33,7 +52,7 @@ func (j JSON) JSON(prefix, indent string) string { return s[:len(s)-1] } -// Raw underlaying value +// Raw underlying value func (j JSON) Raw() interface{} { if j.value == nil { return nil @@ -41,7 +60,7 @@ func (j JSON) Raw() interface{} { return *j.value } -// String implements fmt.Stringer interface +// String implements [fmt.Stringer] interface func (j JSON) String() string { return fmt.Sprintf("%v", j.Val()) } diff --git a/vendor/github.com/ysmood/gson/write.go b/vendor/github.com/ysmood/gson/write.go index 8c9dc629..9237e729 100644 --- a/vendor/github.com/ysmood/gson/write.go +++ b/vendor/github.com/ysmood/gson/write.go @@ -7,7 +7,7 @@ import ( "sync" ) -// New JSON from []byte, io.Reader, or raw value. +// New JSON from []byte, [io.Reader], or raw value. func New(v interface{}) JSON { return JSON{&sync.Mutex{}, &v} } @@ -23,7 +23,7 @@ func (j *JSON) UnmarshalJSON(b []byte) error { return nil } -// Val of the underlaying json value. +// Val of the underlying json value. // The first time it's called, it will try to parse the underlying data. func (j JSON) Val() interface{} { if j.value == nil { diff --git a/vendor/github.com/yuin/gopher-lua/_vm.go b/vendor/github.com/yuin/gopher-lua/_vm.go index 049107e1..687fe797 100644 --- a/vendor/github.com/yuin/gopher-lua/_vm.go +++ b/vendor/github.com/yuin/gopher-lua/_vm.go @@ -840,7 +840,7 @@ func luaModulo(lhs, rhs LNumber) LNumber { flhs := float64(lhs) frhs := float64(rhs) v := math.Mod(flhs, frhs) - if flhs < 0 || frhs < 0 && !(flhs < 0 && frhs < 0) { + if frhs > 0 && v < 0 || frhs < 0 && v > 0 { v += frhs } return LNumber(v) diff --git a/vendor/github.com/yuin/gopher-lua/vm.go b/vendor/github.com/yuin/gopher-lua/vm.go index aaa04dc9..470855f2 100644 --- a/vendor/github.com/yuin/gopher-lua/vm.go +++ b/vendor/github.com/yuin/gopher-lua/vm.go @@ -1533,7 +1533,7 @@ func luaModulo(lhs, rhs LNumber) LNumber { flhs := float64(lhs) frhs := float64(rhs) v := math.Mod(flhs, frhs) - if flhs < 0 || frhs < 0 && !(flhs < 0 && frhs < 0) { + if frhs > 0 && v < 0 || frhs < 0 && v > 0 { v += frhs } return LNumber(v) diff --git a/vendor/golang.org/x/exp/slices/slices.go b/vendor/golang.org/x/exp/slices/slices.go index 0c756c46..cff0cd49 100644 --- a/vendor/golang.org/x/exp/slices/slices.go +++ b/vendor/golang.org/x/exp/slices/slices.go @@ -128,6 +128,12 @@ func Contains[E comparable](s []E, v E) bool { return Index(s, v) >= 0 } +// ContainsFunc reports whether at least one +// element e of s satisfies f(e). +func ContainsFunc[E any](s []E, f func(E) bool) bool { + return IndexFunc(s, f) >= 0 +} + // Insert inserts the values v... into s at index i, // returning the modified slice. // In the returned slice r, r[i] == v[0]. @@ -165,6 +171,7 @@ func Delete[S ~[]E, E any](s S, i, j int) S { // Replace replaces the elements s[i:j] by the given v, and returns the // modified slice. Replace panics if s[i:j] is not a valid slice of s. func Replace[S ~[]E, E any](s S, i, j int, v ...E) S { + _ = s[i:j] // verify that i:j is a valid subslice tot := len(s[:i]) + len(v) + len(s[j:]) if tot <= cap(s) { s2 := s[:tot] @@ -192,8 +199,11 @@ func Clone[S ~[]E, E any](s S) S { // Compact replaces consecutive runs of equal elements with a single copy. // This is like the uniq command found on Unix. // Compact modifies the contents of the slice s; it does not create a new slice. +// When Compact discards m elements in total, it might not modify the elements +// s[len(s)-m:len(s)]. If those elements contain pointers you might consider +// zeroing those elements so that objects they reference can be garbage collected. func Compact[S ~[]E, E comparable](s S) S { - if len(s) == 0 { + if len(s) < 2 { return s } i := 1 @@ -210,7 +220,7 @@ func Compact[S ~[]E, E comparable](s S) S { // CompactFunc is like Compact but uses a comparison function. func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S { - if len(s) == 0 { + if len(s) < 2 { return s } i := 1 diff --git a/vendor/golang.org/x/exp/slices/sort.go b/vendor/golang.org/x/exp/slices/sort.go index c22e74bd..35a5d8f0 100644 --- a/vendor/golang.org/x/exp/slices/sort.go +++ b/vendor/golang.org/x/exp/slices/sort.go @@ -30,7 +30,7 @@ func SortFunc[E any](x []E, less func(a, b E) bool) { pdqsortLessFunc(x, 0, n, bits.Len(uint(n)), less) } -// SortStable sorts the slice x while keeping the original order of equal +// SortStableFunc sorts the slice x while keeping the original order of equal // elements, using less to compare elements. func SortStableFunc[E any](x []E, less func(a, b E) bool) { stableLessFunc(x, len(x), less) diff --git a/vendor/golang.org/x/net/html/token.go b/vendor/golang.org/x/net/html/token.go index be3c7541..ae24a6fd 100644 --- a/vendor/golang.org/x/net/html/token.go +++ b/vendor/golang.org/x/net/html/token.go @@ -605,7 +605,10 @@ func (z *Tokenizer) readComment() { z.data.end = z.data.start } }() - for dashCount := 2; ; { + + var dashCount int + beginning := true + for { c := z.readByte() if z.err != nil { // Ignore up to two dashes at EOF. @@ -620,7 +623,7 @@ func (z *Tokenizer) readComment() { dashCount++ continue case '>': - if dashCount >= 2 { + if dashCount >= 2 || beginning { z.data.end = z.raw.end - len("-->") return } @@ -638,6 +641,7 @@ func (z *Tokenizer) readComment() { } } dashCount = 0 + beginning = false } } diff --git a/vendor/golang.org/x/sys/unix/sockcmsg_unix.go b/vendor/golang.org/x/sys/unix/sockcmsg_unix.go index 453a942c..3865943f 100644 --- a/vendor/golang.org/x/sys/unix/sockcmsg_unix.go +++ b/vendor/golang.org/x/sys/unix/sockcmsg_unix.go @@ -52,6 +52,20 @@ func ParseSocketControlMessage(b []byte) ([]SocketControlMessage, error) { return msgs, nil } +// ParseOneSocketControlMessage parses a single socket control message from b, returning the message header, +// message data (a slice of b), and the remainder of b after that single message. +// When there are no remaining messages, len(remainder) == 0. +func ParseOneSocketControlMessage(b []byte) (hdr Cmsghdr, data []byte, remainder []byte, err error) { + h, dbuf, err := socketControlMessageHeaderAndData(b) + if err != nil { + return Cmsghdr{}, nil, nil, err + } + if i := cmsgAlignOf(int(h.Len)); i < len(b) { + remainder = b[i:] + } + return *h, dbuf, remainder, nil +} + func socketControlMessageHeaderAndData(b []byte) (*Cmsghdr, []byte, error) { h := (*Cmsghdr)(unsafe.Pointer(&b[0])) if h.Len < SizeofCmsghdr || uint64(h.Len) > uint64(len(b)) { diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index e044d5b5..c5a98440 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -1554,6 +1554,7 @@ func sendmsgN(fd int, iov []Iovec, oob []byte, ptr unsafe.Pointer, salen _Sockle var iova [1]Iovec iova[0].Base = &dummy iova[0].SetLen(1) + iov = iova[:] } } msg.Control = &oob[0] diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go index 7a6ba43a..a49853e9 100644 --- a/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -367,6 +367,7 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys IsWindowUnicode(hwnd HWND) (isUnicode bool) = user32.IsWindowUnicode //sys IsWindowVisible(hwnd HWND) (isVisible bool) = user32.IsWindowVisible //sys GetGUIThreadInfo(thread uint32, info *GUIThreadInfo) (err error) = user32.GetGUIThreadInfo +//sys GetLargePageMinimum() (size uintptr) // Volume Management Functions //sys DefineDosDevice(flags uint32, deviceName *uint16, targetPath *uint16) (err error) = DefineDosDeviceW diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go index 96ba8559..ac60052e 100644 --- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -252,6 +252,7 @@ var ( procGetFileType = modkernel32.NewProc("GetFileType") procGetFinalPathNameByHandleW = modkernel32.NewProc("GetFinalPathNameByHandleW") procGetFullPathNameW = modkernel32.NewProc("GetFullPathNameW") + procGetLargePageMinimum = modkernel32.NewProc("GetLargePageMinimum") procGetLastError = modkernel32.NewProc("GetLastError") procGetLogicalDriveStringsW = modkernel32.NewProc("GetLogicalDriveStringsW") procGetLogicalDrives = modkernel32.NewProc("GetLogicalDrives") @@ -2180,6 +2181,12 @@ func GetFullPathName(path *uint16, buflen uint32, buf *uint16, fname **uint16) ( return } +func GetLargePageMinimum() (size uintptr) { + r0, _, _ := syscall.Syscall(procGetLargePageMinimum.Addr(), 0, 0, 0, 0) + size = uintptr(r0) + return +} + func GetLastError() (lasterr error) { r0, _, _ := syscall.Syscall(procGetLastError.Addr(), 0, 0, 0, 0) if r0 != 0 { diff --git a/vendor/golang.org/x/term/terminal.go b/vendor/golang.org/x/term/terminal.go index 4b48a589..f636667f 100644 --- a/vendor/golang.org/x/term/terminal.go +++ b/vendor/golang.org/x/term/terminal.go @@ -233,7 +233,6 @@ func (t *Terminal) queue(data []rune) { t.outBuf = append(t.outBuf, []byte(string(data))...) } -var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'} var space = []rune{' '} func isPrintable(key rune) bool { diff --git a/vendor/modules.txt b/vendor/modules.txt index b9ebfd6d..df9c26f3 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -15,7 +15,7 @@ github.com/andybalholm/cascadia # github.com/antchfx/htmlquery v1.2.5 ## explicit; go 1.14 github.com/antchfx/htmlquery -# github.com/antchfx/xmlquery v1.3.12 +# github.com/antchfx/xmlquery v1.3.13 ## explicit; go 1.14 github.com/antchfx/xmlquery # github.com/antchfx/xpath v1.2.1 @@ -39,8 +39,8 @@ github.com/charmbracelet/bubbles/paginator github.com/charmbracelet/bubbles/progress github.com/charmbracelet/bubbles/spinner github.com/charmbracelet/bubbles/textinput -# github.com/charmbracelet/bubbletea v0.22.1 -## explicit; go 1.13 +# github.com/charmbracelet/bubbletea v0.23.1 +## explicit; go 1.16 github.com/charmbracelet/bubbletea # github.com/charmbracelet/harmonica v0.2.0 ## explicit; go 1.16 @@ -63,7 +63,7 @@ github.com/fatih/color # github.com/fsnotify/fsnotify v1.6.0 ## explicit; go 1.16 github.com/fsnotify/fsnotify -# github.com/go-rod/rod v0.112.0 +# github.com/go-rod/rod v0.112.2 ## explicit; go 1.16 github.com/go-rod/rod github.com/go-rod/rod/lib/assets @@ -121,10 +121,10 @@ github.com/hhrutter/tiff # github.com/iancoleman/orderedmap v0.2.0 ## explicit github.com/iancoleman/orderedmap -# github.com/inconshreveable/mousetrap v1.0.1 +# github.com/inconshreveable/mousetrap v1.1.0 ## explicit; go 1.18 github.com/inconshreveable/mousetrap -# github.com/invopop/jsonschema v0.6.0 +# github.com/invopop/jsonschema v0.7.0 ## explicit; go 1.16 github.com/invopop/jsonschema # github.com/ivanpirog/coloredcobra v1.0.1 @@ -148,8 +148,8 @@ github.com/lithammer/fuzzysearch/fuzzy # github.com/lucasb-eyer/go-colorful v1.2.0 ## explicit; go 1.12 github.com/lucasb-eyer/go-colorful -# github.com/magiconair/properties v1.8.6 -## explicit; go 1.13 +# github.com/magiconair/properties v1.8.7 +## explicit; go 1.19 github.com/magiconair/properties # github.com/mattn/go-colorable v0.1.13 ## explicit; go 1.15 @@ -206,7 +206,7 @@ github.com/mitchellh/mapstructure # github.com/montanaflynn/stats v0.6.6 ## explicit; go 1.13 github.com/montanaflynn/stats -# github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 +# github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a ## explicit; go 1.17 github.com/muesli/ansi github.com/muesli/ansi/compressor @@ -237,16 +237,17 @@ github.com/pdfcpu/pdfcpu/pkg/types # github.com/pelletier/go-toml v1.9.5 ## explicit; go 1.12 github.com/pelletier/go-toml -# github.com/pelletier/go-toml/v2 v2.0.5 +# github.com/pelletier/go-toml/v2 v2.0.6 ## explicit; go 1.16 github.com/pelletier/go-toml/v2 -github.com/pelletier/go-toml/v2/internal/ast +github.com/pelletier/go-toml/v2/internal/characters github.com/pelletier/go-toml/v2/internal/danger github.com/pelletier/go-toml/v2/internal/tracker +github.com/pelletier/go-toml/v2/unstable # github.com/pkg/errors v0.9.1 ## explicit github.com/pkg/errors -# github.com/rivo/uniseg v0.4.2 +# github.com/rivo/uniseg v0.4.3 ## explicit; go 1.18 github.com/rivo/uniseg # github.com/sahilm/fuzzy v0.1.0 @@ -255,10 +256,10 @@ github.com/sahilm/fuzzy # github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca ## explicit github.com/saintfish/chardet -# github.com/samber/lo v1.33.0 +# github.com/samber/lo v1.37.0 ## explicit; go 1.18 github.com/samber/lo -# github.com/samber/mo v1.5.1 +# github.com/samber/mo v1.7.0 ## explicit; go 1.18 github.com/samber/mo # github.com/sirupsen/logrus v1.9.0 @@ -275,7 +276,7 @@ github.com/smartystreets/assertions/internal/oglematchers github.com/smartystreets/goconvey/convey github.com/smartystreets/goconvey/convey/gotest github.com/smartystreets/goconvey/convey/reporting -# github.com/spf13/afero v1.9.2 +# github.com/spf13/afero v1.9.3 ## explicit; go 1.16 github.com/spf13/afero github.com/spf13/afero/internal/common @@ -292,7 +293,7 @@ github.com/spf13/jwalterweatherman # github.com/spf13/pflag v1.0.5 ## explicit; go 1.12 github.com/spf13/pflag -# github.com/spf13/viper v1.13.0 +# github.com/spf13/viper v1.14.0 ## explicit; go 1.17 github.com/spf13/viper github.com/spf13/viper/internal/encoding @@ -312,7 +313,7 @@ github.com/temoto/robotstxt # github.com/ysmood/goob v0.4.0 ## explicit; go 1.15 github.com/ysmood/goob -# github.com/ysmood/gson v0.7.2 +# github.com/ysmood/gson v0.7.3 ## explicit; go 1.15 github.com/ysmood/gson # github.com/ysmood/leakless v0.8.0 @@ -323,39 +324,39 @@ github.com/ysmood/leakless/pkg/utils # github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7 ## explicit github.com/yuin/gluamapper -# github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 +# github.com/yuin/gopher-lua v0.0.0-20221210110428-332342483e3f ## explicit; go 1.17 github.com/yuin/gopher-lua github.com/yuin/gopher-lua/ast github.com/yuin/gopher-lua/parse github.com/yuin/gopher-lua/pm -# golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 +# golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15 ## explicit; go 1.18 golang.org/x/exp/constraints golang.org/x/exp/slices -# golang.org/x/image v0.1.0 +# golang.org/x/image v0.2.0 ## explicit; go 1.12 golang.org/x/image/ccitt golang.org/x/image/riff golang.org/x/image/vp8 golang.org/x/image/vp8l golang.org/x/image/webp -# golang.org/x/net v0.1.0 +# golang.org/x/net v0.4.0 ## explicit; go 1.17 golang.org/x/net/context golang.org/x/net/html golang.org/x/net/html/atom golang.org/x/net/html/charset -# golang.org/x/sys v0.1.0 +# golang.org/x/sys v0.3.0 ## explicit; go 1.17 golang.org/x/sys/internal/unsafeheader golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/term v0.1.0 +# golang.org/x/term v0.3.0 ## explicit; go 1.17 golang.org/x/term -# golang.org/x/text v0.4.0 +# golang.org/x/text v0.5.0 ## explicit; go 1.17 golang.org/x/text/encoding golang.org/x/text/encoding/charmap diff --git a/version/version.go b/version/version.go index 4983c304..b33551fd 100644 --- a/version/version.go +++ b/version/version.go @@ -14,7 +14,7 @@ import ( var versionCacher = gache.New[string](&gache.Options{ Path: filepath.Join(where.Cache(), "version.json"), - Lifetime: time.Hour * 24, + Lifetime: time.Hour * 24 * 2, FileSystem: &filesystem.GacheFs{}, }) diff --git a/where/where.go b/where/where.go index 75da21fc..def252a4 100644 --- a/where/where.go +++ b/where/where.go @@ -3,6 +3,7 @@ package where import ( "github.com/metafates/mangal/constant" "github.com/metafates/mangal/filesystem" + "github.com/metafates/mangal/key" "github.com/samber/lo" "github.com/spf13/viper" "os" @@ -63,7 +64,7 @@ func History() string { // Downloads path // Will create the directory if it doesn't exist func Downloads() string { - path, err := filepath.Abs(viper.GetString(constant.DownloaderPath)) + path, err := filepath.Abs(viper.GetString(key.DownloaderPath)) if err != nil { path, err = os.Getwd()