Skip to content

Commit

Permalink
issue #70: proof of concept refactoring of diff command
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilsk committed Sep 13, 2021
1 parent 1522818 commit bf45f39
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 78 deletions.
4 changes: 2 additions & 2 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"2013-12-27T00:00:00Z": 2
}
$ maintainer github contribution diff --base=/tmp/snap.01.2013.json 2013
$ maintainer github contribution diff /tmp/snap.01.2013.json 2013
Day / Week #46 #48 #49 #50
---------------------- --------------- --------------- --------------- -----------
Sunday - - - -
Expand All @@ -81,7 +81,7 @@
---------------------- --------------- --------------- --------------- -----------
The diff between head{"/tmp/snap.02.2013.json"} → base{"/tmp/snap.01.2013.json"}
$ maintainer github contribution diff --base=/tmp/snap.01.2013.json --head=/tmp/snap.02.2013.json
$ maintainer github contribution diff /tmp/snap.01.2013.json /tmp/snap.02.2013.json
```

* Suggests a reasonable date to contribute
Expand Down
9 changes: 3 additions & 6 deletions internal/command/github/contribution.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"go.octolab.org/toolset/maintainer/internal/command/github/view"
"go.octolab.org/toolset/maintainer/internal/config"
"go.octolab.org/toolset/maintainer/internal/model/github/contribution"
"go.octolab.org/toolset/maintainer/internal/pkg/config/flag"
"go.octolab.org/toolset/maintainer/internal/pkg/http"
"go.octolab.org/toolset/maintainer/internal/pkg/time"
"go.octolab.org/toolset/maintainer/internal/pkg/unsafe"
Expand All @@ -25,7 +24,7 @@ func Contribution(cnf *config.Tool) *cobra.Command {
}

//
// $ maintainer github contribution diff --base=/tmp/snap.01.2013.json --head=/tmp/snap.02.2013.json
// $ maintainer github contribution diff /tmp/snap.01.2013.json /tmp/snap.02.2013.json
//
// Day / Week #46 #48 #49 #50
// ---------------------- --------------- --------------- --------------- -----------
Expand All @@ -39,15 +38,13 @@ func Contribution(cnf *config.Tool) *cobra.Command {
// ---------------------- --------------- --------------- --------------- -----------
// The diff between head{"/tmp/snap.02.2013.json"} → base{"/tmp/snap.01.2013.json"}
//
// $ maintainer github contribution diff --base=/tmp/snap.01.2013.json 2013
// $ maintainer github contribution diff /tmp/snap.01.2013.json 2013
//
diff := cobra.Command{
Use: "diff",
Args: cobra.MaximumNArgs(1),
Args: cobra.ExactArgs(2),
RunE: exec.ContributionDiff(cnf),
}
flag.Adopt(diff.Flags()).File("base", "", "path to a base file")
flag.Adopt(diff.Flags()).File("head", "", "path to a head file")
cmd.AddCommand(&diff)

//
Expand Down
81 changes: 42 additions & 39 deletions internal/command/github/exec/contribution_diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,66 +2,69 @@ package exec

import (
"fmt"
"regexp"

"github.com/spf13/cobra"

"go.octolab.org/toolset/maintainer/internal/command/github/run"
"go.octolab.org/toolset/maintainer/internal/config"
"go.octolab.org/toolset/maintainer/internal/pkg/config/flag"
"go.octolab.org/toolset/maintainer/internal/model/github/contribution"
"go.octolab.org/toolset/maintainer/internal/pkg/http"
"go.octolab.org/toolset/maintainer/internal/pkg/time"
"go.octolab.org/toolset/maintainer/internal/service/github"
)

func ContributionDiff(cnf *config.Tool) Runner {
isYear := regexp.MustCompile(`^\d{4}$`)
wrap := func(err error, input string) error {
return fmt.Errorf(
"please provide the argument in format YYYY, e.g., 2006: %w",
fmt.Errorf("invalid argument %q: %w", input, err),
)
}

// input validation:
// - Args: cobra.ExactArgs(2)
// - base{file|date(year)} head{file|date(year)}
return func(cmd *cobra.Command, args []string) error {
service := github.New(http.TokenSourcedClient(cmd.Context(), cnf.Token))
date := time.TruncateToYear(time.Now().UTC())

// input validation: files{params}, date(year){args}
var baseSource, headSource string
dst, err := flag.Adopt(cmd.Flags()).GetFile("base")
if err != nil {
return err
}
if dst == nil {
return fmt.Errorf("please provide a base file by `--base` parameter")
}
baseSource = dst.Name()

src, err := flag.Adopt(cmd.Flags()).GetFile("head")
if err != nil {
return err
}
if src == nil && len(args) == 0 {
return fmt.Errorf("please provide a compared file by `--head` parameter or year in args")
}
if src != nil && len(args) > 0 {
return fmt.Errorf("please omit `--head` or argument, only one of them is allowed")
}
if len(args) == 1 {
var err error
wrap := func(err error) error {
return fmt.Errorf(
"please provide argument in format YYYY, e.g., 2006: %w",
fmt.Errorf("invalid argument %q: %w", args[0], err),
)
var base run.ContributionSource
if input := args[0]; isYear.MatchString(input) {
year, err := time.Parse(time.RFC3339Year, input)
if err != nil {
return wrap(err, input)
}

switch input := args[0]; len(input) {
case len(time.RFC3339Year):
date, err = time.Parse(time.RFC3339Year, input)
default:
err = fmt.Errorf("unsupported format")
base = &contribution.UpstreamSource{
Provider: service,
Year: year,
}
} else {
base = &contribution.FileSource{
Provider: cnf.FS,
Path: input,
}
}

var head run.ContributionSource
if input := args[1]; isYear.MatchString(input) {
year, err := time.Parse(time.RFC3339Year, input)
if err != nil {
return wrap(err)
return wrap(err, input)
}

head = &contribution.UpstreamSource{
Provider: service,
Year: year,
}
headSource = fmt.Sprintf("upstream:year(%s)", date.Format(time.RFC3339Year))
} else {
headSource = src.Name()
head = &contribution.FileSource{
Provider: cnf.FS,
Path: input,
}
}

return run.ContributionDiff(cmd.Context(), service, cmd, date, dst, src, baseSource, headSource)
return run.ContributionDiff(cmd.Context(), base, head, cmd)
}
}
6 changes: 3 additions & 3 deletions internal/command/github/run/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import (
"context"

"go.octolab.org/toolset/maintainer/internal/model/github/contribution"
"go.octolab.org/toolset/maintainer/internal/pkg/time"
)

type Contributor interface {
ContributionHeatMap(context.Context, time.Range) (contribution.HeatMap, error)
type ContributionSource interface {
Location() string
Fetch(context.Context) (contribution.HeatMap, error)
}

type Printer interface {
Expand Down
34 changes: 8 additions & 26 deletions internal/command/github/run/contribution_diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,24 @@ package run

import (
"context"
"encoding/json"
"os"

"go.octolab.org/toolset/maintainer/internal/command/github/view"
"go.octolab.org/toolset/maintainer/internal/model/github/contribution"
"go.octolab.org/toolset/maintainer/internal/pkg/time"
)

func ContributionDiff(
ctx context.Context,
service Contributor,
src, dst ContributionSource,
printer Printer,

date time.Time,
src, dst *os.File,
baseSource, headSource string,
) error {
var (
base contribution.HeatMap
head contribution.HeatMap
)
if err := json.NewDecoder(dst).Decode(&base); err != nil {
base, err := src.Fetch(ctx)
if err != nil {
return err
}
if src != nil {
if err := json.NewDecoder(src).Decode(&head); err != nil {
return err
}
} else {
var err error
scope := time.RangeByYears(date, 0, false).ExcludeFuture()
head, err = service.ContributionHeatMap(ctx, scope)
if err != nil {
return err
}

head, err := dst.Fetch(ctx)
if err != nil {
return err
}

return view.ContributionDiff(printer, base.Diff(head), baseSource, headSource)
return view.ContributionDiff(printer, base.Diff(head), src.Location(), dst.Location())
}
2 changes: 1 addition & 1 deletion internal/command/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func New() *cobra.Command {

PersistentPreRunE: func(*cobra.Command, []string) error {
// TODO:feature home dir and specific config
return cnf.Load(afero.NewMemMapFs())
return cnf.Load(afero.NewOsFs())
},

SilenceErrors: false,
Expand Down
4 changes: 3 additions & 1 deletion internal/config/tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type GitHub struct {
}

type Tool struct {
FS afero.Fs `mapstructure:"-"`
Git `mapstructure:"git,squash"`
GitHub `mapstructure:"github,squash"`

Expand All @@ -40,8 +41,9 @@ func (cnf *Tool) Bind(bind func(*viper.Viper) error) error {

func (cnf *Tool) Load(fs afero.Fs, bindings ...func(*viper.Viper) error) error {
v := cnf.init().config

v.SetFs(fs)
cnf.FS = fs

for _, bind := range bindings {
if err := bind(v); err != nil {
return err
Expand Down
11 changes: 11 additions & 0 deletions internal/model/github/contribution/contract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package contribution

import (
"context"

"go.octolab.org/toolset/maintainer/internal/pkg/time"
)

type Contributor interface {
ContributionHeatMap(context.Context, time.Range) (HeatMap, error)
}
77 changes: 77 additions & 0 deletions internal/model/github/contribution/source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package contribution

import (
"context"
"encoding/json"
"fmt"
"path/filepath"
"strings"
"time"

"github.com/spf13/afero"
"go.octolab.org/safe"
"go.octolab.org/unsafe"
"gopkg.in/yaml.v2"

xtime "go.octolab.org/toolset/maintainer/internal/pkg/time"
)

type FileSource struct {
Provider afero.Fs
Path string

data HeatMap
}

func (src FileSource) Location() string {
return fmt.Sprintf("file:%s", src.Path)
}

func (src *FileSource) Fetch(_ context.Context) (HeatMap, error) {
if src.data != nil {
return src.data, nil
}

file, err := src.Provider.Open(src.Path)
if err != nil {
return nil, err
}
defer safe.Close(file, unsafe.Ignore)

var data HeatMap
format := strings.ToLower(filepath.Ext(file.Name()))
switch format {
case ".json":
err := json.NewDecoder(file).Decode(&data)
src.data = data
return data, err
case ".yml", ".yaml":
err := yaml.NewDecoder(file).Decode(&data)
src.data = data
return data, err
default:
return nil, fmt.Errorf("unsupported format: %s", format)
}
}

type UpstreamSource struct {
Provider Contributor
Year time.Time

data HeatMap
}

func (src UpstreamSource) Location() string {
return fmt.Sprintf("upstream:year(%s)", src.Year.Format(xtime.RFC3339Year))
}

func (src *UpstreamSource) Fetch(ctx context.Context) (HeatMap, error) {
if src.data != nil {
return src.data, nil
}

var err error
scope := xtime.RangeByYears(src.Year, 0, false).ExcludeFuture()
src.data, err = src.Provider.ContributionHeatMap(ctx, scope)
return src.data, err
}

0 comments on commit bf45f39

Please sign in to comment.