~edwargix/git.sr.ht

bc2b21d1455d6484fbfeee2fd347d34d78313427 — Drew DeVault 5 years ago 8dad611
api: simplify structure of text/binary blobs
M api/go.sum => api/go.sum +1 -0
@@ 3,6 3,7 @@ git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3/go.mod h1:wMEGFFFN
git.sr.ht/~sircmpwn/git.sr.ht v0.0.0-20200405134845-b8fbf5bf484f h1:SW8+xV65kcga0rHmQbnZkLG36yp286BcbVOdQTVo1m8=
git.sr.ht/~sircmpwn/git.sr.ht v0.0.0-20200413150414-046cd382d7b7 h1:PYRTIcsHR5W+aPn98OCC73ly528uw5o/4Z3b5Rvc7vA=
git.sr.ht/~sircmpwn/git.sr.ht v0.0.0-20200430231646-4014870a700b h1:xHyh0xixoMog7F1kqOG55daFfK0uso98i0lQ/D4dSM8=
git.sr.ht/~sircmpwn/git.sr.ht v0.0.0-20200511133609-1161d017dc00 h1:uHwXamWDLQX54bOBJcGub96xOX6ApQtQWJ83QmE39OM=
git.sr.ht/~sircmpwn/gqlgen v0.0.0-20200412134447-57d7234737d4 h1:J/Sb88htNHzZaN6ZEF8BnRWj3LzYoTrOL4WRhZEEiQE=
git.sr.ht/~sircmpwn/gqlgen v0.0.0-20200412134447-57d7234737d4/go.mod h1:W1cijL2EqAyL1eo1WAJ3ijNVkZM2okpYyCF5TRu1VfI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=

M api/graph/generated/generated.go => api/graph/generated/generated.go +258 -194
@@ 73,16 73,11 @@ type ComplexityRoot struct {
	}

	BinaryBlob struct {
		Base64 func(childComplexity int) int
	}

	Blob struct {
		BlobType func(childComplexity int) int
		Data     func(childComplexity int) int
		ID       func(childComplexity int) int
		Raw      func(childComplexity int) int
		ShortID  func(childComplexity int) int
		Type     func(childComplexity int) int
		Base64  func(childComplexity int) int
		ID      func(childComplexity int) int
		Raw     func(childComplexity int) int
		ShortID func(childComplexity int) int
		Type    func(childComplexity int) int
	}

	Commit struct {


@@ 170,7 165,11 @@ type ComplexityRoot struct {
	}

	TextBlob struct {
		Text func(childComplexity int) int
		ID      func(childComplexity int) int
		Raw     func(childComplexity int) int
		ShortID func(childComplexity int) int
		Text    func(childComplexity int) int
		Type    func(childComplexity int) int
	}

	Tree struct {


@@ 371,47 370,33 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in

		return e.complexity.BinaryBlob.Base64(childComplexity), true

	case "Blob.blobType":
		if e.complexity.Blob.BlobType == nil {
	case "BinaryBlob.id":
		if e.complexity.BinaryBlob.ID == nil {
			break
		}

		return e.complexity.Blob.BlobType(childComplexity), true
		return e.complexity.BinaryBlob.ID(childComplexity), true

	case "Blob.data":
		if e.complexity.Blob.Data == nil {
	case "BinaryBlob.raw":
		if e.complexity.BinaryBlob.Raw == nil {
			break
		}

		return e.complexity.Blob.Data(childComplexity), true
		return e.complexity.BinaryBlob.Raw(childComplexity), true

	case "Blob.id":
		if e.complexity.Blob.ID == nil {
	case "BinaryBlob.shortId":
		if e.complexity.BinaryBlob.ShortID == nil {
			break
		}

		return e.complexity.Blob.ID(childComplexity), true
		return e.complexity.BinaryBlob.ShortID(childComplexity), true

	case "Blob.raw":
		if e.complexity.Blob.Raw == nil {
	case "BinaryBlob.type":
		if e.complexity.BinaryBlob.Type == nil {
			break
		}

		return e.complexity.Blob.Raw(childComplexity), true

	case "Blob.shortId":
		if e.complexity.Blob.ShortID == nil {
			break
		}

		return e.complexity.Blob.ShortID(childComplexity), true

	case "Blob.type":
		if e.complexity.Blob.Type == nil {
			break
		}

		return e.complexity.Blob.Type(childComplexity), true
		return e.complexity.BinaryBlob.Type(childComplexity), true

	case "Commit.author":
		if e.complexity.Commit.Author == nil {


@@ 907,6 892,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in

		return e.complexity.Tag.Type(childComplexity), true

	case "TextBlob.id":
		if e.complexity.TextBlob.ID == nil {
			break
		}

		return e.complexity.TextBlob.ID(childComplexity), true

	case "TextBlob.raw":
		if e.complexity.TextBlob.Raw == nil {
			break
		}

		return e.complexity.TextBlob.Raw(childComplexity), true

	case "TextBlob.shortId":
		if e.complexity.TextBlob.ShortID == nil {
			break
		}

		return e.complexity.TextBlob.ShortID(childComplexity), true

	case "TextBlob.text":
		if e.complexity.TextBlob.Text == nil {
			break


@@ 914,6 920,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in

		return e.complexity.TextBlob.Text(childComplexity), true

	case "TextBlob.type":
		if e.complexity.TextBlob.Type == nil {
			break
		}

		return e.complexity.TextBlob.Type(childComplexity), true

	case "Tree.entries":
		if e.complexity.Tree.Entries == nil {
			break


@@ 1385,30 1398,28 @@ type TreeEntry {
  mode: Int!
}

enum BlobType {
  BINARY
  TEXT
interface Blob {
  id: String!
}

type BinaryBlob {
  # TODO: Consdier adding a range specifier
  base64: String!
}
type TextBlob implements Object & Blob {
  type: ObjectType!
  id: String!
  shortId: String!
  raw: String!

type TextBlob {
  # TODO: Consdier adding a range specifier
  # TODO: Consider adding a range specifier
  text: String!
}

union BlobData = BinaryBlob | TextBlob

type Blob implements Object {
type BinaryBlob implements Object & Blob {
  type: ObjectType!
  id: String!
  shortId: String!
  raw: String!
  blobType: BlobType!
  data: BlobData

  # TODO: Consider adding a range specifier
  base64: String!
}

type Tag implements Object {


@@ 2417,7 2428,7 @@ func (ec *executionContext) _Artifact_url(ctx context.Context, field graphql.Col
	return ec.marshalNString2string(ctx, field.Selections, res)
}

func (ec *executionContext) _BinaryBlob_base64(ctx context.Context, field graphql.CollectedField, obj *model.BinaryBlob) (ret graphql.Marshaler) {
func (ec *executionContext) _BinaryBlob_type(ctx context.Context, field graphql.CollectedField, obj *model.BinaryBlob) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))


@@ 2434,40 2445,6 @@ func (ec *executionContext) _BinaryBlob_base64(ctx context.Context, field graphq
	ctx = graphql.WithFieldContext(ctx, fc)
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.Base64, nil
	})
	if err != nil {
		ec.Error(ctx, err)
		return graphql.Null
	}
	if resTmp == nil {
		if !graphql.HasFieldError(ctx, fc) {
			ec.Errorf(ctx, "must not be null")
		}
		return graphql.Null
	}
	res := resTmp.(string)
	fc.Result = res
	return ec.marshalNString2string(ctx, field.Selections, res)
}

func (ec *executionContext) _Blob_type(ctx context.Context, field graphql.CollectedField, obj *model.Blob) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))
			ret = graphql.Null
		}
	}()
	fc := &graphql.FieldContext{
		Object:   "Blob",
		Field:    field,
		Args:     nil,
		IsMethod: false,
	}

	ctx = graphql.WithFieldContext(ctx, fc)
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.Type, nil
	})
	if err != nil {


@@ 2485,7 2462,7 @@ func (ec *executionContext) _Blob_type(ctx context.Context, field graphql.Collec
	return ec.marshalNObjectType2gitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐObjectType(ctx, field.Selections, res)
}

func (ec *executionContext) _Blob_id(ctx context.Context, field graphql.CollectedField, obj *model.Blob) (ret graphql.Marshaler) {
func (ec *executionContext) _BinaryBlob_id(ctx context.Context, field graphql.CollectedField, obj *model.BinaryBlob) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))


@@ 2493,7 2470,7 @@ func (ec *executionContext) _Blob_id(ctx context.Context, field graphql.Collecte
		}
	}()
	fc := &graphql.FieldContext{
		Object:   "Blob",
		Object:   "BinaryBlob",
		Field:    field,
		Args:     nil,
		IsMethod: false,


@@ 2519,7 2496,7 @@ func (ec *executionContext) _Blob_id(ctx context.Context, field graphql.Collecte
	return ec.marshalNString2string(ctx, field.Selections, res)
}

func (ec *executionContext) _Blob_shortId(ctx context.Context, field graphql.CollectedField, obj *model.Blob) (ret graphql.Marshaler) {
func (ec *executionContext) _BinaryBlob_shortId(ctx context.Context, field graphql.CollectedField, obj *model.BinaryBlob) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))


@@ 2527,7 2504,7 @@ func (ec *executionContext) _Blob_shortId(ctx context.Context, field graphql.Col
		}
	}()
	fc := &graphql.FieldContext{
		Object:   "Blob",
		Object:   "BinaryBlob",
		Field:    field,
		Args:     nil,
		IsMethod: false,


@@ 2553,7 2530,7 @@ func (ec *executionContext) _Blob_shortId(ctx context.Context, field graphql.Col
	return ec.marshalNString2string(ctx, field.Selections, res)
}

func (ec *executionContext) _Blob_raw(ctx context.Context, field graphql.CollectedField, obj *model.Blob) (ret graphql.Marshaler) {
func (ec *executionContext) _BinaryBlob_raw(ctx context.Context, field graphql.CollectedField, obj *model.BinaryBlob) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))


@@ 2561,7 2538,7 @@ func (ec *executionContext) _Blob_raw(ctx context.Context, field graphql.Collect
		}
	}()
	fc := &graphql.FieldContext{
		Object:   "Blob",
		Object:   "BinaryBlob",
		Field:    field,
		Args:     nil,
		IsMethod: false,


@@ 2587,7 2564,7 @@ func (ec *executionContext) _Blob_raw(ctx context.Context, field graphql.Collect
	return ec.marshalNString2string(ctx, field.Selections, res)
}

func (ec *executionContext) _Blob_blobType(ctx context.Context, field graphql.CollectedField, obj *model.Blob) (ret graphql.Marshaler) {
func (ec *executionContext) _BinaryBlob_base64(ctx context.Context, field graphql.CollectedField, obj *model.BinaryBlob) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))


@@ 2595,16 2572,16 @@ func (ec *executionContext) _Blob_blobType(ctx context.Context, field graphql.Co
		}
	}()
	fc := &graphql.FieldContext{
		Object:   "Blob",
		Object:   "BinaryBlob",
		Field:    field,
		Args:     nil,
		IsMethod: true,
		IsMethod: false,
	}

	ctx = graphql.WithFieldContext(ctx, fc)
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.BlobType(), nil
		return obj.Base64, nil
	})
	if err != nil {
		ec.Error(ctx, err)


@@ 2616,40 2593,9 @@ func (ec *executionContext) _Blob_blobType(ctx context.Context, field graphql.Co
		}
		return graphql.Null
	}
	res := resTmp.(model.BlobType)
	fc.Result = res
	return ec.marshalNBlobType2gitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐBlobType(ctx, field.Selections, res)
}

func (ec *executionContext) _Blob_data(ctx context.Context, field graphql.CollectedField, obj *model.Blob) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))
			ret = graphql.Null
		}
	}()
	fc := &graphql.FieldContext{
		Object:   "Blob",
		Field:    field,
		Args:     nil,
		IsMethod: true,
	}

	ctx = graphql.WithFieldContext(ctx, fc)
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.Data(), nil
	})
	if err != nil {
		ec.Error(ctx, err)
		return graphql.Null
	}
	if resTmp == nil {
		return graphql.Null
	}
	res := resTmp.(model.BlobData)
	res := resTmp.(string)
	fc.Result = res
	return ec.marshalOBlobData2gitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐBlobData(ctx, field.Selections, res)
	return ec.marshalNString2string(ctx, field.Selections, res)
}

func (ec *executionContext) _Commit_type(ctx context.Context, field graphql.CollectedField, obj *model.Commit) (ret graphql.Marshaler) {


@@ 4741,6 4687,142 @@ func (ec *executionContext) _Tag_message(ctx context.Context, field graphql.Coll
	return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
}

func (ec *executionContext) _TextBlob_type(ctx context.Context, field graphql.CollectedField, obj *model.TextBlob) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))
			ret = graphql.Null
		}
	}()
	fc := &graphql.FieldContext{
		Object:   "TextBlob",
		Field:    field,
		Args:     nil,
		IsMethod: false,
	}

	ctx = graphql.WithFieldContext(ctx, fc)
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.Type, nil
	})
	if err != nil {
		ec.Error(ctx, err)
		return graphql.Null
	}
	if resTmp == nil {
		if !graphql.HasFieldError(ctx, fc) {
			ec.Errorf(ctx, "must not be null")
		}
		return graphql.Null
	}
	res := resTmp.(model.ObjectType)
	fc.Result = res
	return ec.marshalNObjectType2gitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐObjectType(ctx, field.Selections, res)
}

func (ec *executionContext) _TextBlob_id(ctx context.Context, field graphql.CollectedField, obj *model.TextBlob) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))
			ret = graphql.Null
		}
	}()
	fc := &graphql.FieldContext{
		Object:   "TextBlob",
		Field:    field,
		Args:     nil,
		IsMethod: false,
	}

	ctx = graphql.WithFieldContext(ctx, fc)
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.ID, nil
	})
	if err != nil {
		ec.Error(ctx, err)
		return graphql.Null
	}
	if resTmp == nil {
		if !graphql.HasFieldError(ctx, fc) {
			ec.Errorf(ctx, "must not be null")
		}
		return graphql.Null
	}
	res := resTmp.(string)
	fc.Result = res
	return ec.marshalNString2string(ctx, field.Selections, res)
}

func (ec *executionContext) _TextBlob_shortId(ctx context.Context, field graphql.CollectedField, obj *model.TextBlob) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))
			ret = graphql.Null
		}
	}()
	fc := &graphql.FieldContext{
		Object:   "TextBlob",
		Field:    field,
		Args:     nil,
		IsMethod: false,
	}

	ctx = graphql.WithFieldContext(ctx, fc)
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.ShortID, nil
	})
	if err != nil {
		ec.Error(ctx, err)
		return graphql.Null
	}
	if resTmp == nil {
		if !graphql.HasFieldError(ctx, fc) {
			ec.Errorf(ctx, "must not be null")
		}
		return graphql.Null
	}
	res := resTmp.(string)
	fc.Result = res
	return ec.marshalNString2string(ctx, field.Selections, res)
}

func (ec *executionContext) _TextBlob_raw(ctx context.Context, field graphql.CollectedField, obj *model.TextBlob) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))
			ret = graphql.Null
		}
	}()
	fc := &graphql.FieldContext{
		Object:   "TextBlob",
		Field:    field,
		Args:     nil,
		IsMethod: false,
	}

	ctx = graphql.WithFieldContext(ctx, fc)
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.Raw, nil
	})
	if err != nil {
		ec.Error(ctx, err)
		return graphql.Null
	}
	if resTmp == nil {
		if !graphql.HasFieldError(ctx, fc) {
			ec.Errorf(ctx, "must not be null")
		}
		return graphql.Null
	}
	res := resTmp.(string)
	fc.Result = res
	return ec.marshalNString2string(ctx, field.Selections, res)
}

func (ec *executionContext) _TextBlob_text(ctx context.Context, field graphql.CollectedField, obj *model.TextBlob) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {


@@ 6717,17 6799,10 @@ func (ec *executionContext) unmarshalInputRepoInput(ctx context.Context, obj int

// region    ************************** interface.gotpl ***************************

func (ec *executionContext) _BlobData(ctx context.Context, sel ast.SelectionSet, obj model.BlobData) graphql.Marshaler {
func (ec *executionContext) _Blob(ctx context.Context, sel ast.SelectionSet, obj model.Blob) graphql.Marshaler {
	switch obj := (obj).(type) {
	case nil:
		return graphql.Null
	case model.BinaryBlob:
		return ec._BinaryBlob(ctx, sel, &obj)
	case *model.BinaryBlob:
		if obj == nil {
			return graphql.Null
		}
		return ec._BinaryBlob(ctx, sel, obj)
	case model.TextBlob:
		return ec._TextBlob(ctx, sel, &obj)
	case *model.TextBlob:


@@ 6735,6 6810,13 @@ func (ec *executionContext) _BlobData(ctx context.Context, sel ast.SelectionSet,
			return graphql.Null
		}
		return ec._TextBlob(ctx, sel, obj)
	case model.BinaryBlob:
		return ec._BinaryBlob(ctx, sel, &obj)
	case *model.BinaryBlob:
		if obj == nil {
			return graphql.Null
		}
		return ec._BinaryBlob(ctx, sel, obj)
	default:
		panic(fmt.Errorf("unexpected type %T", obj))
	}


@@ 6774,13 6856,20 @@ func (ec *executionContext) _Object(ctx context.Context, sel ast.SelectionSet, o
			return graphql.Null
		}
		return ec._Tree(ctx, sel, obj)
	case model.Blob:
		return ec._Blob(ctx, sel, &obj)
	case *model.Blob:
	case model.TextBlob:
		return ec._TextBlob(ctx, sel, &obj)
	case *model.TextBlob:
		if obj == nil {
			return graphql.Null
		}
		return ec._Blob(ctx, sel, obj)
		return ec._TextBlob(ctx, sel, obj)
	case model.BinaryBlob:
		return ec._BinaryBlob(ctx, sel, &obj)
	case *model.BinaryBlob:
		if obj == nil {
			return graphql.Null
		}
		return ec._BinaryBlob(ctx, sel, obj)
	case model.Tag:
		return ec._Tag(ctx, sel, &obj)
	case *model.Tag:


@@ 6945,7 7034,7 @@ func (ec *executionContext) _Artifact(ctx context.Context, sel ast.SelectionSet,
	return out
}

var binaryBlobImplementors = []string{"BinaryBlob", "BlobData"}
var binaryBlobImplementors = []string{"BinaryBlob", "Object", "Blob"}

func (ec *executionContext) _BinaryBlob(ctx context.Context, sel ast.SelectionSet, obj *model.BinaryBlob) graphql.Marshaler {
	fields := graphql.CollectFields(ec.OperationContext, sel, binaryBlobImplementors)


@@ 6956,60 7045,31 @@ func (ec *executionContext) _BinaryBlob(ctx context.Context, sel ast.SelectionSe
		switch field.Name {
		case "__typename":
			out.Values[i] = graphql.MarshalString("BinaryBlob")
		case "base64":
			out.Values[i] = ec._BinaryBlob_base64(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
			}
		default:
			panic("unknown field " + strconv.Quote(field.Name))
		}
	}
	out.Dispatch()
	if invalids > 0 {
		return graphql.Null
	}
	return out
}

var blobImplementors = []string{"Blob", "Object"}

func (ec *executionContext) _Blob(ctx context.Context, sel ast.SelectionSet, obj *model.Blob) graphql.Marshaler {
	fields := graphql.CollectFields(ec.OperationContext, sel, blobImplementors)

	out := graphql.NewFieldSet(fields)
	var invalids uint32
	for i, field := range fields {
		switch field.Name {
		case "__typename":
			out.Values[i] = graphql.MarshalString("Blob")
		case "type":
			out.Values[i] = ec._Blob_type(ctx, field, obj)
			out.Values[i] = ec._BinaryBlob_type(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
			}
		case "id":
			out.Values[i] = ec._Blob_id(ctx, field, obj)
			out.Values[i] = ec._BinaryBlob_id(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
			}
		case "shortId":
			out.Values[i] = ec._Blob_shortId(ctx, field, obj)
			out.Values[i] = ec._BinaryBlob_shortId(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
			}
		case "raw":
			out.Values[i] = ec._Blob_raw(ctx, field, obj)
			out.Values[i] = ec._BinaryBlob_raw(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
			}
		case "blobType":
			out.Values[i] = ec._Blob_blobType(ctx, field, obj)
		case "base64":
			out.Values[i] = ec._BinaryBlob_base64(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
			}
		case "data":
			out.Values[i] = ec._Blob_data(ctx, field, obj)
		default:
			panic("unknown field " + strconv.Quote(field.Name))
		}


@@ 7600,7 7660,7 @@ func (ec *executionContext) _Tag(ctx context.Context, sel ast.SelectionSet, obj 
	return out
}

var textBlobImplementors = []string{"TextBlob", "BlobData"}
var textBlobImplementors = []string{"TextBlob", "Object", "Blob"}

func (ec *executionContext) _TextBlob(ctx context.Context, sel ast.SelectionSet, obj *model.TextBlob) graphql.Marshaler {
	fields := graphql.CollectFields(ec.OperationContext, sel, textBlobImplementors)


@@ 7611,6 7671,26 @@ func (ec *executionContext) _TextBlob(ctx context.Context, sel ast.SelectionSet,
		switch field.Name {
		case "__typename":
			out.Values[i] = graphql.MarshalString("TextBlob")
		case "type":
			out.Values[i] = ec._TextBlob_type(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
			}
		case "id":
			out.Values[i] = ec._TextBlob_id(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
			}
		case "shortId":
			out.Values[i] = ec._TextBlob_shortId(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
			}
		case "raw":
			out.Values[i] = ec._TextBlob_raw(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
			}
		case "text":
			out.Values[i] = ec._TextBlob_text(ctx, field, obj)
			if out.Values[i] == graphql.Null {


@@ 8154,15 8234,6 @@ func (ec *executionContext) marshalNArtifact2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋgit
	return ec._Artifact(ctx, sel, v)
}

func (ec *executionContext) unmarshalNBlobType2gitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐBlobType(ctx context.Context, v interface{}) (model.BlobType, error) {
	var res model.BlobType
	return res, res.UnmarshalGQL(v)
}

func (ec *executionContext) marshalNBlobType2gitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐBlobType(ctx context.Context, sel ast.SelectionSet, v model.BlobType) graphql.Marshaler {
	return v
}

func (ec *executionContext) unmarshalNBoolean2bool(ctx context.Context, v interface{}) (bool, error) {
	return graphql.UnmarshalBoolean(v)
}


@@ 8877,13 8948,6 @@ func (ec *executionContext) marshalOAccessMode2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋg
	return v
}

func (ec *executionContext) marshalOBlobData2gitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐBlobData(ctx context.Context, sel ast.SelectionSet, v model.BlobData) graphql.Marshaler {
	if v == nil {
		return graphql.Null
	}
	return ec._BlobData(ctx, sel, v)
}

func (ec *executionContext) unmarshalOBoolean2bool(ctx context.Context, v interface{}) (bool, error) {
	return graphql.UnmarshalBoolean(v)
}

M api/graph/model/blob.go => api/graph/model/blob.go +40 -37
@@ 1,7 1,7 @@
package model

import (
	"errors"
	"encoding/base64"
	"io/ioutil"
	"unicode/utf8"



@@ 9,67 9,70 @@ import (
	"github.com/go-git/go-git/v5/plumbing/object"
)

type Blob struct {
type BinaryBlob struct {
	Type    ObjectType `json:"type"`
	ID      string     `json:"id"`
	ShortID string     `json:"shortId"`
	Raw     string     `json:"raw"`

	Base64 string `json:"base64"`

	blob  *object.Blob
	repo  *git.Repository
	bytes []byte
}

func (Blob) IsObject() {}

type BlobData interface {
	IsBlobData()
}

type BinaryBlob struct {
	Base64 string `json:"base64"`
}

func (BinaryBlob) IsBlobData() {}
func (BinaryBlob) IsObject() {}
func (BinaryBlob) IsBlob() {}

type TextBlob struct {
	Type    ObjectType `json:"type"`
	ID      string     `json:"id"`
	ShortID string     `json:"shortId"`
	Raw     string     `json:"raw"`

	Text string `json:"text"`

	blob  *object.Blob
	repo  *git.Repository
}

func (TextBlob) IsBlobData() {}
func (TextBlob) IsObject() {}
func (TextBlob) IsBlob() {}

func (blob *Blob) Bytes() []byte {
	if blob.bytes != nil {
		return blob.bytes
	}
	reader, err := blob.blob.Reader()
func BlobFromObject(repo *git.Repository, obj *object.Blob) Object {
	reader, err := obj.Reader()
	if err != nil {
		panic(err)
	}
	defer reader.Close()

	// XXX: Probably a bad idea
	blob.bytes, err = ioutil.ReadAll(reader)
	bytes, err := ioutil.ReadAll(reader)
	if err != nil {
		panic(err)
	}
	return blob.bytes
}

func (blob *Blob) BlobType() BlobType {
	if utf8.ValidString(string(blob.Bytes())) {
		return BlobTypeText
	text := string(bytes)
	if utf8.ValidString(text) {
		return &TextBlob{
			Type:    ObjectTypeBlob,
			ID:      obj.ID().String(),
			ShortID: obj.ID().String()[:7],
			Text:    text,

			blob: obj,
			repo: repo,
		}
	} else {
		return BlobTypeBinary
	}
}
		b64 := base64.StdEncoding.EncodeToString(bytes)
		return &BinaryBlob{
			Type:    ObjectTypeBlob,
			ID:      obj.ID().String(),
			ShortID: obj.ID().String()[:7],
			Base64:  b64,

func (blob *Blob) Data() BlobData {
	switch blob.BlobType() {
	case BlobTypeBinary:
		panic(errors.New("Unimplemented"))
	case BlobTypeText:
		return &TextBlob{Text: string(blob.Bytes())}
	default:
		panic(errors.New("Unknown blob type"))
			blob: obj,
			repo: repo,
		}
	}
}

M api/graph/model/models_gen.go => api/graph/model/models_gen.go +4 -41
@@ 9,6 9,10 @@ import (
	"time"
)

type Blob interface {
	IsBlob()
}

type Entity interface {
	IsEntity()
}


@@ 117,47 121,6 @@ func (e AccessMode) MarshalGQL(w io.Writer) {
	fmt.Fprint(w, strconv.Quote(e.String()))
}

type BlobType string

const (
	BlobTypeBinary BlobType = "BINARY"
	BlobTypeText   BlobType = "TEXT"
)

var AllBlobType = []BlobType{
	BlobTypeBinary,
	BlobTypeText,
}

func (e BlobType) IsValid() bool {
	switch e {
	case BlobTypeBinary, BlobTypeText:
		return true
	}
	return false
}

func (e BlobType) String() string {
	return string(e)
}

func (e *BlobType) UnmarshalGQL(v interface{}) error {
	str, ok := v.(string)
	if !ok {
		return fmt.Errorf("enums must be strings")
	}

	*e = BlobType(str)
	if !e.IsValid() {
		return fmt.Errorf("%s is not a valid BlobType", str)
	}
	return nil
}

func (e BlobType) MarshalGQL(w io.Writer) {
	fmt.Fprint(w, strconv.Quote(e.String()))
}

type ObjectType string

const (

M api/graph/model/object.go => api/graph/model/object.go +1 -8
@@ 32,14 32,7 @@ func LookupObject(repo *git.Repository, hash plumbing.Hash) (Object, error) {
	case *object.Tree:
		return TreeFromObject(repo, obj), nil
	case *object.Blob:
		return &Blob{
			Type:    ObjectTypeBlob,
			ID:      obj.ID().String(),
			ShortID: obj.ID().String()[:7],

			blob: obj,
			repo: repo,
		}, nil
		return BlobFromObject(repo, obj), nil
	default:
		return nil, errors.New("Unknown object type")
	}

M api/graph/model/tree.go => api/graph/model/tree.go +1 -1
@@ 53,7 53,7 @@ func (tree *Tree) Entry(path string) *TreeEntry {
	}
}

func (tree *Tree) Entries() []*TreeEntry {
func (tree *Tree) GetEntries() []*TreeEntry {
	entries := tree.tree.Entries[:]
	sort.SliceStable(entries, func(a, b int) bool {
		return entries[a].Name < entries[b].Name

M api/graph/schema.graphqls => api/graph/schema.graphqls +12 -14
@@ 222,30 222,28 @@ type TreeEntry {
  mode: Int!
}

enum BlobType {
  BINARY
  TEXT
interface Blob {
  id: String!
}

type BinaryBlob {
  # TODO: Consdier adding a range specifier
  base64: String!
}
type TextBlob implements Object & Blob {
  type: ObjectType!
  id: String!
  shortId: String!
  raw: String!

type TextBlob {
  # TODO: Consdier adding a range specifier
  # TODO: Consider adding a range specifier
  text: String!
}

union BlobData = BinaryBlob | TextBlob

type Blob implements Object {
type BinaryBlob implements Object & Blob {
  type: ObjectType!
  id: String!
  shortId: String!
  raw: String!
  blobType: BlobType!
  data: BlobData

  # TODO: Consider adding a range specifier
  base64: String!
}

type Tag implements Object {

M api/graph/schema.resolvers.go => api/graph/schema.resolvers.go +1 -1
@@ 251,7 251,7 @@ func (r *treeResolver) Entries(ctx context.Context, obj *model.Tree, cursor *mod
	}

	// TODO: Implement cursor properly
	entries := obj.Entries()
	entries := obj.GetEntries()
	if len(entries) > cursor.Count {
		entries = entries[:cursor.Count]
	}