github.com/abemedia/appcast@v0.4.0/source/source.go (about)

     1  package source
     2  
     3  import (
     4  	"context"
     5  	"log"
     6  	"sort"
     7  	"time"
     8  
     9  	"github.com/abemedia/appcast/pkg/slices"
    10  	"github.com/abemedia/appcast/pkg/version"
    11  	"golang.org/x/mod/semver"
    12  )
    13  
    14  type Release struct {
    15  	Name        string
    16  	Description string
    17  	Date        time.Time
    18  	Version     string
    19  	Prerelease  bool
    20  	Assets      []*Asset
    21  }
    22  
    23  type Asset struct {
    24  	Name string
    25  	URL  string
    26  	Size int
    27  }
    28  
    29  type Driver interface {
    30  	GetRelease(ctx context.Context, version string) (*Release, error)
    31  	ListReleases(ctx context.Context) ([]*Release, error)
    32  	DownloadAsset(ctx context.Context, version, name string) ([]byte, error)
    33  	UploadAsset(ctx context.Context, version, name string, data []byte) error
    34  }
    35  
    36  type Source struct {
    37  	s Driver
    38  }
    39  
    40  func New(driver Driver) *Source {
    41  	return &Source{s: driver}
    42  }
    43  
    44  type ListOptions struct {
    45  	// Version constraint e.g. 'v1.2.4', 'v1', '>= v1.1.0, < v2.1'
    46  	Version string
    47  
    48  	// Include pre-releases
    49  	Prerelease bool
    50  }
    51  
    52  func (s *Source) ListReleases(ctx context.Context, opt *ListOptions) ([]*Release, error) {
    53  	if s == nil || s.s == nil {
    54  		return nil, ErrMissingSource
    55  	}
    56  
    57  	var constraint version.Constraint
    58  	if opt != nil && opt.Version != "" && opt.Version != "latest" {
    59  		c, err := version.NewConstraint(opt.Version)
    60  		if err != nil {
    61  			return nil, err
    62  		}
    63  		constraint = c
    64  	}
    65  
    66  	releases, err := s.s.ListReleases(ctx)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	releases = slices.Filter(releases, func(r *Release) bool {
    72  		if !semver.IsValid(r.Version) {
    73  			log.Println("Skipping invalid version:", r.Version)
    74  			return false
    75  		}
    76  
    77  		if !constraint.Check(r.Version) {
    78  			log.Println("Skipping excluded version:", r.Version)
    79  			return false
    80  		}
    81  
    82  		if (opt == nil || !opt.Prerelease) && semver.Prerelease(r.Version) != "" {
    83  			log.Println("Skipping prerelease:", r.Version)
    84  			return false
    85  		}
    86  
    87  		processRelease(r)
    88  		return true
    89  	})
    90  
    91  	if len(releases) == 0 {
    92  		return nil, ErrNoReleaseFound
    93  	}
    94  
    95  	sort.Sort(ByVersion(releases))
    96  
    97  	if opt != nil && opt.Version == "latest" {
    98  		return releases[:1], nil
    99  	}
   100  
   101  	return releases, nil
   102  }
   103  
   104  func (s *Source) GetRelease(ctx context.Context, version string) (*Release, error) {
   105  	if s == nil || s.s == nil {
   106  		return nil, ErrMissingSource
   107  	}
   108  
   109  	r, err := s.s.GetRelease(ctx, version)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  
   114  	processRelease(r)
   115  
   116  	return r, nil
   117  }
   118  
   119  func (s *Source) DownloadAsset(ctx context.Context, version, name string) ([]byte, error) {
   120  	if s == nil || s.s == nil {
   121  		return nil, ErrMissingSource
   122  	}
   123  
   124  	return s.s.DownloadAsset(ctx, version, name)
   125  }
   126  
   127  func (s *Source) UploadAsset(ctx context.Context, version, name string, data []byte) error {
   128  	if s == nil || s.s == nil {
   129  		return ErrMissingSource
   130  	}
   131  
   132  	return s.s.UploadAsset(ctx, version, name, data)
   133  }
   134  
   135  func processRelease(r *Release) {
   136  	if r.Name == "" {
   137  		r.Name = r.Version
   138  	}
   139  	if !r.Prerelease {
   140  		r.Prerelease = semver.Prerelease(r.Version) != ""
   141  	}
   142  }