Browse Source

Initial import

v0
maze 4 years ago
parent
commit
00e88a4733
12 changed files with 553 additions and 0 deletions
  1. +163
    -0
      aur/client.go
  2. +32
    -0
      aur/client_test.go
  3. +5
    -0
      aur/doc.go
  4. +29
    -0
      aur/response.go
  5. +5
    -0
      doc.go
  6. +60
    -0
      package.go
  7. +36
    -0
      tarball/builtin.go
  8. +40
    -0
      tarball/builtin_test.go
  9. +55
    -0
      tarball/register.go
  10. +56
    -0
      tarball/tarball.go
  11. +38
    -0
      tarball/xz/xz.go
  12. +34
    -0
      tarball/xz/xz_test.go

+ 163
- 0
aur/client.go View File

@ -0,0 +1,163 @@
package aur
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
archlinux "maze.io/archlinux.v0"
)
// Defaults
const (
DefaultURL = "https://aur.archlinux.org"
DefaultRPC = DefaultURL + "/rpc/"
DefaultVersion = 5
)
// Defaults
var (
DefaultHTTPClient = http.DefaultClient
)
// Client can query the AURJSON RPC interface
type Client struct {
RPC string
Version int
}
// New client with defaults
func New() *Client {
return &Client{
RPC: DefaultRPC,
Version: DefaultVersion,
}
}
func (c *Client) get(typ string, query url.Values) (*Response, error) {
query.Set("v", strconv.Itoa(c.Version))
query.Set("type", typ)
req, err := http.NewRequest("GET", c.RPC+"?"+query.Encode(), nil)
if err != nil {
return nil, err
}
res, err := DefaultHTTPClient.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var (
response = new(Response)
dec = json.NewDecoder(res.Body)
)
if err = dec.Decode(response); err != nil {
return nil, err
}
if response.Type == "error" {
return nil, response
}
return response, nil
}
// Info retrieves information about a single package
func (c *Client) Info(name string) (*Info, error) {
query := make(url.Values)
query.Add("arg[]", name)
i, err := c.infos(query)
if err != nil {
return nil, err
}
return i[0], nil
}
// Infos retrieves information about multiple packages
func (c *Client) Infos(names ...string) ([]*Info, error) {
if len(names) == 0 {
return nil, nil
}
query := make(url.Values)
for _, name := range names {
query.Add("arg[]", name)
}
return c.infos(query)
}
func (c *Client) infos(query url.Values) ([]*Info, error) {
r, err := c.get("info", query)
if err != nil {
return nil, err
}
if r.Type != "multiinfo" {
return nil, fmt.Errorf("aurjson: expected multiinfo response, got %q", r.Type)
}
var results []*Info
if err = json.Unmarshal(r.Results, &results); err != nil {
return nil, err
}
fixup(results)
return results, nil
}
// Search for a package or maintainer
func (c *Client) Search(keywords string, field Field) ([]*Info, error) {
query := make(url.Values)
query.Set("by", string(field))
query.Set("arg", keywords)
r, err := c.get("search", query)
if err != nil {
return nil, err
}
if r.Type != "search" {
return nil, fmt.Errorf("aurjson: expected search response, got %q", r.Type)
}
var results []*Info
if err = json.Unmarshal(r.Results, &results); err != nil {
return nil, err
}
fixup(results)
return results, nil
}
// Field is a searchable field
type Field string
// Field types
const (
Name Field = "name"
NameOrDescription Field = "name-desc"
Maintainer Field = "maintainer"
)
// Info about a package
type Info struct {
archlinux.Package
ID int64
PackageBaseID int64
NumVotes int
Popularity float64
Maintainer string
FirstSubmitted Time
LastModified Time
URLPath string
Keywords []string
}
// fixup release information
func fixup(results []*Info) {
// Rectify releases
for _, r := range results {
if i := strings.IndexByte(r.Version, '-'); i != -1 {
r.Version, r.Release = r.Version[:i], r.Version[i+1:]
}
r.URL, _ = url.Parse(fmt.Sprintf("%s/packages/%s", DefaultURL, url.QueryEscape(r.Name)))
}
}

+ 32
- 0
aur/client_test.go View File

@ -0,0 +1,32 @@
package aur
import "testing"
func TestClient(t *testing.T) {
var (
c = New()
r []*Info
err error
)
if r, err = c.Search("linux", Name); err != nil {
t.Fatal(err)
}
if len(r) == 0 {
t.Fatal(`no results`)
}
t.Log(r)
if r, err = c.Search("maze", Maintainer); err != nil {
t.Fatal(err)
}
if len(r) == 0 {
t.Fatal(`no results`)
}
t.Log(r)
if r, err = c.Infos("pacaur"); err != nil {
t.Fatal(err)
}
t.Logf("%#+v\n", r[0])
}

+ 5
- 0
aur/doc.go View File

@ -0,0 +1,5 @@
/*
Package aur implements the Remote Procedure Call interface of the Arch Linux
User Repository (AUR) packages.
*/
package aur

+ 29
- 0
aur/response.go View File

@ -0,0 +1,29 @@
package aur
import (
"encoding/json"
"time"
)
type Response struct {
Version int `json:"version"`
Type string `json:"type"`
ResultCount int `json:"resultcount"`
Results json.RawMessage `json:"results"`
ErrorMessage string `json:"error"`
}
func (res Response) Error() string {
return res.ErrorMessage
}
type Time time.Time
func (t *Time) UnmarshalJSON(b []byte) error {
var i int64
if err := json.Unmarshal(b, &i); err != nil {
return err
}
*t = Time(time.Unix(i, 0))
return nil
}

+ 5
- 0
doc.go View File

@ -0,0 +1,5 @@
/*
Package archlinux implements utility functions to handle Arch Linux metadata
and package information.
*/
package archlinux // import "maze.io/archlinux.v0"

+ 60
- 0
package.go View File

@ -0,0 +1,60 @@
package archlinux
import (
"io"
"net/url"
"strings"
)
// Package describes an Arch Linux package
type Package struct {
Name string
Base string
Version string
Release string
Epoch int
Description string
Arch []string
URL *url.URL `json:"-"`
License []string
Groups []string
Depends []string
MakeDepends []string
CheckDepends []string
OptionalDepends []string
Provides []string
Conflicts []string
Replaces []string
Backup []string
Options []string
MD5 []byte
SHA1 []byte
SHA256 []byte
SHA384 []byte
SHA512 []byte
PGPSignature []byte
}
func (pkg Package) String() string {
var s = make([]string, 3)
if pkg.Name != "" {
s[0] = pkg.Name
} else {
s[0] = "unknown"
}
if pkg.Version != "" {
s[1] = pkg.Version
} else {
s[1] = "unknown"
}
if pkg.Release != "" {
s[2] = pkg.Release
} else {
s[2] = "unknown"
}
return strings.Join(s, "-")
}
// ReadPackage reads a package description from a package tarball.
func ReadPackage(r io.Reader) (*Package, error) {
}

+ 36
- 0
tarball/builtin.go View File

@ -0,0 +1,36 @@
package tarball
import (
"compress/bzip2"
"compress/gzip"
"compress/zlib"
"io"
)
func init() {
// bzip2
Register("BZh", func(r io.Reader) (io.ReadCloser, error) {
return nullCloser{bzip2.NewReader(r)}, nil
})
// gzip
Register("\x1f\x8b", func(r io.Reader) (io.ReadCloser, error) {
return gzip.NewReader(r)
})
// zlib
Register("\x78\x01", zlib.NewReader)
Register("\x78\x5e", zlib.NewReader)
Register("\x78\x9c", zlib.NewReader)
Register("\x78\xda", zlib.NewReader)
/*
Register("\x1f\x9d", func(r io.Reader) (io.ReadCloser, error) {
var discard [3]byte
if _, err := io.ReadFull(r, discard[:]); err != nil {
return nil, err
}
return lzw.NewReader(r, lzw.MSB, 3), nil
})
*/
}

+ 40
- 0
tarball/builtin_test.go
File diff suppressed because it is too large
View File


+ 55
- 0
tarball/register.go View File

@ -0,0 +1,55 @@
package tarball
import (
"bytes"
"errors"
"io"
"sync"
)
var ErrDuplicateOpener = errors.New("tarball: duplicate opener for magic")
var (
mu sync.Mutex
registry = make(map[string]OpenerFunc)
magicBufSize = 1
)
type OpenerFunc func(io.Reader) (io.ReadCloser, error)
func Register(magic string, fn OpenerFunc) (err error) {
mu.Lock()
defer mu.Unlock()
if _, dupe := registry[magic]; dupe {
return ErrDuplicateOpener
}
registry[magic] = fn
if l := len(magic); l > magicBufSize {
magicBufSize = l
}
return nil
}
type nullCloser struct {
io.Reader
}
func (nc nullCloser) Close() error { return nil }
// Opener opens a tarball (with compression)
func Open(r io.Reader) (io.ReadCloser, error) {
b := make([]byte, magicBufSize)
if _, err := io.ReadFull(r, b); err != nil {
return nil, err
}
n := io.MultiReader(bytes.NewBuffer(b), r)
for magic, fn := range registry {
if l := len(magic); string(b[:l]) == magic {
return fn(n)
}
}
// No magic match, maybe it's a plain tar?
return nullCloser{n}, nil
}

+ 56
- 0
tarball/tarball.go View File

@ -0,0 +1,56 @@
/*
Package tarball implements a reader for (compressed) tape archives (tars)
Builtin decoders
By default, the following decoders are supported using the Go standard library:
.tar plain tape archive
.tar.bz2 bzip2 compressed tape archive
.tar.gz gzip compressed tape archive
.tar.Z deflate compressed tape archive
Additional decoders
Additional decoders may be loaded that do not use the Go standard library,
inspect the subpackages for what imports they are using.
import (
"maze.io/archlinux.v0/tarball"
_ "maze.io/archlinux.v0/tarball/xz" // Load LZMA support
)
...
*/
package tarball
import (
"archive/tar"
"io"
)
// Reader can read from (compressed) tarballs.
type Reader struct {
*tar.Reader
io.Closer
}
// NewReader opens a (compressed) tarball by inspecting the first bytes of the
// file to see if it matches known compression headers. If no matching
// decompressor is found, we attempt to read it as plain vanilla tar.
func NewReader(r io.Reader) (*Reader, error) {
var (
rc io.ReadCloser
err error
)
if rc, err = Open(r); err != nil {
return nil, err
}
return &Reader{
Reader: tar.NewReader(rc),
Closer: rc,
}, nil
}

+ 38
- 0
tarball/xz/xz.go View File

@ -0,0 +1,38 @@
/*
Package xz implements decompressing LZMA compressed files
Usage
This package exports no symbols, but is to be used as extension to the
containg package:
import (
"maze.io/archlinux.v0/tarball"
_ "maze.io/archlinux.v0/tarball/xz" // Load LZMA support
)
*/
package xz
import (
"io"
"github.com/ulikunitz/xz"
"maze.io/archlinux.v0/tarball"
)
type nullCloser struct {
io.Reader
}
func (nc nullCloser) Close() error { return nil }
func init() {
tarball.Register("\xfd7zXZ\x00", func(r io.Reader) (io.ReadCloser, error) {
z, err := xz.NewReader(r)
return nullCloser{z}, err
})
}

+ 34
- 0
tarball/xz/xz_test.go View File

@ -0,0 +1,34 @@
package xz
import (
"bytes"
"encoding/hex"
"testing"
"maze.io/archlinux.v0/tarball"
)
func TestXZ(t *testing.T) {
var tests = map[string][]byte{
"tar.xz": testTarXZ,
}
for name, b := range tests {
t.Run(name, func(t *testing.T) {
r, err := tarball.NewReader(bytes.NewBuffer(b))
if err != nil {
t.Fatal(err)
}
defer r.Close()
h, err := r.Next()
if err != nil {
t.Fatal(err)
}
t.Log(h)
})
}
}
var (
testTarXZ, _ = hex.DecodeString(`fd377a585a000004e6d6b4460200210116000000742fe5a3e027ff00665d003a194ace1fe3c3298a61c3dd84b33f5a8fe14e3c7c1c5ea085fd41e72abbf98b282f1137cba43bbd50d342c8a481292b2e78d435fdba25b61a99bb326cf36ca19358ce852392fe6996747d83cb5135125324c76e598119b232d549668a00a946e4ecea04c3000000005d4e8473c4bcd9dc000182018050000035d0a82cb1c467fb020000000004595a`)
)

Loading…
Cancel
Save