<commit_before>package sdl

/*
#include "sdl_wrapper.h"
static Sint64 RWsize(SDL_RWops *ctx)
{
	return ctx->size(ctx);
}

static Sint64 RWseek(SDL_RWops *ctx, Sint64 offset, int whence)
{
	return ctx->seek(ctx, offset, whence);
}

static size_t RWread(SDL_RWops *ctx, void *ptr, size_t size, size_t maxnum)
{
	return ctx->read(ctx, ptr, size, maxnum);
}

static size_t RWwrite(SDL_RWops *ctx, void *ptr, size_t size, size_t num)
{
	return ctx->write(ctx, ptr, size, num);
}

static int RWclose(SDL_RWops *ctx)
{
	return ctx->close(ctx);
}

#if !(SDL_VERSION_ATLEAST(2,0,6))
#pragma message("SDL_LoadFile_RW is not supported before SDL 2.0.6")
static void * SDL_LoadFile_RW(SDL_RWops * src, size_t *datasize, int freesrc)
{
	return 0;
}
#endif
*/
import "C"
import (
	"reflect"
	"unsafe"
)

// RWops types
const (
	RWOPS_UNKNOWN   = 0 // unknown stream type
	RWOPS_WINFILE   = 1 // win32 file
	RWOPS_STDFILE   = 2 // stdio file
	RWOPS_JNIFILE   = 3 // android asset
	RWOPS_MEMORY    = 4 // memory stream
	RWOPS_MEMORY_RO = 5 // read-only memory stream
)

// RWops seek from
const (
	RW_SEEK_SET = C.RW_SEEK_SET // seek from the beginning of data
	RW_SEEK_CUR = C.RW_SEEK_CUR // seek relative to current read point
	RW_SEEK_END = C.RW_SEEK_END // seek relative to the end of data
)

// RWops provides an abstract interface to stream I/O. Applications can generally ignore the specifics of this structure's internals and treat them as opaque pointers. The details are important to lower-level code that might need to implement one of these, however.
// (https://wiki.libsdl.org/SDL_RWops)
type RWops C.SDL_RWops

func (rwops *RWops) cptr() *C.SDL_RWops {
	return (*C.SDL_RWops)(rwops)
}

// RWFromFile creates a new RWops structure for reading from and/or writing to a named file.
// (https://wiki.libsdl.org/SDL_RWFromFile)
func RWFromFile(file, mode string) *RWops {
	_file := C.CString(file)
	_mode := C.CString(mode)
	defer C.free(unsafe.Pointer(_file))
	defer C.free(unsafe.Pointer(_mode))
	return (*RWops)(unsafe.Pointer(C.SDL_RWFromFile(_file, _mode)))
}

// RWFromMem prepares a read-write memory buffer for use with RWops.
// (https://wiki.libsdl.org/SDL_RWFromMem)
func RWFromMem(mem []byte) (*RWops, error) {
	if mem == nil {
		return nil, ErrInvalidParameters
	}

	header := (*reflect.SliceHeader)(unsafe.Pointer(&mem))
	_mem := unsafe.Pointer(header.Data)

	rwops := (*RWops)(unsafe.Pointer(C.SDL_RWFromMem(_mem, C.int(len(mem)))))
	if rwops == nil {
		return nil, GetError()
	}
	return rwops, nil
}

// AllocRW allocates an empty, unpopulated RWops structure.
// (https://wiki.libsdl.org/SDL_AllocRW)
func AllocRW() *RWops {
	return (*RWops)(unsafe.Pointer(C.SDL_AllocRW()))
}

// Free frees the RWops structure allocated by AllocRW().
// (https://wiki.libsdl.org/SDL_FreeRW)
func (rwops *RWops) Free() error {
	if rwops == nil {
		return ErrInvalidParameters
	}

	C.SDL_FreeRW(rwops.cptr())
	return nil
}

// Size returns the size of the data stream in the RWops.
// (https://wiki.libsdl.org/SDL_RWsize)
func (rwops *RWops) Size() (int64, error) {
	n := int64(C.RWsize(rwops.cptr()))
	if n < 0 {
		return n, GetError()
	}
	return n, nil
}

// Seek seeks within the RWops data stream.
// (https://wiki.libsdl.org/SDL_RWseek)
func (rwops *RWops) Seek(offset int64, whence int) (int64, error) {
	if rwops == nil {
		return -1, ErrInvalidParameters
	}

	ret := int64(C.RWseek(rwops.cptr(), C.Sint64(offset), C.int(whence)))
	if ret < 0 {
		return ret, GetError()
	}
	return ret, nil
}

// Read reads from a data source.
// (https://wiki.libsdl.org/SDL_RWread)
func (rwops *RWops) Read(buf []byte) (n int, err error) {
	return rwops.Read2(buf, 1, uint(len(buf)))
}

// Read2 reads from a data source (native).
// (https://wiki.libsdl.org/SDL_RWread)
func (rwops *RWops) Read2(buf []byte, size, maxnum uint) (n int, err error) {
	if rwops == nil || buf == nil {
		return 0, ErrInvalidParameters
	}

	header := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
	_data := unsafe.Pointer(header.Data)

	n = int(C.RWread(rwops.cptr(), _data, C.size_t(size), C.size_t(maxnum)))
	if n == 0 {
		err = GetError()
	}
	return
}

// Tell returns the current read/write offset in the RWops data stream.
// (https://wiki.libsdl.org/SDL_RWtell)
func (rwops *RWops) Tell() (int64, error) {
	if rwops == nil {
		return 0, ErrInvalidParameters
	}

	ret := int64(C.RWseek(rwops.cptr(), 0, RW_SEEK_CUR))
	if ret < 0 {
		return ret, GetError()
	}
	return ret, nil
}

// Write writes to the RWops data stream.
// (https://wiki.libsdl.org/SDL_RWwrite)
func (rwops *RWops) Write(buf []byte) (n int, err error) {
	return rwops.Write2(buf, 1, uint(len(buf)))
}

// Write2 writes to the RWops data stream (native).
// (https://wiki.libsdl.org/SDL_RWwrite)
func (rwops *RWops) Write2(buf []byte, size, num uint) (n int, err error) {
	if rwops == nil || buf == nil {
		return 0, ErrInvalidParameters
	}

	header := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
	_data := unsafe.Pointer(header.Data)

	n = int(C.RWwrite(rwops.cptr(), _data, C.size_t(size), C.size_t(num)))
	if n < int(num) {
		err = GetError()
	}
	return
}

// Close closes and frees the allocated RWops structure.
// (https://wiki.libsdl.org/SDL_RWclose)
func (rwops *RWops) Close() error {
	if rwops != nil && C.RWclose(rwops.cptr()) != 0 {
		return GetError()
	}
	return nil
}

// ReadU8 reads a byte from the RWops.
// (https://wiki.libsdl.org/SDL_ReadU8)
func (rwops *RWops) ReadU8() uint8 {
	if rwops == nil {
		return 0
	}
	return uint8(C.SDL_ReadU8(rwops.cptr()))
}

// ReadLE16 reads 16 bits of little-endian data from the RWops and returns in native format.
// (https://wiki.libsdl.org/SDL_ReadLE16)
func (rwops *RWops) ReadLE16() uint16 {
	if rwops == nil {
		return 0
	}
	return uint16(C.SDL_ReadLE16(rwops.cptr()))
}

// ReadBE16 read 16 bits of big-endian data from the RWops and returns in native format.
// (https://wiki.libsdl.org/SDL_ReadBE16)
func (rwops *RWops) ReadBE16() uint16 {
	if rwops == nil {
		return 0
	}
	return uint16(C.SDL_ReadBE16(rwops.cptr()))
}

// ReadLE32 reads 32 bits of little-endian data from the RWops and returns in native format.
// (https://wiki.libsdl.org/SDL_ReadLE32)
func (rwops *RWops) ReadLE32() uint32 {
	if rwops == nil {
		return 0
	}
	return uint32(C.SDL_ReadLE32(rwops.cptr()))
}

// ReadBE32 reads 32 bits of big-endian data from the RWops and returns in native format.
// (https://wiki.libsdl.org/SDL_ReadBE32)
func (rwops *RWops) ReadBE32() uint32 {
	if rwops == nil {
		return 0
	}
	return uint32(C.SDL_ReadBE32(rwops.cptr()))
}

// ReadLE64 reads 64 bits of little-endian data from the RWops and returns in native format.
// (https://wiki.libsdl.org/SDL_ReadLE64)
func (rwops *RWops) ReadLE64() uint64 {
	if rwops == nil {
		return 0
	}
	return uint64(C.SDL_ReadLE64(rwops.cptr()))
}

// ReadBE64 reads 64 bits of big-endian data from the RWops and returns in native format.
// (https://wiki.libsdl.org/SDL_ReadBE64)
func (rwops *RWops) ReadBE64() uint64 {
	if rwops == nil {
		return 0
	}
	return uint64(C.SDL_ReadBE64(rwops.cptr()))
}

// LoadFile_RW loads all the data from an SDL data stream.
// (https://wiki.libsdl.org/SDL_LoadFile_RW)
func (src *RWops) LoadFileRW(freesrc bool) (data []byte, size int) {
	var _size C.size_t
	var _freesrc C.int = 0

	if freesrc {
		_freesrc = 1
	}

	_data := C.SDL_LoadFile_RW(src.cptr(), &_size, _freesrc)
	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&data))
	sliceHeader.Cap = int(_size)
	sliceHeader.Len = int(_size)
	sliceHeader.Data = uintptr(_data)
	size = int(_size)
	return
}

// LoadFile loads an entire file
// (https://wiki.libsdl.org/SDL_LoadFile)
func LoadFile(file string) (data []byte, size int) {
	return RWFromFile(file, "rb").LoadFileRW(true)
}

// WriteU8 writes a byte to the RWops.
// (https://wiki.libsdl.org/SDL_WriteU8)
func (rwops *RWops) WriteU8(value uint8) uint {
	if rwops == nil {
		return 0
	}
	return uint(C.SDL_WriteU8(rwops.cptr(), C.Uint8(value)))
}

// WriteLE16 writes 16 bits in native format to the RWops as little-endian data.
// (https://wiki.libsdl.org/SDL_WriteLE16)
func (rwops *RWops) WriteLE16(value uint16) uint {
	if rwops == nil {
		return 0
	}
	return uint(C.SDL_WriteLE16(rwops.cptr(), C.Uint16(value)))
}

// WriteBE16 writes 16 bits in native format to the RWops as big-endian data.
// (https://wiki.libsdl.org/SDL_WriteBE16)
func (rwops *RWops) WriteBE16(value uint16) uint {
	if rwops == nil {
		return 0
	}
	return uint(C.SDL_WriteBE16(rwops.cptr(), C.Uint16(value)))
}

// WriteLE32 writes 32 bits in native format to the RWops as little-endian data.
// (https://wiki.libsdl.org/SDL_WriteLE32)
func (rwops *RWops) WriteLE32(value uint32) uint {
	if rwops == nil {
		return 0
	}
	return uint(C.SDL_WriteLE32(rwops.cptr(), C.Uint32(value)))
}

// WriteBE32 writes 32 bits in native format to the RWops as big-endian data.
// (https://wiki.libsdl.org/SDL_WriteBE32)
func (rwops *RWops) WriteBE32(value uint32) uint {
	if rwops == nil {
		return 0
	}
	return uint(C.SDL_WriteBE32(rwops.cptr(), C.Uint32(value)))
}

// WriteLE64 writes 64 bits in native format to the RWops as little-endian data.
// (https://wiki.libsdl.org/SDL_WriteLE64)
func (rwops *RWops) WriteLE64(value uint64) uint {
	if rwops == nil {
		return 0
	}
	return uint(C.SDL_WriteLE64(rwops.cptr(), C.Uint64(value)))
}

// WriteBE64 writes 64 bits in native format to the RWops as big-endian data.
// (https://wiki.libsdl.org/SDL_WriteBE64)
func (rwops *RWops) WriteBE64(value uint64) uint {
	if rwops == nil {
		return 0
	}
	return uint(C.SDL_WriteBE64(rwops.cptr(), C.Uint64(value)))
}
<commit_msg>sdl/rwops: Fix Travis CI build for go-tip<commit_after>package sdl

/*
#include "sdl_wrapper.h"
static Sint64 RWsize(SDL_RWops *ctx)
{
	return ctx->size(ctx);
}

static Sint64 RWseek(SDL_RWops *ctx, Sint64 offset, int whence)
{
	return ctx->seek(ctx, offset, whence);
}

static size_t RWread(SDL_RWops *ctx, void *ptr, size_t size, size_t maxnum)
{
	return ctx->read(ctx, ptr, size, maxnum);
}

static size_t RWwrite(SDL_RWops *ctx, void *ptr, size_t size, size_t num)
{
	return ctx->write(ctx, ptr, size, num);
}

static int RWclose(SDL_RWops *ctx)
{
	return ctx->close(ctx);
}

#if !(SDL_VERSION_ATLEAST(2,0,6))
#pragma message("SDL_LoadFile_RW is not supported before SDL 2.0.6")
static void * SDL_LoadFile_RW(SDL_RWops * src, size_t *datasize, int freesrc)
{
	return 0;
}
#endif
*/
import "C"
import (
	"reflect"
	"unsafe"
)

// RWops types
const (
	RWOPS_UNKNOWN   = 0 // unknown stream type
	RWOPS_WINFILE   = 1 // win32 file
	RWOPS_STDFILE   = 2 // stdio file
	RWOPS_JNIFILE   = 3 // android asset
	RWOPS_MEMORY    = 4 // memory stream
	RWOPS_MEMORY_RO = 5 // read-only memory stream
)

// RWops seek from
const (
	RW_SEEK_SET = C.RW_SEEK_SET // seek from the beginning of data
	RW_SEEK_CUR = C.RW_SEEK_CUR // seek relative to current read point
	RW_SEEK_END = C.RW_SEEK_END // seek relative to the end of data
)

// RWops provides an abstract interface to stream I/O. Applications can generally ignore the specifics of this structure's internals and treat them as opaque pointers. The details are important to lower-level code that might need to implement one of these, however.
// (https://wiki.libsdl.org/SDL_RWops)
type RWops C.SDL_RWops

func (rwops *RWops) cptr() *C.SDL_RWops {
	return (*C.SDL_RWops)(rwops)
}

// RWFromFile creates a new RWops structure for reading from and/or writing to a named file.
// (https://wiki.libsdl.org/SDL_RWFromFile)
func RWFromFile(file, mode string) *RWops {
	_file := C.CString(file)
	_mode := C.CString(mode)
	defer C.free(unsafe.Pointer(_file))
	defer C.free(unsafe.Pointer(_mode))
	return (*RWops)(unsafe.Pointer(C.SDL_RWFromFile(_file, _mode)))
}

// RWFromMem prepares a read-write memory buffer for use with RWops.
// (https://wiki.libsdl.org/SDL_RWFromMem)
func RWFromMem(mem []byte) (*RWops, error) {
	if mem == nil {
		return nil, ErrInvalidParameters
	}

	header := (*reflect.SliceHeader)(unsafe.Pointer(&mem))
	_mem := unsafe.Pointer(header.Data)

	rwops := (*RWops)(unsafe.Pointer(C.SDL_RWFromMem(_mem, C.int(len(mem)))))
	if rwops == nil {
		return nil, GetError()
	}
	return rwops, nil
}

// AllocRW allocates an empty, unpopulated RWops structure.
// (https://wiki.libsdl.org/SDL_AllocRW)
func AllocRW() *RWops {
	return (*RWops)(unsafe.Pointer(C.SDL_AllocRW()))
}

// Free frees the RWops structure allocated by AllocRW().
// (https://wiki.libsdl.org/SDL_FreeRW)
func (rwops *RWops) Free() error {
	if rwops == nil {
		return ErrInvalidParameters
	}

	C.SDL_FreeRW(rwops.cptr())
	return nil
}

// Size returns the size of the data stream in the RWops.
// (https://wiki.libsdl.org/SDL_RWsize)
func (rwops *RWops) Size() (int64, error) {
	n := int64(C.RWsize(rwops.cptr()))
	if n < 0 {
		return n, GetError()
	}
	return n, nil
}

// Seek seeks within the RWops data stream.
// (https://wiki.libsdl.org/SDL_RWseek)
func (rwops *RWops) Seek(offset int64, whence int) (int64, error) {
	if rwops == nil {
		return -1, ErrInvalidParameters
	}

	ret := int64(C.RWseek(rwops.cptr(), C.Sint64(offset), C.int(whence)))
	if ret < 0 {
		return ret, GetError()
	}
	return ret, nil
}

// Read reads from a data source.
// (https://wiki.libsdl.org/SDL_RWread)
func (rwops *RWops) Read(buf []byte) (n int, err error) {
	return rwops.Read2(buf, 1, uint(len(buf)))
}

// Read2 reads from a data source (native).
// (https://wiki.libsdl.org/SDL_RWread)
func (rwops *RWops) Read2(buf []byte, size, maxnum uint) (n int, err error) {
	if rwops == nil || buf == nil {
		return 0, ErrInvalidParameters
	}

	header := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
	_data := unsafe.Pointer(header.Data)

	n = int(C.RWread(rwops.cptr(), _data, C.size_t(size), C.size_t(maxnum)))
	if n == 0 {
		err = GetError()
	}
	return
}

// Tell returns the current read/write offset in the RWops data stream.
// (https://wiki.libsdl.org/SDL_RWtell)
func (rwops *RWops) Tell() (int64, error) {
	if rwops == nil {
		return 0, ErrInvalidParameters
	}

	ret := int64(C.RWseek(rwops.cptr(), 0, C.int(RW_SEEK_CUR)))
	if ret < 0 {
		return ret, GetError()
	}
	return ret, nil
}

// Write writes to the RWops data stream.
// (https://wiki.libsdl.org/SDL_RWwrite)
func (rwops *RWops) Write(buf []byte) (n int, err error) {
	return rwops.Write2(buf, 1, uint(len(buf)))
}

// Write2 writes to the RWops data stream (native).
// (https://wiki.libsdl.org/SDL_RWwrite)
func (rwops *RWops) Write2(buf []byte, size, num uint) (n int, err error) {
	if rwops == nil || buf == nil {
		return 0, ErrInvalidParameters
	}

	header := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
	_data := unsafe.Pointer(header.Data)

	n = int(C.RWwrite(rwops.cptr(), _data, C.size_t(size), C.size_t(num)))
	if n < int(num) {
		err = GetError()
	}
	return
}

// Close closes and frees the allocated RWops structure.
// (https://wiki.libsdl.org/SDL_RWclose)
func (rwops *RWops) Close() error {
	if rwops != nil && C.RWclose(rwops.cptr()) != 0 {
		return GetError()
	}
	return nil
}

// ReadU8 reads a byte from the RWops.
// (https://wiki.libsdl.org/SDL_ReadU8)
func (rwops *RWops) ReadU8() uint8 {
	if rwops == nil {
		return 0
	}
	return uint8(C.SDL_ReadU8(rwops.cptr()))
}

// ReadLE16 reads 16 bits of little-endian data from the RWops and returns in native format.
// (https://wiki.libsdl.org/SDL_ReadLE16)
func (rwops *RWops) ReadLE16() uint16 {
	if rwops == nil {
		return 0
	}
	return uint16(C.SDL_ReadLE16(rwops.cptr()))
}

// ReadBE16 read 16 bits of big-endian data from the RWops and returns in native format.
// (https://wiki.libsdl.org/SDL_ReadBE16)
func (rwops *RWops) ReadBE16() uint16 {
	if rwops == nil {
		return 0
	}
	return uint16(C.SDL_ReadBE16(rwops.cptr()))
}

// ReadLE32 reads 32 bits of little-endian data from the RWops and returns in native format.
// (https://wiki.libsdl.org/SDL_ReadLE32)
func (rwops *RWops) ReadLE32() uint32 {
	if rwops == nil {
		return 0
	}
	return uint32(C.SDL_ReadLE32(rwops.cptr()))
}

// ReadBE32 reads 32 bits of big-endian data from the RWops and returns in native format.
// (https://wiki.libsdl.org/SDL_ReadBE32)
func (rwops *RWops) ReadBE32() uint32 {
	if rwops == nil {
		return 0
	}
	return uint32(C.SDL_ReadBE32(rwops.cptr()))
}

// ReadLE64 reads 64 bits of little-endian data from the RWops and returns in native format.
// (https://wiki.libsdl.org/SDL_ReadLE64)
func (rwops *RWops) ReadLE64() uint64 {
	if rwops == nil {
		return 0
	}
	return uint64(C.SDL_ReadLE64(rwops.cptr()))
}

// ReadBE64 reads 64 bits of big-endian data from the RWops and returns in native format.
// (https://wiki.libsdl.org/SDL_ReadBE64)
func (rwops *RWops) ReadBE64() uint64 {
	if rwops == nil {
		return 0
	}
	return uint64(C.SDL_ReadBE64(rwops.cptr()))
}

// LoadFile_RW loads all the data from an SDL data stream.
// (https://wiki.libsdl.org/SDL_LoadFile_RW)
func (src *RWops) LoadFileRW(freesrc bool) (data []byte, size int) {
	var _size C.size_t
	var _freesrc C.int = 0

	if freesrc {
		_freesrc = 1
	}

	_data := C.SDL_LoadFile_RW(src.cptr(), &_size, _freesrc)
	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&data))
	sliceHeader.Cap = int(_size)
	sliceHeader.Len = int(_size)
	sliceHeader.Data = uintptr(_data)
	size = int(_size)
	return
}

// LoadFile loads an entire file
// (https://wiki.libsdl.org/SDL_LoadFile)
func LoadFile(file string) (data []byte, size int) {
	return RWFromFile(file, "rb").LoadFileRW(true)
}

// WriteU8 writes a byte to the RWops.
// (https://wiki.libsdl.org/SDL_WriteU8)
func (rwops *RWops) WriteU8(value uint8) uint {
	if rwops == nil {
		return 0
	}
	return uint(C.SDL_WriteU8(rwops.cptr(), C.Uint8(value)))
}

// WriteLE16 writes 16 bits in native format to the RWops as little-endian data.
// (https://wiki.libsdl.org/SDL_WriteLE16)
func (rwops *RWops) WriteLE16(value uint16) uint {
	if rwops == nil {
		return 0
	}
	return uint(C.SDL_WriteLE16(rwops.cptr(), C.Uint16(value)))
}

// WriteBE16 writes 16 bits in native format to the RWops as big-endian data.
// (https://wiki.libsdl.org/SDL_WriteBE16)
func (rwops *RWops) WriteBE16(value uint16) uint {
	if rwops == nil {
		return 0
	}
	return uint(C.SDL_WriteBE16(rwops.cptr(), C.Uint16(value)))
}

// WriteLE32 writes 32 bits in native format to the RWops as little-endian data.
// (https://wiki.libsdl.org/SDL_WriteLE32)
func (rwops *RWops) WriteLE32(value uint32) uint {
	if rwops == nil {
		return 0
	}
	return uint(C.SDL_WriteLE32(rwops.cptr(), C.Uint32(value)))
}

// WriteBE32 writes 32 bits in native format to the RWops as big-endian data.
// (https://wiki.libsdl.org/SDL_WriteBE32)
func (rwops *RWops) WriteBE32(value uint32) uint {
	if rwops == nil {
		return 0
	}
	return uint(C.SDL_WriteBE32(rwops.cptr(), C.Uint32(value)))
}

// WriteLE64 writes 64 bits in native format to the RWops as little-endian data.
// (https://wiki.libsdl.org/SDL_WriteLE64)
func (rwops *RWops) WriteLE64(value uint64) uint {
	if rwops == nil {
		return 0
	}
	return uint(C.SDL_WriteLE64(rwops.cptr(), C.Uint64(value)))
}

// WriteBE64 writes 64 bits in native format to the RWops as big-endian data.
// (https://wiki.libsdl.org/SDL_WriteBE64)
func (rwops *RWops) WriteBE64(value uint64) uint {
	if rwops == nil {
		return 0
	}
	return uint(C.SDL_WriteBE64(rwops.cptr(), C.Uint64(value)))
}
<|endoftext|>


<commit_before>// Copyright (C) 2017 Damon Revoe. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

package main

import (
	"bufio"
	"errors"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/spf13/cobra"
)

// bootstrapInDir bootstraps the package if 'configure' does not exist.
func bootstrapInDir(packageName, packageDir string) error {
	fmt.Println("[bootstrap] " + packageName)
	bootstrapCmd := exec.Command("./autogen.sh")
	bootstrapCmd.Dir = packageDir
	bootstrapCmd.Stdout = os.Stdout
	bootstrapCmd.Stderr = os.Stderr
	if err := bootstrapCmd.Run(); err != nil {
		return errors.New(filepath.Join(packageDir, "autogen.sh") +
			": " + err.Error())
	}

	return nil
}

type configureHelpParser struct {
	optRegexp        *regexp.Regexp
	classifier       optClassifier
	ignoredFeatOrPkg map[string]struct{}
}

func createConfigureHelpParser() configureHelpParser {
	return configureHelpParser{
		regexp.MustCompile(`^--([^\s\[=]+)([^\s]*)\s*(.*)$`),
		createOptClassifier(),
		map[string]struct{}{
			"FEATURE":             struct{}{},
			"PACKAGE":             struct{}{},
			"aix-soname":          struct{}{},
			"dependency-tracking": struct{}{},
			"fast-install":        struct{}{},
			"gnu-ld":              struct{}{},
			"libtool-lock":        struct{}{},
			"option-checking":     struct{}{},
			"pkgconfigdir":        struct{}{},
			"silent-rules":        struct{}{},
			"sysroot":             struct{}{},
		}}
}

func (helpParser *configureHelpParser) parseOptions(packageDir string) (
	[]optDescription, error) {
	configureHelpCmd := exec.Command("./configure", "--help")
	configureHelpCmd.Dir = packageDir
	configureHelpStdout, err := configureHelpCmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	if err = configureHelpCmd.Start(); err != nil {
		return nil, err
	}
	helpScanner := bufio.NewScanner(configureHelpStdout)

	var options []optDescription
	var currentOption *optDescription

	for helpScanner.Scan() {
		helpLine := strings.TrimRight(helpScanner.Text(), " ")

		if helpLine == "" || !strings.HasPrefix(helpLine, " ") {
			if currentOption != nil {
				options = append(options, *currentOption)
				currentOption = nil
			}
			continue
		}

		helpLine = strings.TrimLeft(helpLine, " ")

		if strings.HasPrefix(helpLine, "-") {
			if currentOption != nil {
				options = append(options, *currentOption)
				currentOption = nil
			}
		} else {
			if currentOption != nil {
				if currentOption.description != "" {
					currentOption.description += " "
				}
				currentOption.description += helpLine
			}
			continue
		}

		parts := helpParser.optRegexp.FindStringSubmatch(helpLine)

		if len(parts) < 4 {
			continue
		}
		opt, arg, descr := parts[1], parts[2], parts[3]

		key := helpParser.classifier.classify(opt)

		if key.optType != optOther {
			_, present := helpParser.ignoredFeatOrPkg[key.optName]
			if present {
				continue
			}
		}

		currentOption = &optDescription{key, descr, "--" + opt + arg}
	}
	if err := helpScanner.Err(); err != nil {
		return nil, err
	}
	if err = configureHelpCmd.Wait(); err != nil {
		return nil, err
	}

	return options, nil
}

func generateAndBootstrapPackages(workspaceDir string,
	pkgSelection []string) error {
	packageIndex, err := readPackageDefinitions(workspaceDir)
	if err != nil {
		return err
	}

	privateDir := getPrivateDir(workspaceDir)

	pkgRootDir := filepath.Join(privateDir, "packages")

	type packageAndGenerator struct {
		pd         *packageDefinition
		packageDir string
		generator  func() (bool, error)
	}

	var packagesAndGenerators []packageAndGenerator

	for _, packageName := range pkgSelection {
		pd, ok := packageIndex.packageByName[packageName]
		if !ok {
			return errors.New("no such package: " + packageName)
		}

		packageDir := filepath.Join(pkgRootDir, pd.packageName)

		generator, err := pd.getPackageGeneratorFunc(packageDir)
		if err != nil {
			return err
		}

		packagesAndGenerators = append(packagesAndGenerators,
			packageAndGenerator{pd, packageDir, generator})
	}

	params := templateParams{
		"makefile":       flags.makefile,
		"default_target": flags.defaultMakeTarget,
	}

	helpParser := createConfigureHelpParser()

	var packagesToBootstrap []packageAndGenerator

	// Generate autoconf and automake sources for the selected packages.
	for _, pg := range packagesAndGenerators {
		changed, err := pg.generator()
		if err != nil {
			return err
		}
		if changed {
			packagesToBootstrap = append(packagesToBootstrap, pg)
		}
	}

	// Bootstrap the selected packages.
	for _, pg := range packagesToBootstrap {
		if err = bootstrapInDir(pg.pd.packageName,
			pg.packageDir); err != nil {
			return err
		}
	}

	conftabPathname := filepath.Join(privateDir, "conftab")

	conftabCreated := false
	conftabUpdated := false

	conftab, err := readConftab(conftabPathname)
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
		conftab = newConftab()
		conftabCreated = true
	}

	for _, pg := range packagesAndGenerators {
		options, err := helpParser.parseOptions(pg.packageDir)
		if err != nil {
			return err
		}

		for _, opt := range options {
			if opt.key.optType != optOther &&
				conftab.addOption(pg.pd.packageName, &opt) {
				conftabUpdated = true
			}
		}
	}

	if conftabCreated || conftabUpdated {
		if conftabCreated {
			fmt.Println("A " + conftabPathname)
		} else {
			fmt.Println("U " + conftabPathname)
		}
		if err = conftab.writeTo(conftabPathname); err != nil {
			return err
		}
	}

	if err = generateWorkspaceFiles(workspaceDir, params); err != nil {
		return err
	}

	return nil
}

// SelectCmd represents the select command
var selectCmd = &cobra.Command{
	Use:   "select package_range...",
	Short: "Choose one or more packages to work on",
	Args:  cobra.MinimumNArgs(1),
	Run: func(_ *cobra.Command, args []string) {
		if err := generateAndBootstrapPackages(getWorkspaceDir(),
			args); err != nil {
			log.Fatal(err)
		}
	},
}

func init() {
	RootCmd.AddCommand(selectCmd)

	selectCmd.Flags().SortFlags = false
	addQuietFlag(selectCmd)
	addPkgPathFlag(selectCmd)
	addWorkspaceDirFlag(selectCmd)
	addMakefileFlag(selectCmd)
	addDefaultMakeTargetFlag(selectCmd)
}
<commit_msg>Fix golint issue<commit_after>// Copyright (C) 2017 Damon Revoe. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

package main

import (
	"bufio"
	"errors"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/spf13/cobra"
)

// bootstrapInDir bootstraps the package if 'configure' does not exist.
func bootstrapInDir(packageName, packageDir string) error {
	fmt.Println("[bootstrap] " + packageName)
	bootstrapCmd := exec.Command("./autogen.sh")
	bootstrapCmd.Dir = packageDir
	bootstrapCmd.Stdout = os.Stdout
	bootstrapCmd.Stderr = os.Stderr
	if err := bootstrapCmd.Run(); err != nil {
		return errors.New(filepath.Join(packageDir, "autogen.sh") +
			": " + err.Error())
	}

	return nil
}

type configureHelpParser struct {
	optRegexp        *regexp.Regexp
	classifier       optClassifier
	ignoredFeatOrPkg map[string]struct{}
}

func createConfigureHelpParser() configureHelpParser {
	return configureHelpParser{
		regexp.MustCompile(`^--([^\s\[=]+)([^\s]*)\s*(.*)$`),
		createOptClassifier(),
		map[string]struct{}{
			"FEATURE":             struct{}{},
			"PACKAGE":             struct{}{},
			"aix-soname":          struct{}{},
			"dependency-tracking": struct{}{},
			"fast-install":        struct{}{},
			"gnu-ld":              struct{}{},
			"libtool-lock":        struct{}{},
			"option-checking":     struct{}{},
			"pkgconfigdir":        struct{}{},
			"silent-rules":        struct{}{},
			"sysroot":             struct{}{},
		}}
}

func (helpParser *configureHelpParser) parseOptions(packageDir string) (
	[]optDescription, error) {
	configureHelpCmd := exec.Command("./configure", "--help")
	configureHelpCmd.Dir = packageDir
	configureHelpStdout, err := configureHelpCmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	if err = configureHelpCmd.Start(); err != nil {
		return nil, err
	}
	helpScanner := bufio.NewScanner(configureHelpStdout)

	var options []optDescription
	var currentOption *optDescription

	for helpScanner.Scan() {
		helpLine := strings.TrimRight(helpScanner.Text(), " ")

		if helpLine == "" || !strings.HasPrefix(helpLine, " ") {
			if currentOption != nil {
				options = append(options, *currentOption)
				currentOption = nil
			}
			continue
		}

		helpLine = strings.TrimLeft(helpLine, " ")

		if strings.HasPrefix(helpLine, "-") {
			if currentOption != nil {
				options = append(options, *currentOption)
				currentOption = nil
			}
		} else {
			if currentOption != nil {
				if currentOption.description != "" {
					currentOption.description += " "
				}
				currentOption.description += helpLine
			}
			continue
		}

		parts := helpParser.optRegexp.FindStringSubmatch(helpLine)

		if len(parts) < 4 {
			continue
		}
		opt, arg, descr := parts[1], parts[2], parts[3]

		key := helpParser.classifier.classify(opt)

		if key.optType != optOther {
			_, present := helpParser.ignoredFeatOrPkg[key.optName]
			if present {
				continue
			}
		}

		currentOption = &optDescription{key, descr, "--" + opt + arg}
	}
	if err := helpScanner.Err(); err != nil {
		return nil, err
	}
	if err = configureHelpCmd.Wait(); err != nil {
		return nil, err
	}

	return options, nil
}

func generateAndBootstrapPackages(workspaceDir string,
	pkgSelection []string) error {
	packageIndex, err := readPackageDefinitions(workspaceDir)
	if err != nil {
		return err
	}

	privateDir := getPrivateDir(workspaceDir)

	pkgRootDir := filepath.Join(privateDir, "packages")

	type packageAndGenerator struct {
		pd         *packageDefinition
		packageDir string
		generator  func() (bool, error)
	}

	var packagesAndGenerators []packageAndGenerator

	for _, packageName := range pkgSelection {
		pd, ok := packageIndex.packageByName[packageName]
		if !ok {
			return errors.New("no such package: " + packageName)
		}

		packageDir := filepath.Join(pkgRootDir, pd.packageName)

		generator, err := pd.getPackageGeneratorFunc(packageDir)
		if err != nil {
			return err
		}

		packagesAndGenerators = append(packagesAndGenerators,
			packageAndGenerator{pd, packageDir, generator})
	}

	params := templateParams{
		"makefile":       flags.makefile,
		"default_target": flags.defaultMakeTarget,
	}

	helpParser := createConfigureHelpParser()

	var packagesToBootstrap []packageAndGenerator

	// Generate autoconf and automake sources for the selected packages.
	for _, pg := range packagesAndGenerators {
		changed, err := pg.generator()
		if err != nil {
			return err
		}
		if changed {
			packagesToBootstrap = append(packagesToBootstrap, pg)
		}
	}

	// Bootstrap the selected packages.
	for _, pg := range packagesToBootstrap {
		if err = bootstrapInDir(pg.pd.packageName,
			pg.packageDir); err != nil {
			return err
		}
	}

	conftabPathname := filepath.Join(privateDir, "conftab")

	conftabCreated := false
	conftabUpdated := false

	conftab, err := readConftab(conftabPathname)
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
		conftab = newConftab()
		conftabCreated = true
	}

	for _, pg := range packagesAndGenerators {
		options, err := helpParser.parseOptions(pg.packageDir)
		if err != nil {
			return err
		}

		for _, opt := range options {
			if opt.key.optType != optOther &&
				conftab.addOption(pg.pd.packageName, &opt) {
				conftabUpdated = true
			}
		}
	}

	if conftabCreated || conftabUpdated {
		if conftabCreated {
			fmt.Println("A " + conftabPathname)
		} else {
			fmt.Println("U " + conftabPathname)
		}
		if err = conftab.writeTo(conftabPathname); err != nil {
			return err
		}
	}

	return generateWorkspaceFiles(workspaceDir, params)
}

// SelectCmd represents the select command
var selectCmd = &cobra.Command{
	Use:   "select package_range...",
	Short: "Choose one or more packages to work on",
	Args:  cobra.MinimumNArgs(1),
	Run: func(_ *cobra.Command, args []string) {
		if err := generateAndBootstrapPackages(getWorkspaceDir(),
			args); err != nil {
			log.Fatal(err)
		}
	},
}

func init() {
	RootCmd.AddCommand(selectCmd)

	selectCmd.Flags().SortFlags = false
	addQuietFlag(selectCmd)
	addPkgPathFlag(selectCmd)
	addWorkspaceDirFlag(selectCmd)
	addMakefileFlag(selectCmd)
	addDefaultMakeTargetFlag(selectCmd)
}
<|endoftext|>


<commit_before>// Brief: Scripture API
// Primary responsibility: Query calls for Scripture functionality

package main

import (
	"fmt"
	"log"
	"strings"

	bmul "github.com/julwrites/BotMultiplexer"
	"golang.org/x/net/html"
)

func GetReference(doc *html.Node, env *bmul.SessionData) string {
	refNode, err := FindByClass(doc, "bcv")
	if err != nil {
		log.Printf("Error parsing for reference: %v", err)
		return ""
	}

	return refNode.FirstChild.Data
}

func ParseNodesForPassage(node *html.Node) string {
	var text string
	var parts []string
	for child := node.FirstChild; child != nil; child = child.NextSibling {
		parts = append(parts, text)

		switch tag := child.Data; tag {
		case "span":
			parts = append(parts, "*")
			parts = append(parts, ParseNodesForPassage(child))
			parts = append(parts, "*")
		case "sup":
			isFootnote := func(node *html.Node) bool {
				for _, attr := range node.Attr {
					if attr.Key == "class" && attr.Val == "footnote" {
						return true
					}
				}
				return false
			}
			if isFootnote(child) {
				break
			}
			parts = append(parts, "^")
			parts = append(parts, ParseNodesForPassage(child))
			parts = append(parts, "^")
			break
		default:
			parts = append(parts, child.Data)
		}
	}
	text = strings.Join(parts, "")
	return text
}

func GetPassage(doc *html.Node, env *bmul.SessionData) string {
	passageNode, startErr := FindByClass(doc, fmt.Sprintf("version-%s result-text-style-normal text-html", GetUserConfig(&env.User).Version))
	if startErr != nil {
		log.Printf("Error parsing for passage: %v", startErr)
		return ""
	}

	filtNodes := FilterChildren(passageNode, func(child *html.Node) bool {
		switch tag := child.Data; tag {
		case "h1":
			fallthrough
		case "h2":
			fallthrough
		case "h3":
			fallthrough
		case "p":
			return true
		}
		return false
	})

	log.Printf("Candidate nodes number %d", len(filtNodes))

	textBlocks := MapNodeList(filtNodes, ParseNodesForPassage)

	// var spanNodes []*html.Node
	// for _, node := range filtNodes {
	// 	spanNodes = append(spanNodes, FilterChildren(node, func(node *html.Node) bool { return node.Data == "span" })...)
	// }

	// textBlocks := MapNodeList(spanNodes, func(node *html.Node) string {
	// 	var text string
	// 	for child := node.FirstChild; child != nil; child = child.NextSibling {
	// 		text = child.Data
	// 		if child.Data == "sup" || child.Data == "span" {
	// 			text = "<sup>" + text + "<sup>"
	// 		}
	// 	}
	// 	return text
	// })

	var passage strings.Builder

	for _, block := range textBlocks {
		passage.WriteString(block)
	}

	log.Printf("%s", passage.String())

	return fmt.Sprintf("I currently can't parse a passage but here's what I got so far: %s", passage.String())
}

func GetBiblePassage(env *bmul.SessionData) {
	if len(env.Msg.Message) > 0 {

		doc := QueryBibleGateway(env.Msg.Message, env)

		ref := GetReference(doc, env)
		log.Printf("Reference retrieved: %s", ref)

		if len(ref) > 0 {
			log.Printf("Getting passage")
			env.Res.Message = GetPassage(doc, env)
		}
	}
}

// func main() {
// 	var env bmul.SessionData
// 	var config UserConfig
// 	config.Version = "NIV"
// 	UpdateUserConfig(&env.User, config)
// 	env.Msg.Message = "gal 1"
// 	GetBiblePassage(&env)
// }
<commit_msg>Splitting up by paragraph<commit_after>// Brief: Scripture API
// Primary responsibility: Query calls for Scripture functionality

package main

import (
	"fmt"
	"log"
	"strings"

	bmul "github.com/julwrites/BotMultiplexer"
	"golang.org/x/net/html"
)

func GetReference(doc *html.Node, env *bmul.SessionData) string {
	refNode, err := FindByClass(doc, "bcv")
	if err != nil {
		log.Printf("Error parsing for reference: %v", err)
		return ""
	}

	return refNode.FirstChild.Data
}

func ParseNodesForPassage(node *html.Node) string {
	var text string
	var parts []string
	for child := node.FirstChild; child != nil; child = child.NextSibling {
		parts = append(parts, text)

		switch tag := child.Data; tag {
		case "span":
			parts = append(parts, "*")
			parts = append(parts, ParseNodesForPassage(child))
			parts = append(parts, "*")
		case "sup":
			isFootnote := func(node *html.Node) bool {
				for _, attr := range node.Attr {
					if attr.Key == "class" && attr.Val == "footnote" {
						return true
					}
				}
				return false
			}
			if isFootnote(child) {
				break
			}
			parts = append(parts, "^")
			parts = append(parts, ParseNodesForPassage(child))
			parts = append(parts, "^")
			break
		default:
			parts = append(parts, child.Data)
		}
	}
	text = strings.Join(parts, "")
	return text
}

func GetPassage(doc *html.Node, env *bmul.SessionData) string {
	passageNode, startErr := FindByClass(doc, fmt.Sprintf("version-%s result-text-style-normal text-html", GetUserConfig(&env.User).Version))
	if startErr != nil {
		log.Printf("Error parsing for passage: %v", startErr)
		return ""
	}

	filtNodes := FilterChildren(passageNode, func(child *html.Node) bool {
		switch tag := child.Data; tag {
		case "h1":
			fallthrough
		case "h2":
			fallthrough
		case "h3":
			fallthrough
		case "p":
			return true
		}
		return false
	})

	log.Printf("Candidate nodes number %d", len(filtNodes))

	var textBlocks []string
	for _, node := range filtNodes {
		textBlocks = append(textBlocks, MapNodeList(node, ParseNodesForPassage)...)
		textBlocks = append(textBlocks, "\n")
	}

	var passage strings.Builder

	for _, block := range textBlocks {
		passage.WriteString(block)
	}

	log.Printf("%s", passage.String())

	return fmt.Sprintf("I currently can't parse a passage but here's what I got so far: %s", passage.String())
}

func GetBiblePassage(env *bmul.SessionData) {
	if len(env.Msg.Message) > 0 {

		doc := QueryBibleGateway(env.Msg.Message, env)

		ref := GetReference(doc, env)
		log.Printf("Reference retrieved: %s", ref)

		if len(ref) > 0 {
			log.Printf("Getting passage")
			env.Res.Message = GetPassage(doc, env)
		}
	}
}

// func main() {
// 	var env bmul.SessionData
// 	var config UserConfig
// 	config.Version = "NIV"
// 	UpdateUserConfig(&env.User, config)
// 	env.Msg.Message = "gal 1"
// 	GetBiblePassage(&env)
// }
<|endoftext|>


<commit_before>package main

type Selection struct {
	on    bool
	start Cursor
	end   Cursor
}

func NewSelection() *Selection {
	return &Selection{}
}

func (s *Selection) SetStart(c *Cursor) {
	s.start = *c
}

func (s *Selection) SetEnd(c *Cursor) {
	s.end = *c
}

// Lines return selected line numbers as int slice.
// Note it will not return last line number if last cursor's offset is 0.
func (s *Selection) Lines() []int {
	if !s.on {
		return nil
	}

	endL := s.end.l
	if s.end.o == 0 {
		endL--
	}

	lns := make([]int, 0)
	for l := s.start.l; l <= endL; l++ {
		lns = append(lns, l)
	}
	return lns
}

func (s *Selection) MinMax() (Cursor, Cursor) {
	if (s.start.l > s.end.l) || (s.start.l == s.end.l && s.start.o > s.end.o) {
		return s.end, s.start
	}
	return s.start, s.end
}

func (s *Selection) Contains(p Point) bool {
	min, max := s.MinMax()
	if min.l <= p.l && p.l <= max.l {
		if p.l == min.l && p.o < min.o {
			return false
		} else if p.l == max.l && p.o >= max.o {
			return false
		}
		return true
	}
	return false
}

func withShift(r rune) bool {
	shifts := "QWERTYUIOP{}|ASDFGHJKL:ZXCVBNM<>?!@#$%^&*()_+"
	for _, s := range shifts {
		if s == r {
			return true
		}
	}
	return false
}
<commit_msg>fix selection.Lines() not work if end < start<commit_after>package main

type Selection struct {
	on    bool
	start Cursor
	end   Cursor
}

func NewSelection() *Selection {
	return &Selection{}
}

func (s *Selection) SetStart(c *Cursor) {
	s.start = *c
}

func (s *Selection) SetEnd(c *Cursor) {
	s.end = *c
}

// Lines return selected line numbers as int slice.
// Note it will not return last line number if last cursor's offset is 0.
func (s *Selection) Lines() []int {
	if !s.on {
		return nil
	}
	start, end := s.MinMax()

	endL := end.l
	if s.end.o == 0 {
		endL--
	}

	lns := make([]int, 0)
	for l := start.l; l <= endL; l++ {
		lns = append(lns, l)
	}
	return lns
}

func (s *Selection) MinMax() (Cursor, Cursor) {
	if (s.start.l > s.end.l) || (s.start.l == s.end.l && s.start.o > s.end.o) {
		return s.end, s.start
	}
	return s.start, s.end
}

func (s *Selection) Contains(p Point) bool {
	min, max := s.MinMax()
	if min.l <= p.l && p.l <= max.l {
		if p.l == min.l && p.o < min.o {
			return false
		} else if p.l == max.l && p.o >= max.o {
			return false
		}
		return true
	}
	return false
}

func withShift(r rune) bool {
	shifts := "QWERTYUIOP{}|ASDFGHJKL:ZXCVBNM<>?!@#$%^&*()_+"
	for _, s := range shifts {
		if s == r {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package semaphore

import (
	"sync"
	"sync/atomic"
)

// An integer-valued semaphore
type Semaphore struct {
	value     int64
	acquireMu sync.Mutex
	wake      chan struct{}
}

// Creates a new semaphore with initial value n. Panics if n is negative.
func New(n int) *Semaphore {
	if n < 0 {
		panic("negative initial value for a semaphore")
	}
	return &Semaphore{
		value: int64(n),
		wake:  make(chan struct{}, 1),
	}
}

// Tries to decrease the semaphore's value by n. If it is smaller than n, waits until it grows large enough.
func (s *Semaphore) Acquire(n int) {
	if n < 0 {
		panic("Semaphore.Acquire called with negative decrement")
	}
	v := atomic.LoadInt64(&s.value)
	for v >= int64(n) {
		if atomic.CompareAndSwapInt64(&s.value, v, v-int64(n)) {
			return
		}
		v = atomic.LoadInt64(&s.value)
	}
	s.acquireMu.Lock()
	v = atomic.AddInt64(&s.value, int64(-n))
	if v < 0 {
		<-s.wake
		v = atomic.LoadInt64(&s.value)
		if v < 0 {
			panic("semaphore: spurious wakeup")
		}
	}
	s.acquireMu.Unlock()
}

// Increases the semaphore's value by n. Will never sleep.
func (s *Semaphore) Release(n int) {
	if n < 0 {
		panic("Semaphore.Release called with negative increment")
	}
	v := atomic.AddInt64(&s.value, int64(n))
	if v-int64(n) < 0 && v >= 0 {
		select {
		case s.wake <- struct{}{}:
		default:
			panic("semaphore: unconsumed wakeup")
		}
	}
}

// Decreases the semaphore value to 0 and returns the difference. Can sleep.
func (s *Semaphore) Drain() int {
	s.acquireMu.Lock()
	v := atomic.LoadInt64(&s.value)
	atomic.AddInt64(&s.value, -v)
	s.acquireMu.Unlock()
	return int(v)
}
<commit_msg>Fixed a race condition between Drain and Acquire.<commit_after>package semaphore

import (
	"sync"
	"sync/atomic"
)

// An integer-valued semaphore
type Semaphore struct {
	value     int64
	acquireMu sync.Mutex
	wake      chan struct{}
}

// Creates a new semaphore with initial value n. Panics if n is negative.
func New(n int) *Semaphore {
	if n < 0 {
		panic("negative initial value for a semaphore")
	}
	return &Semaphore{
		value: int64(n),
		wake:  make(chan struct{}, 1),
	}
}

// Tries to decrease the semaphore's value by n. If it is smaller than n, waits until it grows large enough.
func (s *Semaphore) Acquire(n int) {
	if n < 0 {
		panic("Semaphore.Acquire called with negative decrement")
	}
	v := atomic.LoadInt64(&s.value)
	for v >= int64(n) {
		if atomic.CompareAndSwapInt64(&s.value, v, v-int64(n)) {
			return
		}
		v = atomic.LoadInt64(&s.value)
	}
	s.acquireMu.Lock()
	v = atomic.AddInt64(&s.value, int64(-n))
	if v < 0 {
		<-s.wake
		v = atomic.LoadInt64(&s.value)
		if v < 0 {
			panic("semaphore: spurious wakeup")
		}
	}
	s.acquireMu.Unlock()
}

// Increases the semaphore's value by n. Will never sleep.
func (s *Semaphore) Release(n int) {
	if n < 0 {
		panic("Semaphore.Release called with negative increment")
	}
	v := atomic.AddInt64(&s.value, int64(n))
	if v-int64(n) < 0 && v >= 0 {
		select {
		case s.wake <- struct{}{}:
		default:
			panic("semaphore: unconsumed wakeup")
		}
	}
}

// Decreases the semaphore value to 0 and returns the difference.
func (s *Semaphore) Drain() int {
	for {
		v := atomic.LoadInt64(&s.value)
		if v <= 0 {
			return 0
		}
		if atomic.CompareAndSwapInt64(&s.value, v, 0) {
			return v
		}
	}
	panic("unreachable")
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
    "bytes"

    "gopkg.in/yaml.v2"
	"github.com/guhao022/chca/conf"
	"github.com/guhao022/chca/utils"
    "path"
)

var (
	htmlStor = conf.DirHtml() //编译后保存的文件夹

	contents []*Article
    cates map[string]*Category
    tags map[string]*Tag
)

func LoadArticle() {

    contents = make([]*Article, 0)

    cates = make(map[string]*Category)
    tags = make(map[string]*Tag)

	mdlist := Marklist()

	for _, fi := range mdlist {
		art, err := loadContent(fi)

		if err == nil {
			art.Url = CreatePostLink(art)
			contents = append(contents,art)

            for _, _cate := range art.Category {
                cate := cates[_cate]
                if cate == nil {
                    cate = &Category{0, _cate, make([]*Article, 0), "/category/" + _cate}
                    cates[_cate] = cate
                }
                cate.Count += 1
                cate.Posts = append(cate.Posts, art)
            }

            for _, _tag := range art.Tags {
                tag := tags[_tag]
                if tag == nil {
                    tag = &Tag{0, _tag, make([]*Article, 0), "/tag/" + _tag}
                    tags[_tag] = tag
                }
                tag.Count += 1
                tag.Posts = append(tag.Posts, art)
            }

		} else {
			panic(err)
		}
	}

    sort.Sort(Articles(contents))
}

// 获取归档信息
func GetArchive() []*CollatedYear {

    collated := make(CollatedYears, 0)

    _collated := make(map[string]*CollatedYear)

    for _, post := range contents {

        year := utils.Year(post.CreatedAt)
        month := utils.Month(post.CreatedAt)
        _month := time.Unix(post.CreatedAt, 0).Month()

        yearc := _collated[year]
        if yearc == nil {
            yearc = &CollatedYear{year, make([]*CollatedMonth, 0), make(map[string]*CollatedMonth)}
            _collated[year] = yearc
        }
        monthc := yearc.months[month]
        if monthc == nil {
            monthc = &CollatedMonth{month, []*Article{}, _month}
            yearc.months[month] = monthc
        }
        monthc.Posts = append(monthc.Posts, post)
    }

    for _, yearc := range _collated {
        monthArray := make(CollatedMonths, 0)
        for _, monthc := range yearc.months {
            monthArray = append(monthArray, monthc)
        }

        sort.Sort(monthArray)

        yearc.months = nil
        yearc.Months = monthArray
        collated = append(collated, yearc)
    }

    sort.Sort(collated)
    return collated
}

//获取菜单数组
func GetCate() map[string]*Category {
    return cates
}

// 获取tag
func GetTag() map[string]*Tag {
    return tags
}

func loadContent(file string) (art *Article, err error) {

    art = &Article{}

    ctx, err := ReadMuCtx(file)

    if err != nil {
        return nil, err
    }

    sumLines := conf.SiteSumLine()

    summary, err := makeSummary(ctx.Content, sumLines)

    if err != nil {
        return nil, err
    }

    art.Title = ctx.Title
    art.Description = ctx.Description
    art.Category = ctx.Categories
    art.Tags = ctx.Tags
    art.Summary = summary
    art.Content = utils.MarkdownToHtml(ctx.Content)
    art.CreatedAt = utils.Str2Unix("2006-01-02", ctx.Date)

    return art, nil
}

// 获取所有的文章
func GetAllArt() []*Article {
	return contents
}

// 获取about内容
func GetAbout() (art *Article, err error) {
    art = &Article{}
    about := path.Join(conf.DirTheme(), "/about.md")

    if _, err := os.Stat(about); os.IsNotExist(err) {
        return art, nil
    }

    ctx, err := ReadMuCtx(about)

    if err != nil {
        return nil, err
    }

    art.Title = ""
    art.Content = utils.MarkdownToHtml(ctx.Content)
    art.CreatedAt = utils.Str2Unix("2006-01-02", ctx.Date)

    return art, nil
}

// 获取 markdown 文件夹下所有文件
func Marklist() (mdlist []string) {
	mddir := conf.DirMark()

	filepath.Walk(mddir, func(path string, f os.FileInfo, err error) error {

		if err != nil { //忽略错误
			return err
		}

		if f.IsDir() {
			return nil
		}

		if strings.ToLower(f.Name()) == "readme.md" {
			return nil
		}

		if strings.HasSuffix(f.Name(), ".md") {
			mdlist = append(mdlist, path)
		}
		return nil
	})

	return mdlist
}

// 根据文件获取摘要信息
func makeSummary(content string, lines int) (string, error) {
    buff := bufio.NewReader(bytes.NewBufferString(content))
    dst := ""
    for lines > 0 {
        line, err := buff.ReadString('\n')
        if err != nil || io.EOF == err {
            break
        }

        if strings.Contains(line, "[toc]") {
            continue
        }

        if strings.Trim(line, "\r\n\t ") == "```" {
            continue
        }

        dst += line
        lines--
    }

    return utils.MarkdownToHtml(dst), nil
}

// 根据内容获取摘要信息
func summary(content string, n int) string {
	strSlice := strings.SplitN(content, "\n", -1)

	//var summary string
	var sumSlice []string

	for i, str := range strSlice {
		if strings.Contains(str, "[toc]") {
			continue
		}

		if i >= n {
			break
		}

		sumSlice = append(sumSlice, str)
	}

	summary := strings.Join(sumSlice, "\n")

	return summary
}

// 配置生产路径
func CreatePostLink(art *Article) string {
	t := time.Unix(art.CreatedAt, 0)

	year, month, day := t.Date()

	link := fmt.Sprintf("/%s/%d/%d/%d/%s/", "article", year, month, day, utils.Convert(art.Title))

	return link
}

type mustring struct {
    Title       string
    Description string
    Date        string
    Categories  []string
    Tags        []string
    Content     string
}

func ReadMuCtx(path string) (ctx *mustring, err error) {

	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	br := bufio.NewReader(f)
	line, err := br.ReadString('\n')
	if err != nil {
		return nil, err
	}
	if !strings.HasPrefix(line, "---") {
		err = errors.New("Not Start with ---   : " + path)
		return nil, err
	}

	buf := bytes.NewBuffer(nil)

	for {
		line, err = br.ReadString('\n')
		if err != nil {
			if err != io.EOF {
				return nil, err
			}
		}
		if strings.HasPrefix(line, "---") {
			break
		}
		buf.WriteString(line)
	}

    err = yaml.Unmarshal(buf.Bytes(), &ctx)

    content, err := ioutil.ReadAll(br)
    if err != nil {
        return nil, err
    }

    fi, _ := f.Stat()

    if ctx.Title == "" {
        ctx.Title = strings.Replace(strings.TrimRight(fi.Name(), ".md"), conf.DirMark()+"/", "", 1)
    }

    if ctx.Date == "" {
        ctx.Date = utils.Format(fi.ModTime().Unix())
    }

    ctx.Content = string(content)

	return
}
<commit_msg>add about<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
    "bytes"
    "path"

    "gopkg.in/yaml.v2"
	"github.com/guhao022/chca/conf"
	"github.com/guhao022/chca/utils"
)

var (
	htmlStor = conf.DirHtml() //编译后保存的文件夹

	contents []*Article
    cates map[string]*Category
    tags map[string]*Tag
)

func LoadArticle() {

    contents = make([]*Article, 0)

    cates = make(map[string]*Category)
    tags = make(map[string]*Tag)

	mdlist := Marklist()

	for _, fi := range mdlist {
		art, err := loadContent(fi)

		if err == nil {
			art.Url = CreatePostLink(art)
			contents = append(contents,art)

            for _, _cate := range art.Category {
                cate := cates[_cate]
                if cate == nil {
                    cate = &Category{0, _cate, make([]*Article, 0), "/category/" + _cate}
                    cates[_cate] = cate
                }
                cate.Count += 1
                cate.Posts = append(cate.Posts, art)
            }

            for _, _tag := range art.Tags {
                tag := tags[_tag]
                if tag == nil {
                    tag = &Tag{0, _tag, make([]*Article, 0), "/tag/" + _tag}
                    tags[_tag] = tag
                }
                tag.Count += 1
                tag.Posts = append(tag.Posts, art)
            }

		} else {
			panic(err)
		}
	}

    sort.Sort(Articles(contents))
}

// 获取归档信息
func GetArchive() []*CollatedYear {

    collated := make(CollatedYears, 0)

    _collated := make(map[string]*CollatedYear)

    for _, post := range contents {

        year := utils.Year(post.CreatedAt)
        month := utils.Month(post.CreatedAt)
        _month := time.Unix(post.CreatedAt, 0).Month()

        yearc := _collated[year]
        if yearc == nil {
            yearc = &CollatedYear{year, make([]*CollatedMonth, 0), make(map[string]*CollatedMonth)}
            _collated[year] = yearc
        }
        monthc := yearc.months[month]
        if monthc == nil {
            monthc = &CollatedMonth{month, []*Article{}, _month}
            yearc.months[month] = monthc
        }
        monthc.Posts = append(monthc.Posts, post)
    }

    for _, yearc := range _collated {
        monthArray := make(CollatedMonths, 0)
        for _, monthc := range yearc.months {
            monthArray = append(monthArray, monthc)
        }

        sort.Sort(monthArray)

        yearc.months = nil
        yearc.Months = monthArray
        collated = append(collated, yearc)
    }

    sort.Sort(collated)
    return collated
}

//获取菜单数组
func GetCate() map[string]*Category {
    return cates
}

// 获取tag
func GetTag() map[string]*Tag {
    return tags
}

func loadContent(file string) (art *Article, err error) {

    art = &Article{}

    ctx, err := ReadMuCtx(file)

    if err != nil {
        return nil, err
    }

    sumLines := conf.SiteSumLine()

    summary, err := makeSummary(ctx.Content, sumLines)

    if err != nil {
        return nil, err
    }

    art.Title = ctx.Title
    art.Description = ctx.Description
    art.Category = ctx.Categories
    art.Tags = ctx.Tags
    art.Summary = summary
    art.Content = utils.MarkdownToHtml(ctx.Content)
    art.CreatedAt = utils.Str2Unix("2006-01-02", ctx.Date)

    return art, nil
}

// 获取所有的文章
func GetAllArt() []*Article {
	return contents
}

// 获取about内容
func GetAbout() (art *Article, err error) {
    art = &Article{}
    about := path.Join(conf.DirTheme(), "/about.md")

    if _, err := os.Stat(about); os.IsNotExist(err) {
        return art, nil
    }

    content, err := ioutil.ReadFile(about)

    if err != nil {
        return nil, err
    }

    art.Title = ""
    art.Content = utils.MarkdownToHtml(string(content))
    art.CreatedAt = time.Now().Unix()

    return art, nil
}

// 获取 markdown 文件夹下所有文件
func Marklist() (mdlist []string) {
	mddir := conf.DirMark()

	filepath.Walk(mddir, func(path string, f os.FileInfo, err error) error {

		if err != nil { //忽略错误
			return err
		}

		if f.IsDir() {
			return nil
		}

		if strings.ToLower(f.Name()) == "readme.md" {
			return nil
		}

		if strings.HasSuffix(f.Name(), ".md") {
			mdlist = append(mdlist, path)
		}
		return nil
	})

	return mdlist
}

// 根据文件获取摘要信息
func makeSummary(content string, lines int) (string, error) {
    buff := bufio.NewReader(bytes.NewBufferString(content))
    dst := ""
    for lines > 0 {
        line, err := buff.ReadString('\n')
        if err != nil || io.EOF == err {
            break
        }

        if strings.Contains(line, "[toc]") {
            continue
        }

        if strings.Trim(line, "\r\n\t ") == "```" {
            continue
        }

        dst += line
        lines--
    }

    return utils.MarkdownToHtml(dst), nil
}

// 根据内容获取摘要信息
func summary(content string, n int) string {
	strSlice := strings.SplitN(content, "\n", -1)

	//var summary string
	var sumSlice []string

	for i, str := range strSlice {
		if strings.Contains(str, "[toc]") {
			continue
		}

		if i >= n {
			break
		}

		sumSlice = append(sumSlice, str)
	}

	summary := strings.Join(sumSlice, "\n")

	return summary
}

// 配置生产路径
func CreatePostLink(art *Article) string {
	t := time.Unix(art.CreatedAt, 0)

	year, month, day := t.Date()

	link := fmt.Sprintf("/%s/%d/%d/%d/%s/", "article", year, month, day, utils.Convert(art.Title))

	return link
}

type mustring struct {
    Title       string
    Description string
    Date        string
    Categories  []string
    Tags        []string
    Content     string
}

func ReadMuCtx(path string) (ctx *mustring, err error) {

	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	br := bufio.NewReader(f)
	line, err := br.ReadString('\n')
	if err != nil {
		return nil, err
	}
	if !strings.HasPrefix(line, "---") {
		err = errors.New("Not Start with ---   : " + path)
		return nil, err
	}

	buf := bytes.NewBuffer(nil)

	for {
		line, err = br.ReadString('\n')
		if err != nil {
			if err != io.EOF {
				return nil, err
			}
		}
		if strings.HasPrefix(line, "---") {
			break
		}
		buf.WriteString(line)
	}

    err = yaml.Unmarshal(buf.Bytes(), &ctx)

    content, err := ioutil.ReadAll(br)
    if err != nil {
        return nil, err
    }

    fi, _ := f.Stat()

    if ctx.Title == "" {
        ctx.Title = strings.Replace(strings.TrimRight(fi.Name(), ".md"), conf.DirMark()+"/", "", 1)
    }

    if ctx.Date == "" {
        ctx.Date = utils.Format(fi.ModTime().Unix())
    }

    ctx.Content = string(content)

	return
}
<|endoftext|>


<commit_before>package adyen

/**********
* Payment *
**********/

// One-click functionality gives the shopper the option to store their payment details with the merchant, within the Adyen environment.
//
// In this type of transaction, the shopper needs to enter the CVC code for the transaction to get through.
//
// Link: https://docs.adyen.com/developers/api-reference/payments-api#recurring
const (
	RecurringPaymentOneClick              = "ONECLICK"
	RecurringPaymentRecurring             = "RECURRING"
	ShopperInteractionContAuth            = "ContAuth"
	SelectRecurringDetailReferenceLatests = "LATEST"
)

// AuthoriseEncrypted structure for Authorisation request (with encrypted card information)
//
// Link - https://docs.adyen.com/developers/api-reference/payments-api#paymentrequest
type AuthoriseEncrypted struct {
	AdditionalData                   *AdditionalData `json:"additionalData,omitempty"`
	Amount                           *Amount         `json:"amount"`
	BillingAddress                   *Address        `json:"billingAddress,omitempty"`
	DeliveryAddress                  *Address        `json:"deliveryAddress,omitempty"`
	Reference                        string          `json:"reference"`
	MerchantAccount                  string          `json:"merchantAccount"`
	ShopperReference                 string          `json:"shopperReference,omitempty"` // Mandatory for recurring payment
	Recurring                        *Recurring      `json:"recurring,omitempty"`
	ShopperEmail                     string          `json:"shopperEmail,omitempty"`
	ShopperInteraction               string          `json:"shopperInteraction,omitempty"`
	ShopperIP                        string          `json:"shopperIP,omitempty"`
	ShopperLocale                    string          `json:"shopperLocale,omitempty"`
	ShopperName                      *Name           `json:"shopperName,omitempty"`
	SelectedRecurringDetailReference string          `json:"selectedRecurringDetailReference,omitempty"`
	BrowserInfo                      *BrowserInfo    `json:"browserInfo,omitempty"` // Required for a 3DS process
	CaptureDelayHours                int             `json:"captureDelayHours,omitempty"`
}

// Authorise structure for Authorisation request (card is not encrypted)
//
// Link - https://docs.adyen.com/developers/api-reference/payments-api#paymentrequest
type Authorise struct {
	Card                             *Card        `json:"card,omitempty"`
	Amount                           *Amount      `json:"amount"`
	BillingAddress                   *Address     `json:"billingAddress,omitempty"`
	DeliveryAddress                  *Address     `json:"deliveryAddress,omitempty"`
	Reference                        string       `json:"reference"`
	MerchantAccount                  string       `json:"merchantAccount"`
	ShopperReference                 string       `json:"shopperReference,omitempty"` // Mandatory for recurring payment
	Recurring                        *Recurring   `json:"recurring,omitempty"`
	ShopperEmail                     string       `json:"shopperEmail,omitempty"`
	ShopperInteraction               string       `json:"shopperInteraction,omitempty"`
	ShopperIP                        string       `json:"shopperIP,omitempty"`
	ShopperLocale                    string       `json:"shopperLocale,omitempty"`
	ShopperName                      *Name        `json:"shopperName,omitempty"`
	SelectedRecurringDetailReference string       `json:"selectedRecurringDetailReference,omitempty"`
	BrowserInfo                      *BrowserInfo `json:"browserInfo,omitempty"` // Required for a 3DS process
}

// AuthoriseResponse is a response structure for Adyen
//
// Link - https://docs.adyen.com/developers/api-reference/payments-api#paymentresult
type AuthoriseResponse struct {
	PspReference   string          `json:"pspReference"`
	ResultCode     string          `json:"resultCode"`
	AuthCode       string          `json:"authCode"`
	RefusalReason  string          `json:"refusalReason"`
	IssuerURL      string          `json:"issuerUrl"`
	MD             string          `json:"md"`
	PaRequest      string          `json:"paRequest"`
	AdditionalData *AdditionalData `json:"additionalData,omitempty"`
}

// AdditionalData stores encrypted information about customer's credit card
type AdditionalData struct {
	Content            string      `json:"card.encrypted.json,omitempty"`
	AliasType          string      `json:"aliasType,omitempty"`
	Alias              string      `json:"alias,omitempty"`
	ExpiryDate         string      `json:"expiryDate,omitempty`
	CardBin            string      `json:cardBin,omitempty`
	CardSummary        string      `json:cardSummary,omitempty`
	PaymentMethod      string      `json:mc,omitempty`
	CardPaymentMethod  string      `json:cardPaymentMethod,omitempty`
	CardIssuingCountry string      `json:cardIssuingCountry,omitempty`
	ExecuteThreeD      *StringBool `json:"executeThreeD,omitempty"`
}

// BrowserInfo hold information on the user browser
type BrowserInfo struct {
	AcceptHeader string `json:"acceptHeader"`
	UserAgent    string `json:"userAgent"`
}

// Recurring hold the behavior for a future payment : could be ONECLICK or RECURRING
type Recurring struct {
	Contract string `json:"contract"`
}

/*************
* Payment 3D *
*************/

// Authorise3D structure for Authorisation request (card is not encrypted)
//
// https://docs.adyen.com/developers/api-reference/payments-api#paymentrequest3d
type Authorise3D struct {
	BillingAddress  *Address     `json:"billingAddress,omitempty"`
	DeliveryAddress *Address     `json:"deliveryAddress,omitempty"`
	MD              string       `json:"md"`
	MerchantAccount string       `json:"merchantAccount"`
	BrowserInfo     *BrowserInfo `json:"browserInfo"`
	PaResponse      string       `json:"paResponse"`
	ShopperEmail    string       `json:"shopperEmail,omitempty"`
	ShopperIP       string       `json:"shopperIP,omitempty"`
	ShopperLocale   string       `json:"shopperLocale,omitempty"`
	ShopperName     *Name        `json:"shopperName,omitempty"`
}

/*******************
* Directory lookup *
*******************/

// DirectoryLookupRequest - get list of available payment methods based on skin, country and order details
//
// Description - https://docs.adyen.com/developers/api-reference/hosted-payment-pages-api#directoryrequest
// CountryCode could be used to test local payment methods, if client's IP is from different country
type DirectoryLookupRequest struct {
	CurrencyCode      string `url:"currencyCode"`
	MerchantAccount   string `url:"merchantAccount"`
	PaymentAmount     int    `url:"paymentAmount"`
	SkinCode          string `url:"skinCode"`
	MerchantReference string `url:"merchantReference"`
	SessionsValidity  string `url:"sessionValidity"`
	MerchantSig       string `url:"merchantSig"`
	CountryCode       string `url:"countryCode"`
	ShipBeforeDate    string `url:"shipBeforeDate"`
}

// DirectoryLookupResponse - api response for DirectoryLookupRequest
//
// Description - https://docs.adyen.com/developers/api-reference/hosted-payment-pages-api#directoryresponse
type DirectoryLookupResponse struct {
	PaymentMethods []PaymentMethod `json:"paymentMethods"`
}

// PaymentMethod - structure for single payment method in directory look up response
//
// Part of DirectoryLookupResponse
type PaymentMethod struct {
	BrandCode string   `json:"brandCode"`
	Name      string   `json:"name"`
	Logos     logos    `json:"logos"`
	Issuers   []issuer `json:"issuers"`
}

// logos - payment method logos
//
// Part of DirectoryLookupResponse
type logos struct {
	Normal string `json:"normal"`
	Small  string `json:"small"`
	Tiny   string `json:"tiny"`
}

// issuer - bank issuer type
//
// Part of DirectoryLookupResponse
type issuer struct {
	IssuerID string `json:"issuerId"`
	Name     string `json:"name"`
}

/***********
* Skip HPP *
***********/

// SkipHppRequest contains data that would be used to create Adyen HPP redirect URL
//
// Link: https://docs.adyen.com/developers/ecommerce-integration/local-payment-methods
//
// Request description: https://docs.adyen.com/developers/api-reference/hosted-payment-pages-api#skipdetailsrequest
type SkipHppRequest struct {
	MerchantReference string `url:"merchantReference"`
	PaymentAmount     int    `url:"paymentAmount"`
	CurrencyCode      string `url:"currencyCode"`
	ShipBeforeDate    string `url:"shipBeforeDate"`
	SkinCode          string `url:"skinCode"`
	MerchantAccount   string `url:"merchantAccount"`
	ShopperLocale     string `url:"shopperLocale"`
	SessionsValidity  string `url:"sessionValidity"`
	MerchantSig       string `url:"merchantSig"`
	CountryCode       string `url:"countryCode"`
	BrandCode         string `url:"brandCode"`
	IssuerID          string `url:"issuerId"`
}
<commit_msg>add delayed captured for authorise<commit_after>package adyen

/**********
* Payment *
**********/

// One-click functionality gives the shopper the option to store their payment details with the merchant, within the Adyen environment.
//
// In this type of transaction, the shopper needs to enter the CVC code for the transaction to get through.
//
// Link: https://docs.adyen.com/developers/api-reference/payments-api#recurring
const (
	RecurringPaymentOneClick              = "ONECLICK"
	RecurringPaymentRecurring             = "RECURRING"
	ShopperInteractionContAuth            = "ContAuth"
	SelectRecurringDetailReferenceLatests = "LATEST"
)

// AuthoriseEncrypted structure for Authorisation request (with encrypted card information)
//
// Link - https://docs.adyen.com/developers/api-reference/payments-api#paymentrequest
type AuthoriseEncrypted struct {
	AdditionalData                   *AdditionalData `json:"additionalData,omitempty"`
	Amount                           *Amount         `json:"amount"`
	BillingAddress                   *Address        `json:"billingAddress,omitempty"`
	DeliveryAddress                  *Address        `json:"deliveryAddress,omitempty"`
	Reference                        string          `json:"reference"`
	MerchantAccount                  string          `json:"merchantAccount"`
	ShopperReference                 string          `json:"shopperReference,omitempty"` // Mandatory for recurring payment
	Recurring                        *Recurring      `json:"recurring,omitempty"`
	ShopperEmail                     string          `json:"shopperEmail,omitempty"`
	ShopperInteraction               string          `json:"shopperInteraction,omitempty"`
	ShopperIP                        string          `json:"shopperIP,omitempty"`
	ShopperLocale                    string          `json:"shopperLocale,omitempty"`
	ShopperName                      *Name           `json:"shopperName,omitempty"`
	SelectedRecurringDetailReference string          `json:"selectedRecurringDetailReference,omitempty"`
	BrowserInfo                      *BrowserInfo    `json:"browserInfo,omitempty"` // Required for a 3DS process
	CaptureDelayHours                int             `json:"captureDelayHours,omitempty"`
}

// Authorise structure for Authorisation request (card is not encrypted)
//
// Link - https://docs.adyen.com/developers/api-reference/payments-api#paymentrequest
type Authorise struct {
	Card                             *Card        `json:"card,omitempty"`
	Amount                           *Amount      `json:"amount"`
	BillingAddress                   *Address     `json:"billingAddress,omitempty"`
	DeliveryAddress                  *Address     `json:"deliveryAddress,omitempty"`
	Reference                        string       `json:"reference"`
	MerchantAccount                  string       `json:"merchantAccount"`
	ShopperReference                 string       `json:"shopperReference,omitempty"` // Mandatory for recurring payment
	Recurring                        *Recurring   `json:"recurring,omitempty"`
	ShopperEmail                     string       `json:"shopperEmail,omitempty"`
	ShopperInteraction               string       `json:"shopperInteraction,omitempty"`
	ShopperIP                        string       `json:"shopperIP,omitempty"`
	ShopperLocale                    string       `json:"shopperLocale,omitempty"`
	ShopperName                      *Name        `json:"shopperName,omitempty"`
	SelectedRecurringDetailReference string       `json:"selectedRecurringDetailReference,omitempty"`
	BrowserInfo                      *BrowserInfo `json:"browserInfo,omitempty"` // Required for a 3DS process
	CaptureDelayHours                int          `json:"captureDelayHours,omitempty"`
}

// AuthoriseResponse is a response structure for Adyen
//
// Link - https://docs.adyen.com/developers/api-reference/payments-api#paymentresult
type AuthoriseResponse struct {
	PspReference   string          `json:"pspReference"`
	ResultCode     string          `json:"resultCode"`
	AuthCode       string          `json:"authCode"`
	RefusalReason  string          `json:"refusalReason"`
	IssuerURL      string          `json:"issuerUrl"`
	MD             string          `json:"md"`
	PaRequest      string          `json:"paRequest"`
	AdditionalData *AdditionalData `json:"additionalData,omitempty"`
}

// AdditionalData stores encrypted information about customer's credit card
type AdditionalData struct {
	Content            string      `json:"card.encrypted.json,omitempty"`
	AliasType          string      `json:"aliasType,omitempty"`
	Alias              string      `json:"alias,omitempty"`
	ExpiryDate         string      `json:"expiryDate,omitempty`
	CardBin            string      `json:cardBin,omitempty`
	CardSummary        string      `json:cardSummary,omitempty`
	PaymentMethod      string      `json:mc,omitempty`
	CardPaymentMethod  string      `json:cardPaymentMethod,omitempty`
	CardIssuingCountry string      `json:cardIssuingCountry,omitempty`
	ExecuteThreeD      *StringBool `json:"executeThreeD,omitempty"`
}

// BrowserInfo hold information on the user browser
type BrowserInfo struct {
	AcceptHeader string `json:"acceptHeader"`
	UserAgent    string `json:"userAgent"`
}

// Recurring hold the behavior for a future payment : could be ONECLICK or RECURRING
type Recurring struct {
	Contract string `json:"contract"`
}

/*************
* Payment 3D *
*************/

// Authorise3D structure for Authorisation request (card is not encrypted)
//
// https://docs.adyen.com/developers/api-reference/payments-api#paymentrequest3d
type Authorise3D struct {
	BillingAddress  *Address     `json:"billingAddress,omitempty"`
	DeliveryAddress *Address     `json:"deliveryAddress,omitempty"`
	MD              string       `json:"md"`
	MerchantAccount string       `json:"merchantAccount"`
	BrowserInfo     *BrowserInfo `json:"browserInfo"`
	PaResponse      string       `json:"paResponse"`
	ShopperEmail    string       `json:"shopperEmail,omitempty"`
	ShopperIP       string       `json:"shopperIP,omitempty"`
	ShopperLocale   string       `json:"shopperLocale,omitempty"`
	ShopperName     *Name        `json:"shopperName,omitempty"`
}

/*******************
* Directory lookup *
*******************/

// DirectoryLookupRequest - get list of available payment methods based on skin, country and order details
//
// Description - https://docs.adyen.com/developers/api-reference/hosted-payment-pages-api#directoryrequest
// CountryCode could be used to test local payment methods, if client's IP is from different country
type DirectoryLookupRequest struct {
	CurrencyCode      string `url:"currencyCode"`
	MerchantAccount   string `url:"merchantAccount"`
	PaymentAmount     int    `url:"paymentAmount"`
	SkinCode          string `url:"skinCode"`
	MerchantReference string `url:"merchantReference"`
	SessionsValidity  string `url:"sessionValidity"`
	MerchantSig       string `url:"merchantSig"`
	CountryCode       string `url:"countryCode"`
	ShipBeforeDate    string `url:"shipBeforeDate"`
}

// DirectoryLookupResponse - api response for DirectoryLookupRequest
//
// Description - https://docs.adyen.com/developers/api-reference/hosted-payment-pages-api#directoryresponse
type DirectoryLookupResponse struct {
	PaymentMethods []PaymentMethod `json:"paymentMethods"`
}

// PaymentMethod - structure for single payment method in directory look up response
//
// Part of DirectoryLookupResponse
type PaymentMethod struct {
	BrandCode string   `json:"brandCode"`
	Name      string   `json:"name"`
	Logos     logos    `json:"logos"`
	Issuers   []issuer `json:"issuers"`
}

// logos - payment method logos
//
// Part of DirectoryLookupResponse
type logos struct {
	Normal string `json:"normal"`
	Small  string `json:"small"`
	Tiny   string `json:"tiny"`
}

// issuer - bank issuer type
//
// Part of DirectoryLookupResponse
type issuer struct {
	IssuerID string `json:"issuerId"`
	Name     string `json:"name"`
}

/***********
* Skip HPP *
***********/

// SkipHppRequest contains data that would be used to create Adyen HPP redirect URL
//
// Link: https://docs.adyen.com/developers/ecommerce-integration/local-payment-methods
//
// Request description: https://docs.adyen.com/developers/api-reference/hosted-payment-pages-api#skipdetailsrequest
type SkipHppRequest struct {
	MerchantReference string `url:"merchantReference"`
	PaymentAmount     int    `url:"paymentAmount"`
	CurrencyCode      string `url:"currencyCode"`
	ShipBeforeDate    string `url:"shipBeforeDate"`
	SkinCode          string `url:"skinCode"`
	MerchantAccount   string `url:"merchantAccount"`
	ShopperLocale     string `url:"shopperLocale"`
	SessionsValidity  string `url:"sessionValidity"`
	MerchantSig       string `url:"merchantSig"`
	CountryCode       string `url:"countryCode"`
	BrandCode         string `url:"brandCode"`
	IssuerID          string `url:"issuerId"`
}
<|endoftext|>


<commit_before>package pdb

import (
	"bufio"
	"compress/gzip"
	"fmt"
	"io"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"

	"github.com/BurntSushi/bcbgo/matt"
)

// AminoThreeToOne is a map from three letter amino acids to their
// corresponding single letter representation.
var AminoThreeToOne = map[string]byte{
	"ALA": 'A', "ARG": 'R', "ASN": 'N', "ASP": 'D', "CYS": 'C',
	"GLU": 'E', "GLN": 'Q', "GLY": 'G', "HIS": 'H', "ILE": 'I',
	"LEU": 'L', "LYS": 'K', "MET": 'M', "PHE": 'F', "PRO": 'P',
	"SER": 'S', "THR": 'T', "TRP": 'W', "TYR": 'Y', "VAL": 'V',
	"SEC": 'U', "PYL": 'O',
}

// AminoOneToThree is the reverse of AminoThreeToOne. It is created in
// this packages 'init' function.
var AminoOneToThree = map[byte]string{}

func init() {
	// Create a reverse map of AminoThreeToOne.
	for k, v := range AminoThreeToOne {
		AminoOneToThree[v] = k
	}
}

// Entry represents all information known about a particular PDB file (that
// has been implemented in this package).
//
// Currently, a PDB entry is simply a file path and a map of protein chains.
type Entry struct {
	Path   string
	Chains map[byte]*Chain
}

// New creates a new PDB Entry from a file. If the file cannot be read, or there
// is an error parsing the PDB file, an error is returned.
//
// If the file name ends with ".gz", gzip decompression will be used.
func New(fileName string) (*Entry, error) {
	var reader io.Reader
	var err error

	reader, err = os.Open(fileName)
	if err != nil {
		return nil, err
	}

	// If the file is gzipped, use the gzip decompressor.
	if path.Ext(fileName) == ".gz" {
		reader, err = gzip.NewReader(reader)
		if err != nil {
			return nil, err
		}
	}

	entry := &Entry{
		Path:   fileName,
		Chains: make(map[byte]*Chain, 0),
	}

	// Now traverse each line, and process it according to the record name.
	breader := bufio.NewReaderSize(reader, 1000)
	for {
		// We ignore 'isPrefix' here, since we never care about lines longer
		// than 1000 characters, which is the size of our buffer.
		line, _, err := breader.ReadLine()
		if err == io.EOF {
			break
		} else if err != nil {
			return nil, err
		}

		// The record name is always in the fix six columns.
		switch strings.TrimSpace(string(line[0:6])) {
		case "SEQRES":
			entry.parseSeqres(line)
		case "ATOM":
			entry.parseAtom(line)
		}
	}

	return entry, nil
}

// PDBArg is a convenience method for creating a PDBArg that can be used in
// the 'matt' package. It sets 'Location' in PDBArg to 'Path' from 'Entry'.
func (e *Entry) PDBArg() matt.PDBArg {
	return matt.PDBArg{Location: e.Path}
}

// PDBArgChain is a convenience method for creating a PDBArg for a specific
// chain that can be used in the 'matt' package.
//
// PDBArgChain panics if 'chainIdent' is not in the chain map for this Entry.
func (e *Entry) PDBArgChain(chainIdent byte) matt.PDBArg {
	if _, ok := e.Chains[chainIdent]; !ok {
		panic(fmt.Sprintf("The chain identifier '%c' was not found in the "+
			"chain map for the '%s' PDB entry.", chainIdent, e.Path))
	}
	return matt.PDBArg{
		Location: e.Path,
		Chain:    chainIdent,
	}
}

// String returns a sorted list of all chains, their residue start/stop indices,
// and the amino acid sequence.
func (e *Entry) String() string {
	lines := make([]string, 0)
	for _, chain := range e.Chains {
		lines = append(lines, chain.String())
	}
	sort.Sort(sort.StringSlice(lines))
	return strings.Join(lines, "\n")
}

// getOrMakeChain looks for a chain in the 'Chains' map corresponding to the
// chain indentifier. If one exists, it is returned. If one doesn't exist,
// it is created, memory is allocated and it is returned.
func (e *Entry) getOrMakeChain(ident byte) *Chain {
	if chain, ok := e.Chains[ident]; ok {
		return chain
	}
	e.Chains[ident] = &Chain{
		Ident:            ident,
		Sequence:         make([]byte, 0, 10),
		AtomResidueStart: 0,
		AtomResidueEnd:   0,
	}
	return e.Chains[ident]
}

// parseSeqres loads all pertinent information from SEQRES records in a PDB
// file. In particular, amino acid resides are read and added to the chain's
// "Sequence" field. If a residue isn't a valid amino acid, it is simply
// ignored.
//
// N.B. This assumes that the SEQRES records are in order in the PDB file.
func (e *Entry) parseSeqres(line []byte) {
	chain := e.getOrMakeChain(line[11])

	// Residues are in columns 19-21, 23-25, 27-29, ..., 67-69
	for i := 19; i <= 67; i += 4 {
		end := i + 3

		// If we're passed the end of this line, quit.
		if end >= len(line) {
			break
		}

		// Get the residue. If it's not in our sequence map, skip it.
		residue := strings.TrimSpace(string(line[i:end]))
		if single, ok := AminoThreeToOne[residue]; ok {
			chain.Sequence = append(chain.Sequence, single)
		}
	}
}

// parseAtom loads all pertinent information from ATOM records in a PDB file.
// Currently, this only includes deducing the amino acid residue start and
// stop indices. (Note that the length of the range is not necessarily
// equivalent to the length of the amino acid sequence found in the SEQRES
// records.)
//
// ATOM records without a valid amino acid residue in columns 18-20 are ignored.
func (e *Entry) parseAtom(line []byte) {
	chain := e.getOrMakeChain(line[21])

	// An ATOM record is only processed if it corresponds to an amino acid
	// residue. (Which is in columns 17-19.)
	residue := strings.TrimSpace(string(line[17:20]))
	if _, ok := AminoThreeToOne[residue]; !ok {
		// Sanity check. I'm pretty sure that only amino acids have three
		// letter abbreviations.
		if len(residue) == 3 {
			panic(fmt.Sprintf("The residue '%s' found in PDB file '%s' has "+
				"length 3, but is not in my amino acid map.",
				residue, e.Path))
		}
		return
	}

	// The residue sequence number is in columns 22-25. Grab it, trim it,
	// and look for an integer.
	snum := strings.TrimSpace(string(line[22:26]))
	if num, err := strconv.ParseInt(snum, 10, 32); err == nil {
		inum := int(num)
		switch {
		case chain.AtomResidueStart == 0 || inum < chain.AtomResidueStart:
			chain.AtomResidueStart = inum
		case chain.AtomResidueEnd == 0 || inum > chain.AtomResidueEnd:
			chain.AtomResidueEnd = inum
		}
	}
}

// Chain represents a protein chain or subunit in a PDB file. Each chain has
// its own identifier, amino acid sequence (if its a protein sequence), and
// the start and stop residue indices of the ATOM coordinates.
type Chain struct {
	Ident                            byte
	Sequence                         []byte
	AtomResidueStart, AtomResidueEnd int
}

// String returns a FASTA-like formatted string of this chain and all of its
// related information.
func (c *Chain) String() string {
	return fmt.Sprintf("> Chain %c (%d, %d) :: length %d\n%s",
		c.Ident, c.AtomResidueStart, c.AtomResidueEnd,
		len(c.Sequence), string(c.Sequence))
}
<commit_msg>Convenience and atoms.<commit_after>package pdb

import (
	"bufio"
	"compress/gzip"
	"fmt"
	"io"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"

	"github.com/BurntSushi/bcbgo/matt"
)

// AminoThreeToOne is a map from three letter amino acids to their
// corresponding single letter representation.
var AminoThreeToOne = map[string]byte{
	"ALA": 'A', "ARG": 'R', "ASN": 'N', "ASP": 'D', "CYS": 'C',
	"GLU": 'E', "GLN": 'Q', "GLY": 'G', "HIS": 'H', "ILE": 'I',
	"LEU": 'L', "LYS": 'K', "MET": 'M', "PHE": 'F', "PRO": 'P',
	"SER": 'S', "THR": 'T', "TRP": 'W', "TYR": 'Y', "VAL": 'V',
	"SEC": 'U', "PYL": 'O',
}

// AminoOneToThree is the reverse of AminoThreeToOne. It is created in
// this packages 'init' function.
var AminoOneToThree = map[byte]string{}

func init() {
	// Create a reverse map of AminoThreeToOne.
	for k, v := range AminoThreeToOne {
		AminoOneToThree[v] = k
	}
}

// Entry represents all information known about a particular PDB file (that
// has been implemented in this package).
//
// Currently, a PDB entry is simply a file path and a map of protein chains.
type Entry struct {
	Path   string
	Chains map[byte]*Chain
}

// New creates a new PDB Entry from a file. If the file cannot be read, or there
// is an error parsing the PDB file, an error is returned.
//
// If the file name ends with ".gz", gzip decompression will be used.
func New(fileName string) (*Entry, error) {
	var reader io.Reader
	var err error

	reader, err = os.Open(fileName)
	if err != nil {
		return nil, err
	}

	// If the file is gzipped, use the gzip decompressor.
	if path.Ext(fileName) == ".gz" {
		reader, err = gzip.NewReader(reader)
		if err != nil {
			return nil, err
		}
	}

	entry := &Entry{
		Path:   fileName,
		Chains: make(map[byte]*Chain, 0),
	}

	// Now traverse each line, and process it according to the record name.
	breader := bufio.NewReaderSize(reader, 1000)
	for {
		// We ignore 'isPrefix' here, since we never care about lines longer
		// than 1000 characters, which is the size of our buffer.
		line, _, err := breader.ReadLine()
		if err == io.EOF {
			break
		} else if err != nil {
			return nil, err
		}

		// The record name is always in the fix six columns.
		switch strings.TrimSpace(string(line[0:6])) {
		case "SEQRES":
			entry.parseSeqres(line)
		case "ATOM":
			entry.parseAtom(line)
		}
	}

	// Sort each chain's atom slice.
	for _, chain := range entry.Chains {
		sort.Sort(chain.Atoms)
		sort.Sort(chain.CaAtoms)
	}

	return entry, nil
}

// OneChain returns a single chain in the PDB file. If there is more than one
// chain, OneChain will panic. This is convenient when you expect a PDB file to
// have only a single chain, but don't know the name.
func (e *Entry) OneChain() *Chain {
	if len(e.Chains) != 1 {
		panic(fmt.Sprintf("OneChain can only be called on PDB entries with "+
			"ONE chain. But the '%s' PDB entry has %d chains.",
			e.Path, len(e.Chains)))
	}
	for _, chain := range e.Chains {
		return chain
	}
	panic("unreachable")
}

// PDBArg is a convenience method for creating a PDBArg that can be used in
// the 'matt' package. It sets 'Location' in PDBArg to 'Path' from 'Entry'.
func (e *Entry) PDBArg() matt.PDBArg {
	return matt.PDBArg{Location: e.Path}
}

// String returns a sorted list of all chains, their residue start/stop indices,
// and the amino acid sequence.
func (e *Entry) String() string {
	lines := make([]string, 0)
	for _, chain := range e.Chains {
		lines = append(lines, chain.String())
	}
	sort.Sort(sort.StringSlice(lines))
	return strings.Join(lines, "\n")
}

// getOrMakeChain looks for a chain in the 'Chains' map corresponding to the
// chain indentifier. If one exists, it is returned. If one doesn't exist,
// it is created, memory is allocated and it is returned.
func (e *Entry) getOrMakeChain(ident byte) *Chain {
	if chain, ok := e.Chains[ident]; ok {
		return chain
	}
	e.Chains[ident] = &Chain{
		entry:            e,
		Ident:            ident,
		Sequence:         make([]byte, 0, 10),
		AtomResidueStart: 0,
		AtomResidueEnd:   0,
		CaAtoms:          make(Atoms, 0, 30),
	}
	return e.Chains[ident]
}

// parseSeqres loads all pertinent information from SEQRES records in a PDB
// file. In particular, amino acid resides are read and added to the chain's
// "Sequence" field. If a residue isn't a valid amino acid, it is simply
// ignored.
//
// N.B. This assumes that the SEQRES records are in order in the PDB file.
func (e *Entry) parseSeqres(line []byte) {
	chain := e.getOrMakeChain(line[11])

	// Residues are in columns 19-21, 23-25, 27-29, ..., 67-69
	for i := 19; i <= 67; i += 4 {
		end := i + 3

		// If we're passed the end of this line, quit.
		if end >= len(line) {
			break
		}

		// Get the residue. If it's not in our sequence map, skip it.
		residue := strings.TrimSpace(string(line[i:end]))
		if single, ok := AminoThreeToOne[residue]; ok {
			chain.Sequence = append(chain.Sequence, single)
		}
	}
}

// parseAtom loads all pertinent information from ATOM records in a PDB file.
// Currently, this only includes deducing the amino acid residue start and
// stop indices. (Note that the length of the range is not necessarily
// equivalent to the length of the amino acid sequence found in the SEQRES
// records.)
//
// ATOM records without a valid amino acid residue in columns 18-20 are ignored.
func (e *Entry) parseAtom(line []byte) {
	chain := e.getOrMakeChain(line[21])

	// An ATOM record is only processed if it corresponds to an amino acid
	// residue. (Which is in columns 17-19.)
	residue := strings.TrimSpace(string(line[17:20]))
	if _, ok := AminoThreeToOne[residue]; !ok {
		// Sanity check. I'm pretty sure that only amino acids have three
		// letter abbreviations.
		if len(residue) == 3 {
			panic(fmt.Sprintf("The residue '%s' found in PDB file '%s' has "+
				"length 3, but is not in my amino acid map.",
				residue, e.Path))
		}
		return
	}

	// The residue sequence number is in columns 22-25. Grab it, trim it,
	// and look for an integer.
	snum := strings.TrimSpace(string(line[22:26]))
	inum := int(0)
	if num, err := strconv.ParseInt(snum, 10, 32); err == nil {
		inum = int(num)
		switch {
		case chain.AtomResidueStart == 0 || inum < chain.AtomResidueStart:
			chain.AtomResidueStart = inum
		case chain.AtomResidueEnd == 0 || inum > chain.AtomResidueEnd:
			chain.AtomResidueEnd = inum
		}
	}

	// Build an Atom value. We need the serial number from columns 6-10,
	// the atom name from columns 12-15, the amino acid residue from
	// columns 17-19 (we already have that: 'residue'), the residue sequence
	// number from columns 22-25 (already have that too: 'inum'), and the
	// three dimension coordinates in columns 30-37 (x), 38-45 (y), and
	// 46-53 (z).
	atom := Atom{
		Name:       strings.TrimSpace(string(line[12:16])),
		Residue:    residue,
		ResidueInd: inum,
		Coords:     [3]float64{},
	}

	serialStr := strings.TrimSpace(string(line[6:11]))
	if serial64, err := strconv.ParseInt(serialStr, 10, 32); err == nil {
		atom.Serial = int(serial64)
	}

	xstr := strings.TrimSpace(string(line[30:38]))
	ystr := strings.TrimSpace(string(line[38:46]))
	zstr := strings.TrimSpace(string(line[46:54]))
	if x64, err := strconv.ParseFloat(xstr, 64); err == nil {
		atom.Coords[0] = x64
	}
	if y64, err := strconv.ParseFloat(ystr, 64); err == nil {
		atom.Coords[1] = y64
	}
	if z64, err := strconv.ParseFloat(zstr, 64); err == nil {
		atom.Coords[2] = z64
	}

	// Now add our atom to the chain.
	chain.Atoms = append(chain.Atoms, atom)
	if atom.Name == "CA" {
		chain.CaAtoms = append(chain.CaAtoms, atom)
	}
}

// Chain represents a protein chain or subunit in a PDB file. Each chain has
// its own identifier, amino acid sequence (if its a protein sequence), and
// the start and stop residue indices of the ATOM coordinates.
//
// It also contains a slice of all carbon-alpha ATOM records corresponding
// to an amino acid.
type Chain struct {
	entry                            *Entry
	Ident                            byte
	Sequence                         []byte
	AtomResidueStart, AtomResidueEnd int
	Atoms                            Atoms
	CaAtoms                          Atoms
}

// PDBArg is a convenience method for creating a PDBArg for a specific
// chain that can be used in the 'matt' package.
func (c *Chain) PDBArg() matt.PDBArg {
	return matt.PDBArg{
		Location: c.entry.Path,
		Chain:    c.Ident,
	}
}

// ValidProtein returns true when there are ATOM records corresponding to
// a protein backbone.
func (c *Chain) ValidProtein() bool {
	return c.AtomResidueStart > 0 && c.AtomResidueEnd > 0
}

// String returns a FASTA-like formatted string of this chain and all of its
// related information.
func (c *Chain) String() string {
	return strings.TrimSpace(
		fmt.Sprintf("> Chain %c (%d, %d) :: length %d\n%s",
			c.Ident, c.AtomResidueStart, c.AtomResidueEnd,
			len(c.Sequence), string(c.Sequence)))
}

// Atom contains information about an ATOM record, including the serial
// number, the residue (and residue sequence number), the atom name and the
// three dimensional coordinates.
type Atom struct {
	Serial     int
	Name       string
	ResidueInd int
	Residue    string

	// Coords is a triple where the first element is X, the second is Y and
	// the third is Z.
	Coords [3]float64
}

func (a Atom) String() string {
	return fmt.Sprintf("(%d, %s, %d, %s, [%0.4f %0.4f %0.4f])",
		a.Serial, a.Name, a.ResidueInd, a.Residue,
		a.Coords[0], a.Coords[1], a.Coords[2])
}

// Atoms names a slice of Atom for sorting.
type Atoms []Atom

func (as Atoms) String() string {
	lines := make([]string, len(as))
	for i, atom := range as {
		lines[i] = atom.String()
	}
	return strings.Join(lines, "\n")
}

func (as Atoms) Len() int {
	return len(as)
}

func (as Atoms) Less(i, j int) bool {
	return as[i].Serial < as[j].Serial
}

func (as Atoms) Swap(i, j int) {
	as[i], as[j] = as[j], as[i]
}
<|endoftext|>