matryer / matryer/anno

On the subject of interfaces

Open
#3 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
71
Forks
7
PR merge metrics
No merged PRs in 30d

Description

Hi Mat,
I was brought here by the Medium article so this is very much a learning question for me.
In trying to grok the article and figure out why you used an interface I refactored anno.go without the interface and it seemed to work but I wonder have I missed something.

Listening to "Go Time" you said that you had overused interfaces early on and I have Googled the subject and found opposing advice: 'use interfaces freely' and 'don't overuse interfaces'.

I've only refactored anno.go and run its test so there may be something in the rest of the code that's pertinent to the interface.

So, I'll stop rambling, is there something in this code that is not idiomatic or will come and bite me in the bum or is it just a matter of style:

package main

// This has bare minimum changes to make work without interface.

import (
	"bytes"
	"fmt"
	"log"
)

// ErrNoMatch is returned when a match was expected but
// can not be found.
// Can be returned from FieldFunc.
type ErrNoMatch []byte

func (e ErrNoMatch) Error() string {
	return "no match for '" + string(e) + "'"
}

// Note represents something interesting within
// text.
type Note struct {
	Val   []byte `json:"val"`
	Start int    `json:"start"`
	Kind  string `json:"kind"`
}

// Notes is a sortable slice of Note objects.
type Notes []*Note

// End calculates the end position of this note.
func (n *Note) End() int {
	return n.Start + len(n.Val)
}

// This satisfies sort.Interface
func (n Notes) Len() int           { return len(n) }
func (n Notes) Swap(i, j int)      { n[i], n[j] = n[j], n[i] }
func (n Notes) Less(i, j int) bool { return n[i].Start < n[j].Start }

// FinderFunc represents a function capable of finding
// notes.
type FinderFunc func(s []byte) (Notes, error)

// FieldFunc returns a FinderFunc that finds notes on a per field basis.
// The fn returns true if it's a match, and optionally a subset of the the
// match.
// The returned []byte must be contained in the source string, otherwise
// ErrNoMatch will be returned.
func FieldFunc(kind string, fn func(b []byte) (bool, []byte)) FinderFunc {
	return func(src []byte) (Notes, error) {
		var pos int
		var notes Notes
		fields := bytes.Fields(src)
		for _, f := range fields {
			if ok, match := fn(f); ok {
				s := bytes.Index(src[pos:], match) + pos
				if s == -1 {
					// true was returned without the returned bytes
					// appearing in the match.
					return nil, ErrNoMatch(match)
				}
				notes = append(notes, &Note{
					Val:   match,
					Start: s,
					Kind:  kind,
				})
			}
			pos += len(f) + 1
		}
		return notes, nil
	}
}

// FindManyString runs all finders against the source and returns a
// slice of notes or an error.
func FindManyString(src string, finders ...FinderFunc) (Notes, error) {
	return FindMany([]byte(src), finders...)
}

// FindMany runs all finders against the source and returns a
// slice of notes or an error.
func FindMany(src []byte, finders ...FinderFunc) (Notes, error) {
	var allNotes Notes
	for _, finder := range finders {
		notes, err := finder(src)
		if err != nil {
			return nil, err
		}
		allNotes = append(allNotes, notes...)
	}
	return allNotes, nil
}

func main() {
	// s := "Find http://www.websites.com/ and  # #hashtags and @mentions and @more mentions easily"
	s := "A simple string with no notes."
	notes, err := FindManyString(s, Emails, URLs, Mentions, Hashtags)
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Println("Source:", s)

	for _, note := range notes {
		fmt.Printf("Found a %s at [%d:%d]: \"%s\"\n", note.Kind, note.Start, note.End(), note.Val)
	}
}



Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with anno.go and the test the reporter says they ran, then inspect the rest of the package for uses of the interface and FinderFunc. Compare behavior with and without the interface; this issue is done when the project-specific rationale and any affected behavior are established.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
backend
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.