From 079bb3c37ac8b86c3c48bdb12f89359ed4bb305e Mon Sep 17 00:00:00 2001 From: Ewen Le Bihan Date: Sat, 12 Oct 2024 13:57:38 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Use=20github.com/ewen-lbh/?= =?UTF-8?q?label-logger-go=20for=20logging=20&=20progress=20bar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- add.go | 21 +++--- build.go | 71 ++++++++++--------- cmd/add.go | 3 +- cmd/exporters.go | 3 +- cmd/ui.go | 4 +- cmd/utils.go | 29 ++------ colors.go | 13 ++-- configuration.go | 7 +- description.go | 8 +-- exporter_custom.go | 13 ++-- exporters.go | 5 +- go.mod | 7 +- go.sum | 2 + layout.go | 15 ++-- media.go | 61 ++++++++-------- metadata.go | 15 ++-- progress.go | 105 ++++----------------------- replication.go | 3 +- thumbnails.go | 3 +- ui.go | 172 +++++---------------------------------------- utils.go | 18 +---- validation.go | 3 +- 22 files changed, 175 insertions(+), 406 deletions(-) diff --git a/add.go b/add.go index 419e88a..229bd7c 100644 --- a/add.go +++ b/add.go @@ -11,6 +11,7 @@ import ( "github.com/anaskhan96/soup" "github.com/charmbracelet/huh" + ll "github.com/ewen-lbh/label-logger-go" "golang.org/x/text/cases" "golang.org/x/text/language" @@ -176,7 +177,7 @@ func (ctx *RunContext) CreateDescriptionFile(workId string, metadataItems []stri if fileExists(readmePath) { readmeTitle, readmeBody, err := fromReadme(readmePath) if err != nil { - DisplayWarning("couldn't extract info from README.md", err) + ll.WarnDisplay("couldn't extract info from README.md", err) } else { if readmeTitle != "" { defaultProjectTitle = readmeTitle @@ -188,10 +189,10 @@ func (ctx *RunContext) CreateDescriptionFile(workId string, metadataItems []stri detectedStartDate, err := DetectStartDate(ctx.PathToWorkFolder(workId)) defaultStartedAt := "" if err != nil { - DisplayWarning("while detecting start date of %s", err, workId) + ll.WarnDisplay("while detecting start date of %s", err, workId) } else { defaultStartedAt = detectedStartDate.Format("2006-01-02") - LogCustom("Detected", "cyan", "start date to be [bold][blue]%s[reset]", defaultStartedAt) + ll.Log("Detected", "cyan", "start date to be [bold][blue]%s[reset]", defaultStartedAt) } startedAtPlaceholder := "YYYY-MM-DD" @@ -207,7 +208,7 @@ func (ctx *RunContext) CreateDescriptionFile(workId string, metadataItems []stri autodetectedTechs, err := ctx.DetectTechnologies(workId) if err != nil { - LogWarning(formatErrors(fmt.Errorf("while autodetecting technologies for %s: %w", workId, err))) + ll.Warn(ll.FormatErrors(fmt.Errorf("while autodetecting technologies for %s: %w", workId, err))) } else { displayTags := make([]string, 0, len(autodetectedTechs)) for _, tech := range autodetectedTechs { @@ -215,19 +216,19 @@ func (ctx *RunContext) CreateDescriptionFile(workId string, metadataItems []stri displayTags = append(displayTags, tech.String()) } if len(metadata.MadeWith) > 0 { - LogCustom("Detected", "cyan", "technologies to be %s", formatList(displayTags, "[bold][blue]%s[reset]", ", ")) + ll.Log("Detected", "cyan", "technologies to be %s", ll.List(displayTags, "[bold][blue]%s[reset]", ", ")) } } autodetectedTags, err := ctx.DetectTags(workId, autodetectedTechs) if err != nil { - DisplayWarning("while autodetecting tags for %s", err, workId) + ll.WarnDisplay("while autodetecting tags for %s", err, workId) } else { for _, tag := range autodetectedTags { metadata.Tags = append(metadata.Tags, tag.String()) } if len(metadata.Tags) > 0 { - LogCustom("Detected", "cyan", "tags to be %s", formatList(metadata.Tags, "[bold][blue]%s[reset]", ", ")) + ll.Log("Detected", "cyan", "tags to be %s", ll.List(metadata.Tags, "[bold][blue]%s[reset]", ", ")) } } @@ -248,7 +249,7 @@ func (ctx *RunContext) CreateDescriptionFile(workId string, metadataItems []stri Value(&metadata.MadeWith). Options(allTechsOptions...). Validate(func(s []string) error { - LogDebug("Selected %v", s) + ll.Debug("Selected %v", s) return nil }). Height(2+6), @@ -276,7 +277,7 @@ func (ctx *RunContext) CreateDescriptionFile(workId string, metadataItems []stri defaultFinishedAt := time.Now().Format("2006-01-02") if finishedAtFromGit, err := LastGitCommitDate(ctx.PathToWorkFolder(workId)); err == nil { defaultFinishedAt = finishedAtFromGit.Format("2006-01-02") - LogCustom("Detected", "cyan", "finish date to be [bold][blue]%s[reset]", defaultFinishedAt) + ll.Log("Detected", "cyan", "finish date to be [bold][blue]%s[reset]", defaultFinishedAt) } err = huh.NewForm( @@ -330,6 +331,6 @@ func (ctx *RunContext) CreateDescriptionFile(workId string, metadataItems []stri os.MkdirAll(filepath.Dir(outputPath), 0o755) os.WriteFile(outputPath, []byte(output), 0o644) - LogCustom("Created", "green", "description.md file at [bold]%s[reset]", outputPath) + ll.Log("Created", "green", "description.md file at [bold]%s[reset]", outputPath) return outputPath, nil } diff --git a/build.go b/build.go index ed08f1d..56e90cb 100644 --- a/build.go +++ b/build.go @@ -19,6 +19,7 @@ import ( "path" + ll "github.com/ewen-lbh/label-logger-go" jsoniter "github.com/json-iterator/go" ) @@ -227,7 +228,7 @@ func AcquireBuildLock(outputFilename string) error { func ReleaseBuildLock(outputFilename string) error { err := os.Remove(BuildLockFilepath(outputFilename)) if err != nil { - DisplayError("could not release build lockfile %s", err, BuildLockFilepath(outputFilename)) + ll.ErrorDisplay("could not release build lockfile %s", err, BuildLockFilepath(outputFilename)) } return err } @@ -248,19 +249,19 @@ func PrepareBuild(databaseDirectory string, outputFilename string, flags Flags, thumbnailSizesCount := len(ctx.Config.MakeThumbnails.Sizes) if thumbnailSizesCount/2 > flags.WorkersCount { - LogDebug("ThumbnailSizesCount/2 (%d) > flags.WorkersCount (%d). Using 2 thumbnailers per work.", thumbnailSizesCount/2, flags.WorkersCount) + ll.Debug("ThumbnailSizesCount/2 (%d) > flags.WorkersCount (%d). Using 2 thumbnailers per work.", thumbnailSizesCount/2, flags.WorkersCount) ctx.thumbnailersPerWork = 2 } else { - LogDebug("Configuration asks for %d thumbnail sizes. setting thumbnail workers count per work to half of that.", thumbnailSizesCount) + ll.Debug("Configuration asks for %d thumbnail sizes. setting thumbnail workers count per work to half of that.", thumbnailSizesCount) ctx.thumbnailersPerWork = thumbnailSizesCount / 2 } - LogDebug("Using %d thumbnailers threads per work", ctx.thumbnailersPerWork) + ll.Debug("Using %d thumbnailers threads per work", ctx.thumbnailersPerWork) if ctx.ProgressInfoFile != "" { - LogDebug("Removing progress info file %s", ctx.ProgressInfoFile) + ll.Debug("Removing progress info file %s", ctx.ProgressInfoFile) if err := os.Remove(ctx.ProgressInfoFile); err != nil { - LogDebug("Could not remove progress info file %s: %s", ctx.ProgressInfoFile, err.Error()) + ll.Debug("Could not remove progress info file %s: %s", ctx.ProgressInfoFile, err.Error()) } } @@ -278,24 +279,24 @@ func PrepareBuild(databaseDirectory string, outputFilename string, flags Flags, ctx.Exporters = append(ctx.Exporters, exporter) } - LogDebug("Running with configuration %#v", &config) + ll.Debug("Running with configuration %#v", &config) previousBuiltDatabaseRaw, err := os.ReadFile(outputFilename) if err != nil { if !os.IsNotExist(err) { - DisplayError("No previously built database file %s to use", err, outputFilename) + ll.ErrorDisplay("No previously built database file %s to use", err, outputFilename) } } else { previousDb := Database{} err = json.Unmarshal(previousBuiltDatabaseRaw, &previousDb) if err != nil { - DisplayError("Couldn't use previous built database file %s", err, outputFilename) + ll.ErrorDisplay("Couldn't use previous built database file %s", err, outputFilename) } ctx.previousBuiltDatabase = PreviouslyBuiltDatabase{Database: previousDb} } if ctx.Config.IsDefault { - LogInfo("No configuration file found. The default configuration was used.") + ll.Info("No configuration file found. The default configuration was used.") } err = os.MkdirAll(config.Media.At, 0o755) @@ -312,7 +313,7 @@ func PrepareBuild(databaseDirectory string, outputFilename string, flags Flags, return &ctx, err } - LogCustom("Using", "magenta", "exporter [bold]%s[reset]\n[dim]%s", exporter.Name(), exporter.Description()) + ll.Log("Using", "magenta", "exporter [bold]%s[reset]\n[dim]%s", exporter.Name(), exporter.Description()) err = exporter.Before(&ctx, options) if err != nil { return &ctx, fmt.Errorf("while running exporter %s before hook: %w", exporter.Name(), err) @@ -348,7 +349,7 @@ func directoriesLeftToBuild(all []string, built []string) []string { func (ctx *RunContext) RunExporters(work *Work) error { for _, exporter := range ctx.Exporters { if debugging { - LogCustom("Exporting", "magenta", "%s to %s", work.ID, exporter.Name()) + ll.Log("Exporting", "magenta", "%s to %s", work.ID, exporter.Name()) } options := ctx.Config.Exporters[exporter.Name()] err := exporter.Export(ctx, options, work) @@ -371,7 +372,7 @@ func (ctx *RunContext) BuildSome(include string, databaseDirectory string, outpu // Initialize stuff works := ctx.PreviouslyBuiltDatabase() - // LogDebug("initialized works@%p from previous@%p", works, ctx.previousBuiltDatabase.Database) + // ll.Debug("initialized works@%p from previous@%p", works, ctx.previousBuiltDatabase.Database) workDirectories, err := ctx.ComputeProgressTotal() if err != nil { return Database{}, fmt.Errorf("while computing total number of works to build: %w", err) @@ -391,20 +392,20 @@ func (ctx *RunContext) BuildSome(include string, databaseDirectory string, outpu ctx.StartProgressBar(len(workDirectories)) if flags.WorkersCount < ctx.thumbnailersPerWork { - LogWarning("Number of workers (%d) is less than the number of thumbnailers per work (%d). Setting number of workers to %d", ctx.Flags.WorkersCount, ctx.thumbnailersPerWork, ctx.thumbnailersPerWork) + ll.Warn("Number of workers (%d) is less than the number of thumbnailers per work (%d). Setting number of workers to %d", ctx.Flags.WorkersCount, ctx.thumbnailersPerWork, ctx.thumbnailersPerWork) flags.WorkersCount = ctx.thumbnailersPerWork } // Build works in parallel for i := 0; i < flags.WorkersCount/ctx.thumbnailersPerWork; i++ { i := i - LogDebug("worker #%d: starting", i) + ll.Debug("worker #%d: starting", i) go func() { - LogDebug("worker #%d: starting", i) + ll.Debug("worker #%d: starting", i) for { dirEntry := <-workDirectoriesChannel workID := dirEntry.Name() - LogDebug("worker #%d: starting with work %s", i, workID) + ll.Debug("worker #%d: starting with work %s", i, workID) _, presentBefore := ctx.PreviouslyBuiltWork(workID) var included bool if include == "*" { @@ -430,7 +431,7 @@ func (ctx *RunContext) BuildSome(include string, databaseDirectory string, outpu ctx.Status(workID, PhaseBuilding) newWork, usedCache, err := ctx.Build(string(descriptionRaw), outputFilename, workID) if err != nil { - DisplayError("while building %s", err, workID) + ll.ErrorDisplay("while building %s", err, workID) builtChannel <- builtItem{err: fmt.Errorf("while building %s (%s): %w", workID, ctx.DescriptionFilename(databaseDirectory, workID), err)} continue } @@ -443,55 +444,55 @@ func (ctx *RunContext) BuildSome(include string, databaseDirectory string, outpu } // Update in database - LogDebug("worker #%d: sending freshly built work %s", i, workID) + ll.Debug("worker #%d: sending freshly built work %s", i, workID) builtChannel <- builtItem{work: newWork, workID: workID} continue // } } else if presentBefore { // Nothing to do, old work will be kept as-is. - LogDebug("worker #%d: nothing to do for work %s", i, workID) + ll.Debug("worker #%d: nothing to do for work %s", i, workID) ctx.Status(workID, PhaseUnchanged) } else { - LogDebug("worker #%d: Build skipped: not included by %s, not present in previous database file.", i, include) + ll.Debug("worker #%d: Build skipped: not included by %s, not present in previous database file.", i, include) } - LogDebug("worker #%d: reusing old work %s", i, workID) + ll.Debug("worker #%d: reusing old work %s", i, workID) builtChannel <- builtItem{reuseOld: true, workID: workID} } }() } - LogDebug("main: filling work directories") + ll.Debug("main: filling work directories") for _, workDirectory := range workDirectories { workDirectoriesChannel <- workDirectory } // Collect all newly-built works - LogDebug("main: collecting results") + ll.Debug("main: collecting results") for len(builtDirectories) < len(workDirectories) { result := <-builtChannel - LogDebug("main: got result %v", result) + ll.Debug("main: got result %v", result) if result.err != nil { - LogDebug("main: got error, returning early") + ll.Debug("main: got error, returning early") return Database{}, result.err } if !result.reuseOld { - LogDebug("main: updating work %s", result.workID) + ll.Debug("main: updating work %s", result.workID) ctx.previousBuiltDatabase.mu.Lock() works[result.workID] = result.work ctx.previousBuiltDatabase.mu.Unlock() } ctx.WriteDatabase(works, flags, outputFilename, true) builtDirectories = append(builtDirectories, result.workID) - LogDebug("main: built dirs: %d out of %d", len(builtDirectories), len(workDirectories)) - LogDebug("main: left to build: %v", directoriesLeftToBuild(workDirectoriesNames, builtDirectories)) + ll.Debug("main: built dirs: %d out of %d", len(builtDirectories), len(workDirectories)) + ll.Debug("main: left to build: %v", directoriesLeftToBuild(workDirectoriesNames, builtDirectories)) } for _, exporter := range ctx.Exporters { options := ctx.Config.Exporters[exporter.Name()] - LogDebug("Running exporter %s's after hook with options %#v", exporter.Name(), options) + ll.Debug("Running exporter %s's after hook with options %#v", exporter.Name(), options) err := exporter.After(ctx, options, &works) if err != nil { - DisplayError("while running exporter %s's after hook: %s", err, exporter.Name()) + ll.ErrorDisplay("while running exporter %s's after hook: %s", err, exporter.Name()) } } @@ -500,7 +501,7 @@ func (ctx *RunContext) BuildSome(include string, databaseDirectory string, outpu } func (ctx *RunContext) WriteDatabase(works Database, flags Flags, outputFilename string, partial bool) { - LogDebug("Writing database (partial=%v) to %s", partial, outputFilename) + ll.Debug("Writing database (partial=%v) to %s", partial, outputFilename) worksWithDatabaseMetadata := make(Database, 0) for id, work := range works { work.Metadata.DatabaseMetadata = DatabaseMeta{Partial: partial} @@ -559,7 +560,7 @@ func (ctx *RunContext) ComputeProgressTotal() (workDirectories []fs.DirEntry, er } // If it's not there, this directory is not a project worth scanning. if _, err := os.Stat(descriptionFilename); os.IsNotExist(err) { - LogDebug("skipping %s as it has no description file: %s does not exist", dirEntry.Name(), descriptionFilename) + ll.Debug("skipping %s as it has no description file: %s does not exist", dirEntry.Name(), descriptionFilename) continue } @@ -594,7 +595,7 @@ func (ctx *RunContext) Build(descriptionRaw string, outputFilename string, workI newDescriptionHash := base64.StdEncoding.EncodeToString(hash[:]) if oldWork, found := ctx.PreviouslyBuiltWork(workID); found && oldWork.DescriptionHash == newDescriptionHash && !ctx.Flags.NoCache { - LogDebug("parsing description for %s: using cached work", workID) + ll.Debug("parsing description for %s: using cached work", workID) work = oldWork usedCache = true } else { @@ -613,7 +614,7 @@ func (ctx *RunContext) Build(descriptionRaw string, outputFilename string, workI if block.Type != "media" { continue } - LogDebug("Handling media %#v", block.Media) + ll.Debug("Handling media %#v", block.Media) analyzed, anchor, usedCacheForMedia, err := ctx.HandleMedia(workID, block.ID, block.Media, lang) if err != nil { return Work{}, false, err diff --git a/cmd/add.go b/cmd/add.go index f8620e2..446fb60 100644 --- a/cmd/add.go +++ b/cmd/add.go @@ -8,6 +8,7 @@ import ( "github.com/MakeNowJust/heredoc" ortfodb "github.com/ortfo/db" + ll "github.com/ewen-lbh/label-logger-go" "github.com/spf13/cobra" ) @@ -69,7 +70,7 @@ var addCmd = &cobra.Command{ editor := os.Getenv("EDITOR") if editor != "" { - ortfodb.LogCustom("Opening", "cyan", "%s in %s", descriptionFilepath, editor) + ll.Log("Opening", "cyan", "%s in %s", descriptionFilepath, editor) editorPath, err := exec.LookPath(editor) if err != nil { handleError(fmt.Errorf("while getting path to %s: %w", editor, err)) diff --git a/cmd/exporters.go b/cmd/exporters.go index 3d37e25..e348949 100644 --- a/cmd/exporters.go +++ b/cmd/exporters.go @@ -13,6 +13,7 @@ import ( ortfodb "github.com/ortfo/db" "github.com/spf13/cobra" "github.com/spf13/pflag" + ll "github.com/ewen-lbh/label-logger-go" ) var exportersCmd = &cobra.Command{ @@ -53,7 +54,7 @@ var exportersInitCmd = &cobra.Command{ " "), ), 0644) - ortfodb.LogCustom("Created", "green", fmt.Sprintf("example exporter at [bold]%s.yaml[reset]", args[0])) + ll.Log("Created", "green", fmt.Sprintf("example exporter at [bold]%s.yaml[reset]", args[0])) }, } diff --git a/cmd/ui.go b/cmd/ui.go index 4472b9d..923048e 100644 --- a/cmd/ui.go +++ b/cmd/ui.go @@ -10,7 +10,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/mitchellh/colorstring" - ortfodb "github.com/ortfo/db" + ll "github.com/ewen-lbh/label-logger-go" "github.com/spf13/cobra" "github.com/spf13/pflag" "golang.org/x/term" @@ -176,7 +176,7 @@ func customFlagsUsage(f *pflag.FlagSet) string { } s := buf.String() - if !ortfodb.ShowingColors() { + if !ll.ShowingColors() { s = regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`).ReplaceAllString(s, "") } return s diff --git a/cmd/utils.go b/cmd/utils.go index 495a23a..bc579a4 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -3,51 +3,30 @@ package main import ( "os" "os/signal" - "strings" - "github.com/mitchellh/colorstring" + ll "github.com/ewen-lbh/label-logger-go" ortfodb "github.com/ortfo/db" ) func handleError(err error) { if err != nil { - ortfodb.LogCustom("Error", "red", formatError(err)) + ll.ErrorDisplay("", err) os.Exit(1) } } -func formatError(err error) string { - output := "" - errorFragments := strings.Split(err.Error(), ": ") - for i, fragment := range errorFragments { - if i > 0 { - output += strings.Repeat(" ", i-1) + colorstring.Color("[dim][bold]→[reset] ") - } - if i == 0 { - output += colorstring.Color("[red]" + fragment + "[reset]") - } else if i == len(errorFragments)-1 { - output += colorstring.Color("[bold]" + fragment + "[reset]") - } else { - output += fragment - } - if i < len(errorFragments)-1 { - output += "\n" - } - } - return output -} func handleControlC(outputFilepath string, context *ortfodb.RunContext) { sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt) go func() { for range sig { - ortfodb.LogCustom("Cancelled", "yellow", "Partial database written to [bold]./%s[reset]", context.OutputDatabaseFile) + ll.Log("Cancelled", "yellow", "Partial database written to [bold]./%s[reset]", context.OutputDatabaseFile) buildLockFilepath := ortfodb.BuildLockFilepath(outputFilepath) if _, err := os.Stat(buildLockFilepath); err == nil { os.Remove(buildLockFilepath) } - context.StopProgressBar() + ll.StopProgressBar() os.Exit(1) } }() diff --git a/colors.go b/colors.go index 8e72f12..498f06f 100644 --- a/colors.go +++ b/colors.go @@ -10,6 +10,7 @@ import ( "time" "github.com/EdlinOrg/prominentcolor" + ll "github.com/ewen-lbh/label-logger-go" "github.com/lucasb-eyer/go-colorful" "github.com/zyedidia/generic/mapset" _ "golang.org/x/image/webp" @@ -49,7 +50,7 @@ func (colors *ColorPalette) SortBySaturation() { secondary := colors.Secondary tertiary := colors.Tertiary - LogDebug("sorting colors based on saturations: primary(%s) = %f, secondary(%s) = %f, tertiary(%s) = %f", primary, saturation(primary), secondary, saturation(secondary), tertiary, saturation(tertiary)) + ll.Debug("sorting colors based on saturations: primary(%s) = %f, secondary(%s) = %f, tertiary(%s) = %f", primary, saturation(primary), secondary, saturation(secondary), tertiary, saturation(tertiary)) if saturation(primary) < saturation(secondary) { primary, secondary = secondary, primary @@ -75,7 +76,7 @@ func paletteFromMostSaturated(colors mapset.Set[color.Color]) ColorPalette { bySaturation[hex] = saturation(hex) }) - LogDebug("paletteFromMostSaturated: bySaturation = %v", bySaturation) + ll.Debug("paletteFromMostSaturated: bySaturation = %v", bySaturation) leastSaturatedSaturation := 0.0 mostSaturateds := make([]string, 3) @@ -86,7 +87,7 @@ func paletteFromMostSaturated(colors mapset.Set[color.Color]) ColorPalette { } } - LogDebug("paletteFromMostSaturated: mostSaturateds = %v", mostSaturateds) + ll.Debug("paletteFromMostSaturated: mostSaturateds = %v", mostSaturateds) return ColorPalette{ Primary: mostSaturateds[0], @@ -119,7 +120,7 @@ func canExtractColors(contentType string) bool { // ExtractColors extracts the 3 most proeminent colors from the given image-decodable file. // See https://pkg.go.dev/image#Decode for what formats are decodable. func ExtractColors(filename string, contentType string) (ColorPalette, error) { - defer TimeTrack(time.Now(), "ExtractColors", filename) + defer ll.TimeTrack(time.Now(), "ExtractColors", filename) file, err := os.Open(filename) if err != nil { return ColorPalette{}, err @@ -127,7 +128,7 @@ func ExtractColors(filename string, contentType string) (ColorPalette, error) { defer file.Close() if contentType == "image/gif" { - LogDebug("extract colors from %s: decoding gif", filename) + ll.Debug("extract colors from %s: decoding gif", filename) var decodedGif *gif.GIF decodedGif, err = gif.DecodeAll(file) if err != nil { @@ -154,7 +155,7 @@ func ExtractColors(filename string, contentType string) (ColorPalette, error) { } } - LogDebug("extract colors from %s: extracting most saturated colors from %d unique colors: %v", filename, gifColors.Size(), gifColorsWithAppearanceCount) + ll.Debug("extract colors from %s: extracting most saturated colors from %d unique colors: %v", filename, gifColors.Size(), gifColorsWithAppearanceCount) return paletteFromMostSaturated(gifColors), nil diff --git a/configuration.go b/configuration.go index 6c43aa7..c49efb7 100644 --- a/configuration.go +++ b/configuration.go @@ -6,6 +6,7 @@ import ( "path/filepath" "strings" + ll "github.com/ewen-lbh/label-logger-go" jsoniter "github.com/json-iterator/go" "github.com/mitchellh/go-homedir" "github.com/xeipuuv/gojsonschema" @@ -87,7 +88,7 @@ func LoadConfiguration(filename string, loadInto *Configuration) error { if err != nil { return err } - LogDebug("Loaded configuration from %s to %#v", filename, loadInto) + ll.Debug("Loaded configuration from %s to %#v", filename, loadInto) return nil } @@ -97,14 +98,14 @@ func LoadConfiguration(filename string, loadInto *Configuration) error { func NewConfiguration(filename string) (Configuration, error) { if filename == DefaultConfigurationFilename { if _, err := os.Stat(filename); os.IsNotExist(err) { - LogCustom("Writing", "yellow", "default configuration file at %s", filename) + ll.Log("Writing", "yellow", "default configuration file at %s", filename) defaultConfig := DefaultConfiguration() err := writeYAML(defaultConfig, filename) if err != nil { return Configuration{}, fmt.Errorf("while writing default configuration: %w", err) } - LogWarning("default configuration assumes that your projects live in %s. Change this with [bold]projects at[reset] in the generated configuration file", defaultConfig.ProjectsDirectory) + ll.Warn("default configuration assumes that your projects live in %s. Change this with [bold]projects at[reset] in the generated configuration file", defaultConfig.ProjectsDirectory) return DefaultConfiguration(), nil } } diff --git a/description.go b/description.go index 1ed9b99..ff7c918 100644 --- a/description.go +++ b/description.go @@ -16,6 +16,7 @@ import ( "mvdan.cc/xurls/v2" "github.com/anaskhan96/soup" + ll "github.com/ewen-lbh/label-logger-go" "github.com/k3a/html2text" "github.com/metal3d/go-slugify" "github.com/mitchellh/mapstructure" @@ -25,7 +26,6 @@ import ( "github.com/yuin/goldmark/extension" "github.com/yuin/goldmark/renderer/html" "github.com/zyedidia/generic/mapset" - // goldmarkFrontmatter "github.com/abhinav/goldmark-frontmatter" ) @@ -97,11 +97,11 @@ func ParseYAMLHeader[Metadata interface{}](descriptionRaw string) (Metadata, str // Media content blocks are left unanalyzed. // BuiltAt and DescriptionHash are also not set. func ParseDescription(ctx *RunContext, markdownRaw string, workID string) (Work, error) { - defer TimeTrack(time.Now(), "ParseDescription", workID) + defer ll.TimeTrack(time.Now(), "ParseDescription", workID) metadata, markdownRaw := ParseYAMLHeader[WorkMetadata](markdownRaw) // notLocalizedRaw: raw markdown before the first language marker notLocalizedRaw, localizedRawBlocks := SplitOnLanguageMarkers(markdownRaw) - LogDebug("split description into notLocalizedRaw: %#v and localizedRawBlocks: %#v", notLocalizedRaw, localizedRawBlocks) + ll.Debug("split description into notLocalizedRaw: %#v and localizedRawBlocks: %#v", notLocalizedRaw, localizedRawBlocks) localized := len(localizedRawBlocks) > 0 var allLanguages []string if localized { @@ -587,7 +587,7 @@ func (ctx *RunContext) ParseSingleLanguageDescription(markdownRaw string) (title blocks[i].Paragraph = ReplaceAbbreviations(block.Paragraph, abbreviations) } - LogDebug("Parsed description into blocks: %#v", blocks) + ll.Debug("Parsed description into blocks: %#v", blocks) return } diff --git a/exporter_custom.go b/exporter_custom.go index 67e5c40..015e998 100644 --- a/exporter_custom.go +++ b/exporter_custom.go @@ -11,6 +11,7 @@ import ( "github.com/Masterminds/sprig/v3" "gopkg.in/alessio/shellescape.v1" + ll "github.com/ewen-lbh/label-logger-go" jsoniter "github.com/json-iterator/go" ) @@ -49,12 +50,12 @@ func (e *CustomExporter) OptionsType() any { } func (e *CustomExporter) Before(ctx *RunContext, opts ExporterOptions) error { - LogDebug("Running before commands for %s", e.name) + ll.Debug("Running before commands for %s", e.name) err := e.VerifyRequiredPrograms() if err != nil { return err } - LogDebug("Setting user-supplied data for exporter %s: %v", e.name, opts) + ll.Debug("Setting user-supplied data for exporter %s: %v", e.name, opts) e.data = merge(e.Manifest.Data, opts) if e.Manifest.Verbose { ExporterLogCustom(e, "Debug", "magenta", ".Data for %s is %v", e.name, e.data) @@ -92,7 +93,7 @@ func (e *CustomExporter) runCommands(ctx *RunContext, verbose bool, commands []E } proc := exec.Command("bash", "-c", commandline) - LogDebug("exec.Command = %v", commandline) + ll.Debug("exec.Command = %v", commandline) proc.Dir = filepath.Dir(ctx.Config.source) stderr, _ := proc.StderrPipe() stdout, _ := proc.StdoutPipe() @@ -147,7 +148,7 @@ func (e *CustomExporter) runCommands(ctx *RunContext, verbose bool, commands []E // Hide output atfter it's done if there's no errors for i := 0; i < 6 && i < linesPrintedCount; i++ { if debugging { - LogDebug("would clear line %d", i) + ll.Debug("would clear line %d", i) } else { fmt.Print("\033[1A\033[K") } @@ -196,14 +197,14 @@ func (e *CustomExporter) renderCommandParts(ctx *RunContext, commands []string, } } - LogDebugNoColor("rendering command part %q, data=%v; renderedData=%v", command, e.data, renderedData) + ll.DebugNoColor("rendering command part %q, data=%v; renderedData=%v", command, e.data, renderedData) completeData := merge(additionalData, map[string]any{ "Data": renderedData, "Ctx": ctx, "Verbose": e.verbose, "DryRun": e.dryRun, }) - LogDebugNoColor("rendering (recursive=%v) part %q with data %v", recursive, command, completeData) + ll.DebugNoColor("rendering (recursive=%v) part %q with data %v", recursive, command, completeData) err = tmpl.Execute(&buf, completeData) if err != nil { return []string{}, fmt.Errorf("custom exporter: while rendering template part %s: %w", neutralizeColostring(command), err) diff --git a/exporters.go b/exporters.go index 4d37396..62657a3 100644 --- a/exporters.go +++ b/exporters.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" + ll "github.com/ewen-lbh/label-logger-go" "github.com/mitchellh/mapstructure" "gopkg.in/yaml.v2" ) @@ -134,7 +135,7 @@ func (ctx *RunContext) FindExporter(name string) (Exporter, error) { return LoadExporter(name, rawManifest, ctx.Config.Exporters[name]) } else if isValidURL(ensureHttpPrefix(name)) { url := ensureHttpPrefix(name) - LogDebug("No builtin exporter named %s, attempting download since %s looks like an URL…", name, url) + ll.Debug("No builtin exporter named %s, attempting download since %s looks like an URL…", name, url) return DownloadExporter(name, url, ctx.Config.Exporters[name]) } return nil, fmt.Errorf("no exporter named %s", name) @@ -173,7 +174,7 @@ func LoadExporter(name string, manifestRaw []byte, config map[string]any) (*Cust // DownloadExporter loads an exporter from a URL. func DownloadExporter(name string, url string, config map[string]any) (*CustomExporter, error) { - LogCustom("Installing", "cyan", "exporter at %s", url) + ll.Log("Installing", "cyan", "exporter at %s", url) manifestRaw, err := downloadFile(url) if err != nil { return &CustomExporter{}, fmt.Errorf("while downloading exporter manifest file: %w", err) diff --git a/go.mod b/go.mod index 4f57ffd..e03f436 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,17 @@ module github.com/ortfo/db -go 1.22.2 +go 1.23.2 require ( github.com/EdlinOrg/prominentcolor v1.0.0 github.com/JohannesKaufmann/html-to-markdown v1.5.0 github.com/anaskhan96/soup v1.2.5 github.com/charmbracelet/huh v0.3.0 + github.com/ewen-lbh/label-logger-go v0.1.0 github.com/gabriel-vasile/mimetype v1.4.3 github.com/go-git/go-git/v5 v5.12.0 - github.com/gosuri/uiprogress v0.0.1 github.com/invopop/jsonschema v0.12.0 github.com/json-iterator/go v1.1.12 - github.com/mattn/go-isatty v0.0.20 github.com/metal3d/go-slugify v0.0.0-20160607203414-7ac2014b2f23 github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db github.com/mitchellh/go-homedir v1.1.0 @@ -54,11 +53,13 @@ require ( github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gosuri/uilive v0.0.4 // indirect + github.com/gosuri/uiprogress v0.0.1 // indirect github.com/huandu/xstrings v1.4.0 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect diff --git a/go.sum b/go.sum index 48b9511..b53290b 100644 --- a/go.sum +++ b/go.sum @@ -51,6 +51,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/ewen-lbh/label-logger-go v0.1.0 h1:xMifUH3MBTSUK2q/unqkSvw+PjPG/tddrUZiJB+a2PM= +github.com/ewen-lbh/label-logger-go v0.1.0/go.mod h1:ORVakjovWm+MfrGXmHBZAJvxNqYwAxdG3Sev8CXXChM= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= diff --git a/layout.go b/layout.go index 6a63088..30cb848 100644 --- a/layout.go +++ b/layout.go @@ -2,6 +2,7 @@ package ortfodb import ( "fmt" + ll "github.com/ewen-lbh/label-logger-go" "strconv" ) @@ -35,7 +36,7 @@ func lcm(integers ...int) int { // Normalize returns a normalized layout where every row has the same number of cells. func (layout Layout) Normalize() (normalized Layout) { - LogDebug("normalizing layout %#v", layout) + ll.Debug("normalizing layout %#v", layout) normalized = make(Layout, 0) // Determine the common width @@ -82,7 +83,7 @@ func (layout Layout) BlockIDs() (blockIDs []string) { // ResolveLayout returns a layout, given the parsed description. func ResolveLayout(metadata WorkMetadata, language string, blocks []ContentBlock) (Layout, error) { - LogDebug("Resolving layout from metadata %#v", metadata) + ll.Debug("Resolving layout from metadata %#v", metadata) layout := make(Layout, 0) userProvided := metadata.AdditionalMetadata["layout"] // Handle case where the layout is explicitly specified. @@ -91,7 +92,7 @@ func ResolveLayout(metadata WorkMetadata, language string, blocks []ContentBlock if _, ok := userProvided.([]interface{}); ok { for _, line := range userProvided.([]interface{}) { layoutLine := make([]LayoutCell, 0) - LogDebug("processing layout line %#v", line) + ll.Debug("processing layout line %#v", line) switch line := line.(type) { case string: cell, err := ResolveBlockID(blocks, language, line) @@ -101,10 +102,10 @@ func ResolveLayout(metadata WorkMetadata, language string, blocks []ContentBlock layoutLine = append(layoutLine, LayoutCell(cell)) case nil: - LogDebug("encountered nil value in layout single line, treating as empty cell") + ll.Debug("encountered nil value in layout single line, treating as empty cell") layoutLine = append(layoutLine, LayoutCell(EmptyLayoutCell)) case []interface{}: - LogDebug("processing layout line %#v", line) + ll.Debug("processing layout line %#v", line) for _, cell := range line { if val, ok := cell.(string); ok { cell, err := ResolveBlockID(blocks, language, val) @@ -114,7 +115,7 @@ func ResolveLayout(metadata WorkMetadata, language string, blocks []ContentBlock layoutLine = append(layoutLine, LayoutCell(cell)) } else if cell == nil { - LogDebug("encountered nil value in layout line, treating as empty cell") + ll.Debug("encountered nil value in layout line, treating as empty cell") layoutLine = append(layoutLine, LayoutCell(EmptyLayoutCell)) } } @@ -128,7 +129,7 @@ func ResolveLayout(metadata WorkMetadata, language string, blocks []ContentBlock layout = append(layout, []LayoutCell{LayoutCell(block.ID)}) } } - LogDebug("Layout resolved to %#v", layout) + ll.Debug("Layout resolved to %#v", layout) return layout.Normalize(), nil } diff --git a/media.go b/media.go index 9a230d7..2271d30 100644 --- a/media.go +++ b/media.go @@ -31,6 +31,7 @@ import ( "github.com/gabriel-vasile/mimetype" // "github.com/gen2brain/go-fitz" // FIXME requires cgo, which goreleaser has a hard time with + ll "github.com/ewen-lbh/label-logger-go" "github.com/lafriks/go-svg" "github.com/metal3d/go-slugify" recurcopy "github.com/plus3it/gorecurcopy" @@ -146,8 +147,8 @@ func (ctx *RunContext) PathToWorkFolder(workID string) string { // AnalyzeMediaFile analyzes the file at its absolute filepath filename and returns a Media struct, merging the analysis' results with information from the matching MediaEmbedDeclaration. // TODO prevent duplicate analysis of the same file in the current session even when file was never analyzed on previous runs of the command func (ctx *RunContext) AnalyzeMediaFile(workID string, embedDeclaration Media) (usedCache bool, analyzedMedia Media, anchor string, err error) { - defer TimeTrack(time.Now(), "AnalyzeMediaFile", workID, embedDeclaration.RelativeSource) - LogDebug("Analyzing media %#v", embedDeclaration) + defer ll.TimeTrack(time.Now(), "AnalyzeMediaFile", workID, embedDeclaration.RelativeSource) + ll.Debug("Analyzing media %#v", embedDeclaration) // Compute absolute filepath to media var filename string @@ -180,10 +181,10 @@ func (ctx *RunContext) AnalyzeMediaFile(workID string, embedDeclaration Media) ( } if usedCache && cachedAnalysis.ContentType != "" { - LogDebug("Reusing cached analysis %#v", cachedAnalysis) + ll.Debug("Reusing cached analysis %#v", cachedAnalysis) return true, cachedAnalysis, anchor, nil } else if usedCache { - LogDebug("UseMediaCache tells me to use cache for %s, but the cached analysis has no content type. Will reanalyze.", filename) + ll.Debug("UseMediaCache tells me to use cache for %s, but the cached analysis has no content type. Will reanalyze.", filename) } ctx.Status(workID, PhaseMediaAnalysis, string(embedDeclaration.RelativeSource)) @@ -216,15 +217,15 @@ func (ctx *RunContext) AnalyzeMediaFile(workID string, embedDeclaration Media) ( } if ctx.Config.ExtractColors.Enabled { if canExtractColors(contentType) { - LogDebug("Extracting colors from %s", filename) + ll.Debug("Extracting colors from %s", filename) colors, err = ExtractColors(filename, contentType) if err != nil { - DisplayError("Could not extract colors from %s", err, filename) + ll.ErrorDisplay("Could not extract colors from %s", err, filename) err = nil } - LogDebug("Colors extracted from %s: %#v", filename, colors) + ll.Debug("Colors extracted from %s: %#v", filename, colors) } else { - LogDebug("Not extracting colors from %s: unsupported content type", filename) + ll.Debug("Not extracting colors from %s: unsupported content type", filename) } } } @@ -234,17 +235,17 @@ func (ctx *RunContext) AnalyzeMediaFile(workID string, embedDeclaration Media) ( if err != nil { return } - LogDebug("Video analyzed: dimensions=%#v, duration=%v, hasSound=%v", dimensions, duration, hasSound) + ll.Debug("Video analyzed: dimensions=%#v, duration=%v, hasSound=%v", dimensions, duration, hasSound) } if isAudio { duration = AnalyzeAudio(file) hasSound = true - LogDebug("Audio analyzed: duration=%v", duration) + ll.Debug("Audio analyzed: duration=%v", duration) } if isPDF { - LogWarning("PDF analysis is disabled") + ll.Warn("PDF analysis is disabled") // dimensions, duration, err = AnalyzePDF(filename) // if err != nil { // return @@ -267,13 +268,13 @@ func (ctx *RunContext) AnalyzeMediaFile(workID string, embedDeclaration Media) ( Analyzed: true, Hash: contentHash, } - LogDebug("Analyzed to %#v (no cache used)", analyzedMedia) + ll.Debug("Analyzed to %#v (no cache used)", analyzedMedia) return } func (ctx *RunContext) UseMediaCache(filename string, embedDeclaration Media, workID string) (newHash string, used bool, media Media, err error) { if ctx.Flags.NoCache { - LogDebug("--no-cache is set, eagerly computing hash") + ll.Debug("--no-cache is set, eagerly computing hash") newHash, err = hashFile(filename) if err != nil { err = fmt.Errorf("while computing hash of media: %w", err) @@ -282,7 +283,7 @@ func (ctx *RunContext) UseMediaCache(filename string, embedDeclaration Media, wo return } - LogDebug("checking mtime of %s before trying to compute hashes", filename) + ll.Debug("checking mtime of %s before trying to compute hashes", filename) stat, err := os.Stat(filename) if err != nil { err = fmt.Errorf("could not check modification times of %s: %w", filename, err) @@ -295,16 +296,16 @@ func (ctx *RunContext) UseMediaCache(filename string, embedDeclaration Media, wo if oldMedia, oldWork, oldMediaFound = ctx.PreviouslyBuiltMedia(workID, embedDeclaration); oldMediaFound { if embedDeclaration.Hash == "" { - LogDebug("media %s in old database has no hash stored, hash will be computed.", filename) + ll.Debug("media %s in old database has no hash stored, hash will be computed.", filename) } else if oldWork.BuiltAt.After(stat.ModTime()) { - LogDebug("mtime cache strategy: not recomputing hash of %s because it was last modified before the previous build (file modified at %s, previous build at %s) , using cached analysis", filename, stat.ModTime(), oldWork.BuiltAt) - LogDebug("cache hit by modtime for %s: using cache from embed decl %#v", filename, embedDeclaration) + ll.Debug("mtime cache strategy: not recomputing hash of %s because it was last modified before the previous build (file modified at %s, previous build at %s) , using cached analysis", filename, stat.ModTime(), oldWork.BuiltAt) + ll.Debug("cache hit by modtime for %s: using cache from embed decl %#v", filename, embedDeclaration) return embedDeclaration.Hash, true, oldMedia, nil } else { - LogDebug("file mtime of %s is newer than previous build (file modified at %s, previous build at %s), computing hash", filename, stat.ModTime(), oldWork.BuiltAt) + ll.Debug("file mtime of %s is newer than previous build (file modified at %s, previous build at %s), computing hash", filename, stat.ModTime(), oldWork.BuiltAt) } } else { - LogDebug("mtime cache strategy: media %s of %s not found in previous database, will compute hash", filename, workID) + ll.Debug("mtime cache strategy: media %s of %s not found in previous database, will compute hash", filename, workID) } newHash, err = hashFile(filename) @@ -315,14 +316,14 @@ func (ctx *RunContext) UseMediaCache(filename string, embedDeclaration Media, wo if oldMediaFound { if oldMedia.Hash == newHash { - LogDebug("cache hit by hash for %s: using cache from embed decl %#v", filename, embedDeclaration) + ll.Debug("cache hit by hash for %s: using cache from embed decl %#v", filename, embedDeclaration) return newHash, true, oldMedia, nil } - LogDebug("Cache miss for %s: old content hash %q is different from %q", filename, oldMedia.Hash, newHash) + ll.Debug("Cache miss for %s: old content hash %q is different from %q", filename, oldMedia.Hash, newHash) return newHash, false, embedDeclaration, nil } - LogDebug("Cache miss for %s: media not found in previous database build", filename) + ll.Debug("Cache miss for %s: media not found in previous database build", filename) return } @@ -403,7 +404,7 @@ func AnalyzeVideo(filename string) (dimensions ImageDimensions, duration uint, h } func (ctx *RunContext) HandleMedia(workID string, blockID string, embedDeclaration Media, language string) (media Media, anchor string, usedCache bool, err error) { - defer TimeTrack(time.Now(), "HandleMedia", workID, embedDeclaration.RelativeSource) + defer ll.TimeTrack(time.Now(), "HandleMedia", workID, embedDeclaration.RelativeSource) usedCache, media, anchor, err = ctx.AnalyzeMediaFile(workID, embedDeclaration) if err != nil { err = fmt.Errorf("while analyzing media: %w", err) @@ -422,7 +423,7 @@ func (ctx *RunContext) HandleMedia(workID string, blockID string, embedDeclarati copyingStepStart := time.Now() skipCopy := usedCache && fileExists(absolutePathDestination) if skipCopy { - LogDebug("Skipping media copy for %s because it already exists", absolutePathDestination) + ll.Debug("Skipping media copy for %s because it already exists", absolutePathDestination) } if absolutePathDestination != absolutePathSource && !skipCopy { err = os.MkdirAll(filepath.Dir(absolutePathDestination), 0o755) @@ -447,13 +448,13 @@ func (ctx *RunContext) HandleMedia(workID string, blockID string, embedDeclarati return } } - TimeTrack(copyingStepStart, "HandleMedia > copy to dist", media.RelativeSource, media.DistSource) + ll.TimeTrack(copyingStepStart, "HandleMedia > copy to dist", media.RelativeSource, media.DistSource) thumbnailsStepStart := time.Now() // Make thumbnail if media.Thumbnailable() && ctx.Config.MakeThumbnails.Enabled { if media.Thumbnails == nil { - LogDebug("%s: initializing thumbnails map since it's nil in the (previously built?) work", media.RelativeSource) + ll.Debug("%s: initializing thumbnails map since it's nil in the (previously built?) work", media.RelativeSource) media.Thumbnails = make(map[int]FilePathInsideMediaRoot) } type result struct { @@ -468,11 +469,11 @@ func (ctx *RunContext) HandleMedia(workID string, blockID string, embedDeclarati for i, sizesToDo := range chunkSlice(ctx.Config.MakeThumbnails.Sizes, ctx.thumbnailersPerWork) { go func(i int, sizesToDo []int, results chan result) { for _, size := range sizesToDo { - LogDebug("Making thumbnail @%d for %s#%s", size, media.RelativeSource, blockID) + ll.Debug("Making thumbnail @%d for %s#%s", size, media.RelativeSource, blockID) saveTo := ctx.ComputeOutputThumbnailFilename(media, blockID, workID, size, language) if _, err := os.Stat(string(saveTo.Absolute(ctx))); err == nil && usedCache { - LogDebug("Skipping thumbnail creation @%d for %s#%s because it already exists", size, media.RelativeSource, blockID) + ll.Debug("Skipping thumbnail creation @%d for %s#%s because it already exists", size, media.RelativeSource, blockID) results <- result{size: size, skipped: true} continue } @@ -488,7 +489,7 @@ func (ctx *RunContext) HandleMedia(workID string, blockID string, embedDeclarati results <- result{err: fmt.Errorf("while making thumbnail @%d for %s: %w", size, workID, err)} continue } - LogDebug("Made thumbnail %s", saveTo) + ll.Debug("Made thumbnail %s", saveTo) results <- result{size: size} } }(i, sizesToDo, results) @@ -509,7 +510,7 @@ func (ctx *RunContext) HandleMedia(workID string, blockID string, embedDeclarati } } } - TimeTrack(thumbnailsStepStart, "HandleMedia > thumbnails", media.RelativeSource) + ll.TimeTrack(thumbnailsStepStart, "HandleMedia > thumbnails", media.RelativeSource) return } diff --git a/metadata.go b/metadata.go index a452280..17cf043 100644 --- a/metadata.go +++ b/metadata.go @@ -9,6 +9,7 @@ import ( "strings" "sync" + ll "github.com/ewen-lbh/label-logger-go" "github.com/go-git/go-git/v5/plumbing/format/gitignore" "github.com/metal3d/go-slugify" "gopkg.in/yaml.v2" @@ -135,10 +136,10 @@ func (t autodetectData) Detect(ctx *RunContext, workId string) (matched bool, er contentDetectionConditions[path] = append(contentDetectionConditions[path], content) } } - LogDebug("Starting auto-detect for %s: contentDetection map is %v", t, contentDetectionConditions) + ll.Debug("Starting auto-detect for %s: contentDetection map is %v", t, contentDetectionConditions) for _, f := range append(t.Files, contentDetectionFiles...) { _, isContentDetection := contentDetectionConditions[f] - LogDebug("Auto-detecting %s in %s: %q: isContentDetection=%v", t, workId, f, isContentDetection) + ll.Debug("Auto-detecting %s in %s: %q: isContentDetection=%v", t, workId, f, isContentDetection) pat := gitignore.ParsePattern(f, nil) // Walk all files of the work folder (excl. hidden files unfortunately) err = fs.WalkDir(os.DirFS(ctx.PathToWorkFolder(workId)), ".", func(path string, d fs.DirEntry, err error) error { @@ -170,7 +171,7 @@ func (t autodetectData) Detect(ctx *RunContext, workId string) (matched bool, er for _, contentCondition := range contentDetectionConditions[path] { if strings.Contains(contents, contentCondition) { - LogDebug("Auto-detected %s in %s: condition %q in %q met", t, workId, contentCondition, path) + ll.Debug("Auto-detected %s in %s: condition %q in %q met", t, workId, contentCondition, path) matched = true return filepath.SkipAll } @@ -179,11 +180,11 @@ func (t autodetectData) Detect(ctx *RunContext, workId string) (matched bool, er } else { result := pat.Match(pathFragments, d.IsDir()) if result == gitignore.Exclude { - LogDebug("Auto-detected %s in %s: filepattern %q matches %q", t, workId, f, path) + ll.Debug("Auto-detected %s in %s: filepattern %q matches %q", t, workId, f, path) matched = true return filepath.SkipAll } else if result == gitignore.Include { - LogDebug("Auto-detected %s in %s: filepattern %q matches %q", t, workId, f, path) + ll.Debug("Auto-detected %s in %s: filepattern %q matches %q", t, workId, f, path) matched = false return filepath.SkipAll } @@ -308,7 +309,7 @@ func (ctx *RunContext) LoadTagsRepository() ([]Tag, error) { var tags []Tag if ctx.Config.Tags.Repository == "" { - LogWarning("No tags repository specified in configuration at %s", ctx.Config.source) + ll.Warn("No tags repository specified in configuration at %s", ctx.Config.source) return []Tag{}, nil } raw, err := readFileBytes(ctx.Config.Tags.Repository) @@ -332,7 +333,7 @@ func (ctx *RunContext) LoadTechnologiesRepository() ([]Technology, error) { var technologies []Technology if ctx.Config.Technologies.Repository == "" { - LogWarning("No technologies repository specified in configuration at %s", ctx.Config.source) + ll.Warn("No technologies repository specified in configuration at %s", ctx.Config.source) return []Technology{}, nil } raw, err := readFileBytes(ctx.Config.Technologies.Repository) diff --git a/progress.go b/progress.go index 241005f..499a912 100644 --- a/progress.go +++ b/progress.go @@ -5,14 +5,10 @@ import ( "fmt" "os" "strings" - "time" - "github.com/gosuri/uiprogress" - "github.com/mitchellh/colorstring" + ll "github.com/ewen-lbh/label-logger-go" ) -var progressbar *uiprogress.Bar -var progressBars *uiprogress.Progress var currentlyBuildingWorkIDs []string var builtWorksCount int var worksToBuildCount int @@ -27,98 +23,36 @@ const ( PhaseUnchanged BuildPhase = "Reusing" ) +func (phase BuildPhase) String() string { + return string(phase) +} + func padPhaseVerb(phase BuildPhase) string { // length of longest phase verb: "Thumbnailing", plus some padding return fmt.Sprintf("%15s", phase) } func (ctx *RunContext) StartProgressBar(total int) { - if progressbar != nil { - panic("progress bar already started") - } - worksToBuildCount = total - - if isInteractiveTerminal() || os.Getenv("FORCE_PROGRESS_BAR") == "1" { - LogDebug("terminal is interactive, starting progress bar") - } else { - LogDebug("not starting progress bar because not in an interactive terminal") - return - } - - progressBars = uiprogress.New() - progressBars.SetRefreshInterval(1 * time.Millisecond) - progressbar = progressBars.AddBar(total) - progressbar.Empty = ' ' - progressbar.Fill = '=' - progressbar.Head = '>' - progressbar.Width = 30 - progressbar.PrependFunc(func(b *uiprogress.Bar) string { - if ShowingColors() { - return colorstring.Color( - fmt.Sprintf( - `[magenta][bold]%15s[reset]`, - "Building", - ), - ) - } - return fmt.Sprintf("%15s", "Building") - }) - progressbar.AppendFunc(func(b *uiprogress.Bar) string { - // truncatedCurrentlyBuildingWorkIDs := make([]string, 0, len(currentlyBuildingWorkIDs)) - // for _, id := range currentlyBuildingWorkIDs { - // if len(id) > 5 { - // truncatedCurrentlyBuildingWorkIDs = append(truncatedCurrentlyBuildingWorkIDs, id[:5]) - // } else { - // truncatedCurrentlyBuildingWorkIDs = append(truncatedCurrentlyBuildingWorkIDs, id) - // } - // } - - return fmt.Sprintf("%d/%d", b.Current(), b.Total) - }) - progressBars.Start() + ll.StartProgressBar(total, "Building", "magenta") } func (ctx *RunContext) IncrementProgress() { builtWorksCount++ if BuildIsFinished() { - ctx.showFinishedMessage() + ll.Log("Finished", "green", "compiling to %s\n", ctx.OutputDatabaseFile) os.Remove(ctx.ProgressInfoFile) } - if progressbar != nil { - progressbar.Incr() - if BuildIsFinished() { - ctx.StopProgressBar() - } + ll.IncrementProgressBar() + if BuildIsFinished() { + ll.StopProgressBar() } } func BuildIsFinished() bool { - if progressbar == nil { - return builtWorksCount >= worksToBuildCount - } - return progressbar.CompletedPercent() >= 100 -} - -func (ctx *RunContext) showFinishedMessage() { - if progressbar == nil { - LogCustom("Finished", "green", "compiling to %s\n", ctx.OutputDatabaseFile) - } else { - LogCustom("Finished", "green", "compiling to %s in %s\n", ctx.OutputDatabaseFile, progressbar.TimeElapsedString()) - } -} - -func (ctx *RunContext) StopProgressBar() { - if progressbar == nil { - return - } - - progressBars.Bars = nil - progressBars.Stop() - // Clear progress bar empty line - fmt.Print("\r\033[K") + return ll.ProgressBarFinished() || builtWorksCount >= worksToBuildCount } // Status updates the current progress and writes the progress to a file if --write-progress is set. @@ -136,17 +70,8 @@ func (ctx *RunContext) Status(workID string, phase BuildPhase, details ...string if len(details) > 0 { formattedDetails = fmt.Sprintf(" [dim]%s[reset]", strings.Join(details, " ")) } - formattedMessage := colorstring.Color(fmt.Sprintf("[bold][%s]%s[reset] %s"+formattedDetails, color, padPhaseVerb(phase), workID)) - if progressBars != nil { - writer := progressBars.Bypass() - if !ShowingColors() { - writer = noAnsiCodesWriter{out: writer} - } - fmt.Fprintln(writer, formattedMessage) - } else { - Println(formattedMessage) - } + ll.Log(phase.String(), color, "%s%s", workID, formattedDetails) if phase == PhaseBuilt || phase == PhaseUnchanged { for i, id := range currentlyBuildingWorkIDs { @@ -161,7 +86,7 @@ func (ctx *RunContext) Status(workID string, phase BuildPhase, details ...string } if err := ctx.appendToProgressFile(workID, phase, details...); err != nil { - DisplayWarning("could not append progress info to file", err) + ll.WarnDisplay("could not append progress info to file", err) } } @@ -178,7 +103,7 @@ type ProgressInfoEvent struct { func (ctx *RunContext) appendToProgressFile(workID string, phase BuildPhase, details ...string) error { if ctx.ProgressInfoFile == "" { - LogDebug("not writing progress info to file because --write-progress is not set") + ll.Debug("not writing progress info to file because --write-progress is not set") return nil } event := ProgressInfoEvent{ @@ -188,7 +113,7 @@ func (ctx *RunContext) appendToProgressFile(workID string, phase BuildPhase, det Phase: phase, Details: details, } - LogDebug("Appending event %#v to progress file %s", event, ctx.ProgressInfoFile) + ll.Debug("Appending event %#v to progress file %s", event, ctx.ProgressInfoFile) // append JSON marshalled event to file file, err := os.OpenFile(ctx.ProgressInfoFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { diff --git a/replication.go b/replication.go index d6ab366..cec0742 100644 --- a/replication.go +++ b/replication.go @@ -9,6 +9,7 @@ import ( html2md "github.com/JohannesKaufmann/html-to-markdown" "github.com/anaskhan96/soup" + ll "github.com/ewen-lbh/label-logger-go" "github.com/mitchellh/mapstructure" "gopkg.in/yaml.v2" ) @@ -73,7 +74,7 @@ func (ctx *RunContext) replicateLocalizedBlock(work Work, language string) (stri // Then, for each block (ordered by the layout) // spew.Dump(work) for _, block := range content.Blocks { - LogDebug("replicating %s block #%s", block.Type, block.ID) + ll.Debug("replicating %s block #%s", block.Type, block.ID) switch block.Type { case "media": result += ctx.replicateMediaEmbed(block.Media) + end diff --git a/thumbnails.go b/thumbnails.go index 39b891f..0176b7d 100644 --- a/thumbnails.go +++ b/thumbnails.go @@ -3,6 +3,7 @@ package ortfodb import ( "bytes" "fmt" + ll "github.com/ewen-lbh/label-logger-go" "io" "io/ioutil" "os" @@ -36,7 +37,7 @@ func (m Media) Thumbnailable() bool { // It returns the path where the thumbnail has been written to. // saveTo should be relative to cwd. func (ctx *RunContext) MakeThumbnail(media Media, targetSize int, saveTo string) error { - LogDebug("Making thumbnail for %s at size %d to %s", media.DistSource.Absolute(ctx), targetSize, saveTo) + ll.Debug("Making thumbnail for %s at size %d to %s", media.DistSource.Absolute(ctx), targetSize, saveTo) if media.ContentType == "image/gif" { return ctx.makeGifThumbnail(media, targetSize, saveTo) } diff --git a/ui.go b/ui.go index 367f756..7245f3c 100644 --- a/ui.go +++ b/ui.go @@ -7,50 +7,13 @@ import ( "regexp" "strings" - "time" - - "github.com/mattn/go-isatty" + ll "github.com/ewen-lbh/label-logger-go" "github.com/mitchellh/colorstring" "github.com/xeipuuv/gojsonschema" ) var LogFilePath string var PrependDateToLogs = false -var showingTimingLogs = os.Getenv("DEBUG_TIMING") != "" - -func logWriter(original io.Writer) io.Writer { - writer := original - if LogFilePath != "" { - logfile, err := os.OpenFile(LogFilePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) - if err == nil { - writer = io.MultiWriter(writer, logfile) - } - } - - if PrependDateToLogs { - writer = prependDateWriter{out: writer} - } - - if progressBars != nil { - writer = progressBars.Bypass() - } - if !ShowingColors() { - writer = noAnsiCodesWriter{out: writer} - } - return writer -} - -type prependDateWriter struct { - out io.Writer -} - -func (w prependDateWriter) Write(p []byte) (n int, err error) { - return w.out.Write([]byte( - fmt.Sprintf("[%s] %s", - time.Now(), - strings.TrimLeft(string(p), " "), - ))) -} // noAnsiCodesWriter is an io.Writer that writes to the underlying writer, but strips ANSI color codes beforehand type noAnsiCodesWriter struct { @@ -65,7 +28,7 @@ func (w noAnsiCodesWriter) Write(p []byte) (n int, err error) { func Println(a ...interface{}) { var writer io.Writer writer = os.Stdout - if !ShowingColors() { + if !ll.ShowingColors() { writer = noAnsiCodesWriter{out: writer} } fmt.Fprintln(writer, a...) @@ -73,13 +36,21 @@ func Println(a ...interface{}) { // Printf is like fmt.Printf but automatically strips ANSI color codes if colors are disabled func Printf(format string, a ...interface{}) { - writer := logWriter(os.Stdout) + var writer io.Writer + writer = os.Stdout + if !ll.ShowingColors() { + writer = noAnsiCodesWriter{out: writer} + } fmt.Fprintf(writer, format, a...) } // Print is like fmt.Print but automatically strips ANSI color codes if colors are disabled func Print(a ...interface{}) { - writer := logWriter(os.Stdout) + var writer io.Writer + writer = os.Stdout + if !ll.ShowingColors() { + writer = noAnsiCodesWriter{out: writer} + } fmt.Fprint(writer, a...) } @@ -90,38 +61,20 @@ func indentSubsequent(size int, text string) string { func ExporterLogCustom(exporter Exporter, verb string, color string, message string, fmtArgs ...interface{}) { if debugging { - LogCustom(verb, color, fmt.Sprintf("[dim][bold](from exporter %s)[reset] %s", exporter.Name(), message), fmtArgs...) + ll.Log(verb, color, fmt.Sprintf("[dim][bold](from exporter %s)[reset] %s", exporter.Name(), message), fmtArgs...) } else { - LogCustom(verb, color, message, fmtArgs...) + ll.Log(verb, color, message, fmtArgs...) } } func ExporterLogCustomNoFormatting(exporter Exporter, verb string, color string, message string) { if debugging { - LogCustomNoFormatting(verb, color, colorstring.Color("[dim][bold](from exporter "+exporter.Name()+")[reset] ")+message) + ll.LogNoFormatting(verb, color, colorstring.Color("[dim][bold](from exporter "+exporter.Name()+")[reset] ")+message) } else { - LogCustomNoFormatting(verb, color, message) + ll.LogNoFormatting(verb, color, message) } } -func LogCustom(verb string, color string, message string, fmtArgs ...interface{}) { - LogCustomNoFormatting(verb, color, colorstring.Color(fmt.Sprintf(message, fmtArgs...))) -} - -// LogCustomNoColor logs a message without applying colorstring syntax to message. -func LogCustomNoColor(verb string, color string, message string, fmtArgs ...interface{}) { - LogCustomNoFormatting(verb, color, fmt.Sprintf(message, fmtArgs...)) -} - -func LogCustomNoFormatting(verb string, color string, message string) { - fmt.Fprintln( - logWriter(os.Stderr), - colorstring.Color(fmt.Sprintf("[bold][%s]%15s[reset]", color, verb))+ - " "+ - indentSubsequent(15+1, message), - ) -} - // DisplayValidationErrors takes in a slice of json schema validation errors and displays them nicely to in the terminal. func DisplayValidationErrors(errors []gojsonschema.ResultError, filename string, rootPath ...string) { println("Your " + filename + " file is invalid. Here are the validation errors:\n") @@ -154,99 +107,6 @@ func displayValidationErrorFieldPath(field string, rootPath ...string) string { return strings.Join(append(rootPath, field), "/") } -// LogError logs non-fatal errors. -func LogError(message string, fmtArgs ...interface{}) { - LogCustom("Error", "red", message, fmtArgs...) -} - -func DisplayError(msg string, err error, fmtArgs ...interface{}) { - LogError(formatErrors(fmt.Errorf(msg+": %w", append(fmtArgs, err)...))) -} - -func DisplayWarning(msg string, err error, fmtArgs ...interface{}) { - LogWarning(formatErrors(fmt.Errorf(msg+": %w", append(fmtArgs, err)...))) -} - -// LogInfo logs infos. -func LogInfo(message string, fmtArgs ...interface{}) { - LogCustom("Info", "blue", message, fmtArgs...) -} - -// LogDebug logs debug information. -func LogDebug(message string, fmtArgs ...interface{}) { - if os.Getenv("DEBUG") == "" { - return - } - LogCustom("Debug", "magenta", message, fmtArgs...) -} - -// LogDebugNoColor logs debug information without applying colorstring syntax to message. -func LogDebugNoColor(message string, fmtArgs ...interface{}) { - if os.Getenv("DEBUG") == "" { - return - } - LogCustomNoColor("Debug", "magenta", message, fmtArgs...) -} - -// LogWarning logs warnings. -func LogWarning(message string, fmtArgs ...interface{}) { - LogCustom("Warning", "yellow", message, fmtArgs...) -} - -// LogTiming logs timing debug logs. Mostly used with TimeTrack -func LogTiming(job string, args []interface{}, timeTaken time.Duration) { - if !showingTimingLogs { - return - } - formattedArgs := "" - for i, arg := range args { - if i > 0 { - formattedArgs += " " - } - formattedArgs += fmt.Sprintf("%v", arg) - } - LogCustom("Timing", "dim", "[bold]%-30s[reset][dim]([reset]%-50s[dim])[reset] took [yellow]%s", job, formattedArgs, timeTaken) -} - -// TimeTrack logs the time taken for a function to execute, and logs out the time taken. -// Usage: at the top of your function, defer TimeTrack(time.Now(), "your job name") -func TimeTrack(start time.Time, job string, args ...interface{}) { - if !showingTimingLogs { - return - } - elapsed := time.Since(start) - LogTiming(job, args, elapsed) -} - -func formatList(list []string, format string, separator string) string { - result := "" - for i, tag := range list { - sep := separator - if i == len(list)-1 { - sep = "" - } - result += fmt.Sprintf(format, tag) + sep - } - return result -} - -// formatErrors returns a string where the error message was split on ': ', and each item is on a new line, indented once more than the previous line. -func formatErrors(err error) string { - causes := strings.Split(err.Error(), ": ") - output := "" - for i, cause := range causes { - output += strings.Repeat(" ", i) + cause - if i < len(causes)-1 { - output += "\n" - } - } - return output -} - -func isInteractiveTerminal() bool { - return isatty.IsTerminal(os.Stdout.Fd()) && isatty.IsTerminal(os.Stderr.Fd()) -} - func stripansicolors(b []byte) []byte { // TODO find a way to do this without converting to string s := string(b) diff --git a/utils.go b/utils.go index 20e8bcf..174ce7c 100644 --- a/utils.go +++ b/utils.go @@ -14,6 +14,7 @@ import ( "regexp" "strings" + ll "github.com/ewen-lbh/label-logger-go" "github.com/invopop/jsonschema" "github.com/xeipuuv/gojsonschema" "gopkg.in/yaml.v3" @@ -243,19 +244,6 @@ func ensureHttpPrefix(url string) string { return url } -// ShowingColors returns true if colors (ANSI escape codes) should be printed. -// Environment variables can control this: NO_COLOR=1 disables colors, and FORCE_COLOR=1 forces colors. -// Otherwise, heuristics (such as whether the output is an interactive terminal) are used. -func ShowingColors() bool { - if os.Getenv("NO_COLOR") == "1" { - return false - } - if os.Getenv("FORCE_COLOR") == "1" { - return true - } - return isInteractiveTerminal() -} - func writeYAML(v any, filename string) error { encoded, err := yaml.Marshal(v) if err != nil { @@ -302,14 +290,14 @@ func copyFile(src, dest string) error { } func hashFile(filename string) (string, error) { - LogDebug("reading %s for hash computation", filename) + ll.Debug("reading %s for hash computation", filename) content, err := os.ReadFile(filename) if err != nil { return "", fmt.Errorf("while reading file %s for hashing: %w", filename, err) } - LogDebug("computing hash of %s", filename) + ll.Debug("computing hash of %s", filename) hash := md5.Sum(content) return base64.StdEncoding.EncodeToString(hash[:]), nil } diff --git a/validation.go b/validation.go index 0bdcefd..c750068 100644 --- a/validation.go +++ b/validation.go @@ -3,6 +3,7 @@ package ortfodb import ( "encoding/json" + ll "github.com/ewen-lbh/label-logger-go" "github.com/xeipuuv/gojsonschema" ) @@ -14,7 +15,7 @@ func ValidateAsJSONSchema(typ any, yaml bool, values any) []gojsonschema.ResultE } _, valiationErrors, err := validateWithJSONSchema(string(jsonOpts), schema) if err != nil { - LogCustom("Error", "red", "could not validate as JSON schema: %s", err) + ll.Log("Error", "red", "could not validate as JSON schema: %s", err) return []gojsonschema.ResultError{} } return valiationErrors