<commit_before>// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package kube

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"reflect"
	"strings"

	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"

	multierror "github.com/hashicorp/go-multierror"

	"istio.io/manager/model"

	"k8s.io/client-go/1.5/kubernetes"
	"k8s.io/client-go/1.5/pkg/api"
	"k8s.io/client-go/1.5/pkg/api/errors"
	"k8s.io/client-go/1.5/pkg/api/unversioned"
	"k8s.io/client-go/1.5/pkg/api/v1"
	"k8s.io/client-go/1.5/pkg/apis/extensions/v1beta1"
	"k8s.io/client-go/1.5/pkg/runtime"
	"k8s.io/client-go/1.5/pkg/runtime/serializer"
	"k8s.io/client-go/1.5/rest"
	"k8s.io/client-go/1.5/tools/clientcmd"
)

const (
	// IstioAPIGroup defines Kubernetes API group for TRP
	IstioAPIGroup = "istio.io"
	// IstioResourceVersion defined Kubernetes API group version
	IstioResourceVersion = "v1"
)

// KubernetesRegistry bindings for the manager:
// - configuration objects are stored as third-party resources
type KubernetesRegistry struct {
	mapping   model.KindMap
	client    *kubernetes.Clientset
	dyn       *rest.RESTClient
	Namespace string
}

// CreateRESTConfig for cluster API server, pass empty config file for in-cluster
func CreateRESTConfig(kubeconfig string, km model.KindMap) (*rest.Config, error) {
	config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
	if err != nil {
		return nil, err
	}

	version := unversioned.GroupVersion{
		Group:   IstioAPIGroup,
		Version: IstioResourceVersion,
	}

	config.GroupVersion = &version
	config.APIPath = "/apis"
	config.ContentType = runtime.ContentTypeJSON
	config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: api.Codecs}

	schemeBuilder := runtime.NewSchemeBuilder(
		func(scheme *runtime.Scheme) error {
			scheme.AddKnownTypes(
				version,
				&api.ListOptions{},
				&api.DeleteOptions{},
			)
			for kind := range km {
				scheme.AddKnownTypeWithName(unversioned.GroupVersionKind{
					Group:   IstioAPIGroup,
					Version: IstioResourceVersion,
					Kind:    kind,
				}, &Config{})
				scheme.AddKnownTypeWithName(unversioned.GroupVersionKind{
					Group:   IstioAPIGroup,
					Version: IstioResourceVersion,
					Kind:    kind + "List",
				}, &ConfigList{})
			}
			return nil
		})
	schemeBuilder.AddToScheme(api.Scheme)

	return config, nil
}

// NewKubernetesRegistry creates a client to Kubernetes API using kubeconfig file
func NewKubernetesRegistry(kubeconfig string, km model.KindMap) (*KubernetesRegistry, error) {
	config, err := CreateRESTConfig(kubeconfig, km)
	if err != nil {
		return nil, err
	}
	cl, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	dyn, err := rest.RESTClientFor(config)
	if err != nil {
		return nil, err
	}

	out := &KubernetesRegistry{
		mapping:   km,
		client:    cl,
		dyn:       dyn,
		Namespace: api.NamespaceDefault,
	}

	if err = out.RegisterResources(); err != nil {
		return nil, err
	}

	return out, nil
}

// RegisterResources creates third party resources
func (kr *KubernetesRegistry) RegisterResources() error {
	var out error
	for kind, v := range kr.mapping {
		apiName := kindToAPIName(kind)
		// initialize resource if it does not exist
		res, err := kr.client.Extensions().ThirdPartyResources().Get(apiName)
		if err != nil {
			if errors.IsNotFound(err) {
				tpr := &v1beta1.ThirdPartyResource{
					ObjectMeta: v1.ObjectMeta{
						Name: apiName,
					},
					Versions: []v1beta1.APIVersion{
						{Name: IstioResourceVersion},
					},
					Description: v.Description,
				}
				_, err = kr.client.Extensions().ThirdPartyResources().Create(tpr)
				if err != nil {
					out = multierror.Append(out, err)
				}
			} else {
				out = multierror.Append(out, err)
			}
		} else {
			log.Printf("Resource already exists: %s", res.Name)
		}
	}
	return out
}

// DeregisterResources removes third party resources
func (kr *KubernetesRegistry) DeregisterResources() error {
	var out error
	for kind := range kr.mapping {
		apiName := kindToAPIName(kind)
		err := kr.client.Extensions().ThirdPartyResources().Delete(apiName, &api.DeleteOptions{})
		if err != nil {
			out = multierror.Append(out, err)
		}
	}
	return out
}

func (kr *KubernetesRegistry) Get(key model.ConfigKey) (*model.Config, bool) {
	config := &Config{}
	err := kr.dyn.Get().
		Namespace(kr.Namespace).
		Resource(key.Kind + "s").
		Name(encodeName(key)).
		Do().Into(config)
	if err != nil {
		log.Printf(err.Error())
		return nil, false
	}
	out, err := kr.convert(key.Kind, config)
	if err != nil {
		log.Printf(err.Error())
		return nil, false
	}
	return out, true
}

func (kr *KubernetesRegistry) Put(obj model.Config) error {
	if err := kr.mapping.ValidateConfig(obj); err != nil {
		return err
	}
	pb, _ := obj.Content.(proto.Message)

	// Marshal from proto to json bytes
	m := jsonpb.Marshaler{}
	bytes, err := m.MarshalToString(pb)
	if err != nil {
		return err
	}

	// Unmarshal from bytes to go map
	var data map[string]interface{}
	err = json.Unmarshal([]byte(bytes), &data)
	if err != nil {
		return err
	}

	out := &Config{
		Metadata: api.ObjectMeta{Name: encodeName(obj.ConfigKey)},
		Data:     data,
	}

	err = kr.dyn.Post().
		Namespace(kr.Namespace).
		Resource(obj.Kind + "s").
		Body(out).
		Do().Error()
	if err != nil {
		return err
	}

	return nil
}

func (kr *KubernetesRegistry) Delete(key model.ConfigKey) {
	err := kr.dyn.Delete().
		Namespace(kr.Namespace).
		Resource(key.Kind + "s").
		Name(encodeName(key)).
		Do().Error()
	if err != nil {
		log.Printf(err.Error())

	}
}

func (kr *KubernetesRegistry) List(kind string) []*model.Config {
	_, ok := kr.mapping[kind]
	if !ok {
		return nil
	}

	list := &ConfigList{}
	err := kr.dyn.Get().
		Namespace(kr.Namespace).
		Resource(kind + "s").
		Do().Into(list)
	if err != nil {
		log.Printf(err.Error())
		return nil
	}

	var out []*model.Config
	for _, item := range list.Items {
		elt, err := kr.convert(kind, &item)
		if err != nil {
			log.Printf(err.Error())
		}
		out = append(out, elt)
	}
	return out
}

// camelCaseToKabobCase converts "MyName" to "my-name"
func camelCaseToKabobCase(s string) string {
	var out bytes.Buffer
	for i := range s {
		if 'A' <= s[i] && s[i] <= 'Z' {
			if i > 0 {
				out.WriteByte('-')
			}
			out.WriteByte(s[i] - 'A' + 'a')
		} else {
			out.WriteByte(s[i])
		}
	}
	return out.String()
}

// kindToAPIName converts Kind name to 3rd party API group
func kindToAPIName(s string) string {
	return camelCaseToKabobCase(s) + "." + IstioAPIGroup
}

func (kr *KubernetesRegistry) convert(kind string, config *Config) (*model.Config, error) {
	pbt := proto.MessageType(kr.mapping[kind].MessageName)
	pb := reflect.New(pbt.Elem()).Interface().(proto.Message)

	// Marshal to JSON bytes
	str, err := json.Marshal(config.Data)
	if err != nil {
		return nil, err
	}

	// Unmarshal from bytes to proto
	err = jsonpb.UnmarshalString(string(str), pb)
	if err != nil {
		return nil, err
	}
	name, version := decodeName(config.GetObjectMeta().GetName())
	out := model.Config{
		ConfigKey: model.ConfigKey{
			Name:    name,
			Kind:    kind,
			Version: version,
		},
		Content: pb,
	}

	return &out, nil
}

func encodeName(key model.ConfigKey) string {
	if key.Version == "" {
		return key.Name + "-default"
	}
	return fmt.Sprintf("%s-%s", key.Name, key.Version)
}

func decodeName(kubeName string) (string, string) {
	i := strings.LastIndex(kubeName, "-")
	name := kubeName[0:i]
	version := kubeName[i+1:]
	if version == "default" {
		version = ""
	}
	return name, version
}
<commit_msg>Stylistic improvements<commit_after>// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package kube

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"reflect"
	"strings"

	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"

	multierror "github.com/hashicorp/go-multierror"

	"istio.io/manager/model"

	"k8s.io/client-go/1.5/kubernetes"
	"k8s.io/client-go/1.5/pkg/api"
	"k8s.io/client-go/1.5/pkg/api/errors"
	"k8s.io/client-go/1.5/pkg/api/unversioned"
	"k8s.io/client-go/1.5/pkg/api/v1"
	"k8s.io/client-go/1.5/pkg/apis/extensions/v1beta1"
	"k8s.io/client-go/1.5/pkg/runtime"
	"k8s.io/client-go/1.5/pkg/runtime/serializer"
	"k8s.io/client-go/1.5/rest"
	"k8s.io/client-go/1.5/tools/clientcmd"
)

const (
	// IstioAPIGroup defines Kubernetes API group for TRP
	IstioAPIGroup = "istio.io"
	// IstioResourceVersion defined Kubernetes API group version
	IstioResourceVersion = "v1"
)

// KubernetesRegistry bindings for the manager:
// - configuration objects are stored as third-party resources
type KubernetesRegistry struct {
	mapping   model.KindMap
	client    *kubernetes.Clientset
	dyn       *rest.RESTClient
	Namespace string
}

// CreateRESTConfig for cluster API server, pass empty config file for in-cluster
func CreateRESTConfig(kubeconfig string, km model.KindMap) (*rest.Config, error) {
	config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
	if err != nil {
		return nil, err
	}

	version := unversioned.GroupVersion{
		Group:   IstioAPIGroup,
		Version: IstioResourceVersion,
	}

	config.GroupVersion = &version
	config.APIPath = "/apis"
	config.ContentType = runtime.ContentTypeJSON
	config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: api.Codecs}

	schemeBuilder := runtime.NewSchemeBuilder(
		func(scheme *runtime.Scheme) error {
			scheme.AddKnownTypes(
				version,
				&api.ListOptions{},
				&api.DeleteOptions{},
			)
			for kind := range km {
				scheme.AddKnownTypeWithName(unversioned.GroupVersionKind{
					Group:   IstioAPIGroup,
					Version: IstioResourceVersion,
					Kind:    kind,
				}, &Config{})
				scheme.AddKnownTypeWithName(unversioned.GroupVersionKind{
					Group:   IstioAPIGroup,
					Version: IstioResourceVersion,
					Kind:    kind + "List",
				}, &ConfigList{})
			}
			return nil
		})
	schemeBuilder.AddToScheme(api.Scheme)

	return config, nil
}

// NewKubernetesRegistry creates a client to Kubernetes API using kubeconfig file
func NewKubernetesRegistry(kubeconfig string, km model.KindMap) (*KubernetesRegistry, error) {
	config, err := CreateRESTConfig(kubeconfig, km)
	if err != nil {
		return nil, err
	}
	cl, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	dyn, err := rest.RESTClientFor(config)
	if err != nil {
		return nil, err
	}

	out := &KubernetesRegistry{
		mapping:   km,
		client:    cl,
		dyn:       dyn,
		Namespace: api.NamespaceDefault,
	}

	if err = out.RegisterResources(); err != nil {
		return nil, err
	}

	return out, nil
}

// RegisterResources creates third party resources
func (kr *KubernetesRegistry) RegisterResources() error {
	var out error
	for kind, v := range kr.mapping {
		apiName := kindToAPIName(kind)
		res, err := kr.client.Extensions().ThirdPartyResources().Get(apiName)
		if err == nil {
			log.Printf("Resource already exists: %q", res.Name)
		} else if errors.IsNotFound(err) {
			log.Printf("Creating resource: %q", kind)
			tpr := &v1beta1.ThirdPartyResource{
				ObjectMeta:  v1.ObjectMeta{Name: apiName},
				Versions:    []v1beta1.APIVersion{{Name: IstioResourceVersion}},
				Description: v.Description,
			}
			res, err = kr.client.Extensions().ThirdPartyResources().
				Create(tpr)
			if err != nil {
				out = multierror.Append(out, err)
			} else {
				log.Printf("Created resource: %q", res.Name)
			}
		} else {
			out = multierror.Append(out, err)
		}
	}
	return out
}

// DeregisterResources removes third party resources
func (kr *KubernetesRegistry) DeregisterResources() error {
	var out error
	for kind := range kr.mapping {
		apiName := kindToAPIName(kind)
		err := kr.client.Extensions().ThirdPartyResources().
			Delete(apiName, &api.DeleteOptions{})
		if err != nil {
			out = multierror.Append(out, err)
		}
	}
	return out
}

func (kr *KubernetesRegistry) Get(key model.ConfigKey) (*model.Config, bool) {
	config := &Config{}
	err := kr.dyn.Get().
		Namespace(kr.Namespace).
		Resource(key.Kind + "s").
		Name(encodeName(key)).
		Do().Into(config)
	if err != nil {
		log.Printf(err.Error())
		return nil, false
	}
	out, err := kr.convert(key.Kind, config)
	if err != nil {
		log.Printf(err.Error())
		return nil, false
	}
	return out, true
}

func (kr *KubernetesRegistry) Put(obj model.Config) error {
	if err := kr.mapping.ValidateConfig(obj); err != nil {
		return err
	}
	pb, _ := obj.Content.(proto.Message)

	// Marshal from proto to json bytes
	m := jsonpb.Marshaler{}
	bytes, err := m.MarshalToString(pb)
	if err != nil {
		return err
	}

	// Unmarshal from json bytes to go map
	var data map[string]interface{}
	err = json.Unmarshal([]byte(bytes), &data)
	if err != nil {
		return err
	}

	out := &Config{
		Metadata: api.ObjectMeta{Name: encodeName(obj.ConfigKey)},
		Data:     data,
	}

	err = kr.dyn.Post().
		Namespace(kr.Namespace).
		Resource(obj.Kind + "s").
		Body(out).
		Do().Error()
	if err != nil {
		return err
	}

	return nil
}

func (kr *KubernetesRegistry) Delete(key model.ConfigKey) {
	err := kr.dyn.Delete().
		Namespace(kr.Namespace).
		Resource(key.Kind + "s").
		Name(encodeName(key)).
		Do().Error()
	if err != nil {
		log.Printf(err.Error())
	}
}

func (kr *KubernetesRegistry) List(kind string) []*model.Config {
	if _, ok := kr.mapping[kind]; !ok {
		return nil
	}

	list := &ConfigList{}
	err := kr.dyn.Get().
		Namespace(kr.Namespace).
		Resource(kind + "s").
		Do().Into(list)
	if err != nil {
		log.Printf(err.Error())
		return nil
	}

	var out []*model.Config
	for _, item := range list.Items {
		elt, err := kr.convert(kind, &item)
		if err != nil {
			log.Printf(err.Error())
		}
		out = append(out, elt)
	}
	return out
}

// camelCaseToKabobCase converts "MyName" to "my-name"
func camelCaseToKabobCase(s string) string {
	var out bytes.Buffer
	for i := range s {
		if 'A' <= s[i] && s[i] <= 'Z' {
			if i > 0 {
				out.WriteByte('-')
			}
			out.WriteByte(s[i] - 'A' + 'a')
		} else {
			out.WriteByte(s[i])
		}
	}
	return out.String()
}

// kindToAPIName converts Kind name to 3rd party API group
func kindToAPIName(s string) string {
	return camelCaseToKabobCase(s) + "." + IstioAPIGroup
}

func (kr *KubernetesRegistry) convert(kind string, config *Config) (*model.Config, error) {
	pbt := proto.MessageType(kr.mapping[kind].MessageName)
	pb := reflect.New(pbt.Elem()).Interface().(proto.Message)

	// Marshal to JSON bytes
	str, err := json.Marshal(config.Data)
	if err != nil {
		return nil, err
	}

	// Unmarshal from bytes to proto
	err = jsonpb.UnmarshalString(string(str), pb)
	if err != nil {
		return nil, err
	}
	name, version := decodeName(config.GetObjectMeta().GetName())
	out := model.Config{
		ConfigKey: model.ConfigKey{
			Name:    name,
			Kind:    kind,
			Version: version,
		},
		Content: pb,
	}

	return &out, nil
}

func encodeName(key model.ConfigKey) string {
	if key.Version == "" {
		return key.Name + "-default"
	}
	return fmt.Sprintf("%s-%s", key.Name, key.Version)
}

func decodeName(kubeName string) (string, string) {
	i := strings.LastIndex(kubeName, "-")
	name := kubeName[0:i]
	version := kubeName[i+1:]
	if version == "default" {
		version = ""
	}
	return name, version
}
<|endoftext|>


<commit_before>package collector

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	simpleJson "github.com/bitly/go-simplejson"
	"github.com/matsumana/flink_exporter/util"
	"strconv"
	"strings"
)

var (
	jobNames map[string]string
)

type Job struct{}

type ReadWriteMertics struct {
	JobName      string
	ReadBytes    int64
	WriteBytes   int64
	ReadRecords  int64
	WriteRecords int64
}

type ReadWriteTotalMertics struct {
	ReadBytesTotal    int64
	WriteBytesTotal   int64
	ReadRecordsTotal  int64
	WriteRecordsTotal int64
	Details           []ReadWriteMertics
}

type CheckpointMetrics struct {
	JobName  string
	Count    int64
	Duration int
	Size     int64
}

type ExceptionMetrics struct {
	JobName string
	Count   int
}

// see https://github.com/apache/flink/blob/release-1.0.3/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/JobStatus.java
// TODO Must modify, After Flink version up.
type JobStatusMetrics struct {
	JobName    string
	Created    int
	Running    int
	Failing    int
	Failed     int
	Cancelling int
	Canceled   int
	Finished   int
	Restarting int
}

func (j *Job) GetMetrics(flinkJobManagerUrl string) ([]JobStatusMetrics, ReadWriteTotalMertics, []CheckpointMetrics, []ExceptionMetrics) {
	jobNames = make(map[string]string)
	jobs := j.getJobs(flinkJobManagerUrl)
	jobStatuses := j.getJobStatus(flinkJobManagerUrl, jobs)
	readWrites := j.getReadWrite(flinkJobManagerUrl, jobs)
	checkpoints := j.getCheckpoints(flinkJobManagerUrl, jobs)
	exceptions := j.getExceptions(flinkJobManagerUrl, jobs)
	return jobStatuses, readWrites, checkpoints, exceptions
}

func (j *Job) getJobs(flinkJobManagerUrl string) []string {
	url := strings.Trim(flinkJobManagerUrl, "/") + "/jobs"
	httpClient := util.HttpClient{}
	jsonStr, err := httpClient.Get(url)
	if err != nil {
		log.Errorf("HttpClient.Get = %v", err)
		return []string{}
	}

	// parse
	js, err := simpleJson.NewJson([]byte(jsonStr))
	if err != nil {
		log.Errorf("simpleJson.NewJson = %v", err)
		return []string{}
	}

	// jobs
	var jobs []string
	jobs, err = js.Get("jobs-running").StringArray()
	if err != nil {
		log.Errorf("js.Get 'jobs-running' = %v", err)
		return []string{}
	}
	log.Debugf("jobs = %v", jobs)

	return jobs
}

func appendJobNameMap(jobId string, jobName string) {
	jobNames[jobId] = jobName
}

func (j *Job) getJobStatus(flinkJobManagerUrl string, jobs []string) []JobStatusMetrics {
	jobStatuses := []JobStatusMetrics{}
	httpClient := util.HttpClient{}
	for _, job := range jobs {
		url := strings.Trim(flinkJobManagerUrl, "/") + "/jobs/" + job
		jsonStr, err := httpClient.Get(url)
		if err != nil {
			log.Errorf("HttpClient.Get = %v", err)
			return []JobStatusMetrics{}
		}

		// parse
		js, err := simpleJson.NewJson([]byte(jsonStr))
		if err != nil {
			log.Errorf("simpleJson.NewJson = %v", err)
			return []JobStatusMetrics{}
		}

		// job name
		var jobName string
		jobName, err = js.Get("name").String()
		if err != nil {
			log.Errorf("js.Get 'name' = %v", err)
			return []JobStatusMetrics{}
		}
		log.Debugf("jobName = %v", jobName)
		appendJobNameMap(job, jobName)

		// state
		var state string
		state, err = js.Get("state").String()
		if err != nil {
			log.Errorf("js.Get 'state' = %v", err)
			return []JobStatusMetrics{}
		}
		log.Debugf("state = %v", state)

		jobStatus := JobStatusMetrics{}
		jobStatus.JobName = jobNames[job]

		switch state {
		case "CREATED":
			jobStatus.Created += 1
		case "RUNNING":
			jobStatus.Running += 1
		case "FAILING":
			jobStatus.Failing += 1
		case "FAILED":
			jobStatus.Failed += 1
		case "CANCELLING":
			jobStatus.Cancelling += 1
		case "CANCELED":
			jobStatus.Canceled += 1
		case "FINISHED":
			jobStatus.Finished += 1
		case "RESTARTING":
			jobStatus.Restarting += 1
		}

		jobStatuses = append(jobStatuses, jobStatus)
	}

	log.Debugf("jobStatuses = %v", jobStatuses)

	return jobStatuses
}

func (j *Job) getReadWrite(flinkJobManagerUrl string, jobs []string) ReadWriteTotalMertics {
	total := ReadWriteTotalMertics{}
	readWrites := []ReadWriteMertics{}
	for _, job := range jobs {
		url := strings.Trim(flinkJobManagerUrl, "/") + "/jobs/" + job
		httpClient := util.HttpClient{}
		jsonStr, err := httpClient.Get(url)
		if err != nil {
			log.Errorf("HttpClient.Get = %v", err)
			return ReadWriteTotalMertics{}
		}

		// parse
		js, err := simpleJson.NewJson([]byte(jsonStr))
		if err != nil {
			log.Errorf("simpleJson.NewJson = %v", err)
			return ReadWriteTotalMertics{}
		}

		// vertices
		var vertices []interface{}
		vertices, err = js.Get("vertices").Array()
		if err != nil {
			log.Errorf("js.Get 'vertices' = %v", err)
			return ReadWriteTotalMertics{}
		}
		log.Debugf("vertices = %v", vertices)

		readWrite := ReadWriteMertics{}
		readWrite.JobName = jobNames[job]

		for _, vertice := range vertices {
			v, _ := vertice.(map[string]interface{})
			log.Debugf("metrics = %v", v["metrics"])

			metrics, _ := v["metrics"].(map[string]interface{})
			record := ReadWriteMertics{}
			if strings.HasPrefix(fmt.Sprint(v["name"]), "Source") {
				record.WriteBytes, _ = strconv.ParseInt(fmt.Sprint(metrics["write-bytes"]), 10, 64)
				record.WriteRecords, _ = strconv.ParseInt(fmt.Sprint(metrics["write-records"]), 10, 64)
				readWrite.WriteBytes += record.WriteBytes
				readWrite.WriteRecords += record.WriteRecords
			} else {
				record.ReadBytes, _ = strconv.ParseInt(fmt.Sprint(metrics["read-bytes"]), 10, 64)
				record.ReadRecords, _ = strconv.ParseInt(fmt.Sprint(metrics["read-records"]), 10, 64)
				readWrite.ReadBytes += record.ReadBytes
				readWrite.ReadRecords += record.ReadRecords
			}
		}

		total.ReadBytesTotal += readWrite.ReadBytes
		total.ReadRecordsTotal += readWrite.ReadRecords
		total.WriteBytesTotal += readWrite.WriteBytes
		total.WriteRecordsTotal += readWrite.WriteRecords

		readWrites = append(readWrites, readWrite)
	}

	log.Debugf("readWrites = %v", readWrites)

	total.Details = readWrites

	return total
}

func (j *Job) getCheckpoints(flinkJobManagerUrl string, jobs []string) []CheckpointMetrics {
	checkpoints := []CheckpointMetrics{}
	httpClient := util.HttpClient{}
	for _, job := range jobs {
		url := strings.Trim(flinkJobManagerUrl, "/") + "/jobs/" + job + "/checkpoints"
		jsonStr, err := httpClient.Get(url)
		if err != nil {
			log.Errorf("HttpClient.Get = %v", err)
			return []CheckpointMetrics{}
		}

		// not exists checkpoint info
		if jsonStr == "{}" {
			continue
		}

		// parse
		js, err := simpleJson.NewJson([]byte(jsonStr))
		if err != nil {
			log.Errorf("simpleJson.NewJson = %v", err)
			return []CheckpointMetrics{}
		}

		checkpoint := CheckpointMetrics{}
		checkpoint.JobName = jobNames[job]

		// count
		checkpoint.Count, err = js.Get("count").Int64()
		if err != nil {
			log.Errorf("js.Get 'count' = %v", err)
			return []CheckpointMetrics{}
		}
		log.Debugf("count = %v", checkpoint.Count)

		// history
		var histories []interface{}
		histories, err = js.Get("history").Array()
		if err != nil {
			log.Errorf("js.Get 'history' = %v", err)
			return []CheckpointMetrics{}
		}
		log.Debugf("history = %v", histories)

		if len(histories) > 0 {
			latest, _ := histories[len(histories)-1].(map[string]interface{})
			checkpoint.Duration, _ = strconv.Atoi(fmt.Sprint(latest["duration"]))
			checkpoint.Size, _ = strconv.ParseInt(fmt.Sprint(latest["size"]), 10, 64)
		}

		checkpoints = append(checkpoints, checkpoint)
	}

	log.Debugf("checkpoints = %v", checkpoints)

	return checkpoints
}

func (j *Job) getExceptions(flinkJobManagerUrl string, jobs []string) []ExceptionMetrics {
	exceptions := []ExceptionMetrics{}
	httpClient := util.HttpClient{}
	for _, job := range jobs {
		url := strings.Trim(flinkJobManagerUrl, "/") + "/jobs/" + job + "/exceptions"
		jsonStr, err := httpClient.Get(url)
		if err != nil {
			log.Errorf("HttpClient.Get = %v", err)
			return []ExceptionMetrics{}
		}

		// parse
		js, err := simpleJson.NewJson([]byte(jsonStr))
		if err != nil {
			log.Errorf("simpleJson.NewJson = %v", err)
			return []ExceptionMetrics{}
		}

		// exceptions
		var allExceptions []interface{}
		allExceptions, err = js.Get("all-exceptions").Array()
		if err != nil {
			log.Errorf("js.Get 'exceptions' = %v", err)
			return []ExceptionMetrics{}
		}
		log.Debugf("allExceptions = %v", allExceptions)

		exceptions = append(exceptions,
			ExceptionMetrics{
				JobName: jobNames[job],
				Count:   len(allExceptions),
			})
	}

	log.Debugf("exceptions = %v", exceptions)

	return exceptions
}
<commit_msg>refactoring<commit_after>package collector

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	simpleJson "github.com/bitly/go-simplejson"
	"github.com/matsumana/flink_exporter/util"
	"strconv"
	"strings"
)

type ReadWriteMertics struct {
	JobName      string
	ReadBytes    int64
	WriteBytes   int64
	ReadRecords  int64
	WriteRecords int64
}

type ReadWriteTotalMertics struct {
	ReadBytesTotal    int64
	WriteBytesTotal   int64
	ReadRecordsTotal  int64
	WriteRecordsTotal int64
	Details           []ReadWriteMertics
}

type CheckpointMetrics struct {
	JobName  string
	Count    int64
	Duration int
	Size     int64
}

type ExceptionMetrics struct {
	JobName string
	Count   int
}

// see https://github.com/apache/flink/blob/release-1.0.3/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/JobStatus.java
// TODO Must modify, After Flink version up.
type JobStatusMetrics struct {
	JobName    string
	Created    int
	Running    int
	Failing    int
	Failed     int
	Cancelling int
	Canceled   int
	Finished   int
	Restarting int
}

type jobDetail struct {
	id          string
	name        string
	detail      *simpleJson.Json
	checkPoints *simpleJson.Json
	exceptions  *simpleJson.Json
}

type Job struct{}

func (j *Job) GetMetrics(flinkJobManagerUrl string) ([]JobStatusMetrics, ReadWriteTotalMertics, []CheckpointMetrics, []ExceptionMetrics) {
	jobs := j.getJobs(flinkJobManagerUrl)
	jobDetails := j.getJobDetails(flinkJobManagerUrl, jobs)
	jobStatuses := j.getJobStatus(jobDetails)
	readWrites := j.getReadWrite(jobDetails)
	checkpoints := j.getCheckpoints(jobDetails)
	exceptions := j.getExceptions(jobDetails)
	return jobStatuses, readWrites, checkpoints, exceptions
}

func (j *Job) getJobs(flinkJobManagerUrl string) []string {
	url := strings.Trim(flinkJobManagerUrl, "/") + "/jobs"
	httpClient := util.HttpClient{}
	jsonStr, err := httpClient.Get(url)
	if err != nil {
		log.Errorf("HttpClient.Get = %v", err)
		return []string{}
	}

	// parse
	js, err := simpleJson.NewJson([]byte(jsonStr))
	if err != nil {
		log.Errorf("simpleJson.NewJson = %v", err)
		return []string{}
	}

	// jobs
	var jobs []string
	jobs, err = js.Get("jobs-running").StringArray()
	if err != nil {
		log.Errorf("js.Get 'jobs-running' = %v", err)
		return []string{}
	}
	log.Debugf("jobs = %v", jobs)

	return jobs
}

func (j *Job) getJobDetails(flinkJobManagerUrl string, jobs []string) map[string]jobDetail {
	httpClient := util.HttpClient{}
	details := map[string]jobDetail{}
	details = make(map[string]jobDetail)

	// collect all metrics
	for _, job := range jobs {
		// --- detail ---------------------
		url := strings.Trim(flinkJobManagerUrl, "/") + "/jobs/" + job
		jsonStr, err := httpClient.Get(url)
		if err != nil {
			log.Errorf("HttpClient.Get = %v", err)
			continue
		}

		// parse
		js, err := simpleJson.NewJson([]byte(jsonStr))
		if err != nil {
			log.Errorf("simpleJson.NewJson = %v", err)
			continue
		}

		// job name
		var jobName string
		jobName, err = js.Get("name").String()
		if err != nil {
			log.Errorf("js.Get 'name' = %v", err)
			continue
		}
		log.Debugf("jobName = %v", jobName)

		detail := jobDetail{}
		detail.id = job
		detail.name = jobName
		detail.detail = js

		// --- checkpoints ---------------------
		url = strings.Trim(flinkJobManagerUrl, "/") + "/jobs/" + job + "/checkpoints"
		jsonStr, err = httpClient.Get(url)
		if err != nil {
			log.Errorf("HttpClient.Get = %v", err)
			continue
		}

		// parse when exists checkpoints
		if jsonStr != "{}" {
			js, err = simpleJson.NewJson([]byte(jsonStr))
			if err != nil {
				log.Errorf("simpleJson.NewJson = %v", err)
				continue
			}
			detail.checkPoints = js
		}

		// --- exceptions ---------------------
		url = strings.Trim(flinkJobManagerUrl, "/") + "/jobs/" + job + "/exceptions"
		jsonStr, err = httpClient.Get(url)
		if err != nil {
			log.Errorf("HttpClient.Get = %v", err)
			continue
		}

		// parse
		js, err = simpleJson.NewJson([]byte(jsonStr))
		if err != nil {
			log.Errorf("simpleJson.NewJson = %v", err)
			continue
		}
		detail.exceptions = js

		details[detail.id] = detail
	}
	log.Debugf("jobDetails = %v", details)

	return details
}

func (j *Job) getJobStatus(jobDetails map[string]jobDetail) []JobStatusMetrics {
	jobStatuses := []JobStatusMetrics{}
	for _, jobDetail := range jobDetails {
		// state
		var state string
		state, err := jobDetail.detail.Get("state").String()
		if err != nil {
			log.Errorf("js.Get 'state' = %v", err)
			return []JobStatusMetrics{}
		}
		log.Debugf("state = %v", state)

		jobStatus := JobStatusMetrics{}
		jobStatus.JobName = jobDetail.name

		switch state {
		case "CREATED":
			jobStatus.Created += 1
		case "RUNNING":
			jobStatus.Running += 1
		case "FAILING":
			jobStatus.Failing += 1
		case "FAILED":
			jobStatus.Failed += 1
		case "CANCELLING":
			jobStatus.Cancelling += 1
		case "CANCELED":
			jobStatus.Canceled += 1
		case "FINISHED":
			jobStatus.Finished += 1
		case "RESTARTING":
			jobStatus.Restarting += 1
		}

		jobStatuses = append(jobStatuses, jobStatus)
	}

	log.Debugf("jobStatuses = %v", jobStatuses)

	return jobStatuses
}

func (j *Job) getReadWrite(jobDetails map[string]jobDetail) ReadWriteTotalMertics {
	total := ReadWriteTotalMertics{}
	readWrites := []ReadWriteMertics{}
	for _, jobDetail := range jobDetails {
		// vertices
		var vertices []interface{}
		vertices, err := jobDetail.detail.Get("vertices").Array()
		if err != nil {
			log.Errorf("js.Get 'vertices' = %v", err)
			return ReadWriteTotalMertics{}
		}
		log.Debugf("vertices = %v", vertices)

		readWrite := ReadWriteMertics{}
		readWrite.JobName = jobDetail.name

		for _, vertice := range vertices {
			v, _ := vertice.(map[string]interface{})
			log.Debugf("metrics = %v", v["metrics"])

			metrics, _ := v["metrics"].(map[string]interface{})
			record := ReadWriteMertics{}
			if strings.HasPrefix(fmt.Sprint(v["name"]), "Source") {
				record.WriteBytes, _ = strconv.ParseInt(fmt.Sprint(metrics["write-bytes"]), 10, 64)
				record.WriteRecords, _ = strconv.ParseInt(fmt.Sprint(metrics["write-records"]), 10, 64)
				readWrite.WriteBytes += record.WriteBytes
				readWrite.WriteRecords += record.WriteRecords
			} else {
				record.ReadBytes, _ = strconv.ParseInt(fmt.Sprint(metrics["read-bytes"]), 10, 64)
				record.ReadRecords, _ = strconv.ParseInt(fmt.Sprint(metrics["read-records"]), 10, 64)
				readWrite.ReadBytes += record.ReadBytes
				readWrite.ReadRecords += record.ReadRecords
			}
		}

		total.ReadBytesTotal += readWrite.ReadBytes
		total.ReadRecordsTotal += readWrite.ReadRecords
		total.WriteBytesTotal += readWrite.WriteBytes
		total.WriteRecordsTotal += readWrite.WriteRecords

		readWrites = append(readWrites, readWrite)
	}

	log.Debugf("readWrites = %v", readWrites)

	total.Details = readWrites

	return total
}

func (j *Job) getCheckpoints(jobDetails map[string]jobDetail) []CheckpointMetrics {
	checkpoints := []CheckpointMetrics{}
	for _, jobDetail := range jobDetails {
		checkpoint := CheckpointMetrics{}
		checkpoint.JobName = jobDetail.name
		if jobDetail.checkPoints != nil {
			// count
			var count int64
			count, err := jobDetail.checkPoints.Get("count").Int64()
			if err != nil {
				log.Errorf("js.Get 'count' = %v", err)
				return []CheckpointMetrics{}
			}
			log.Debugf("count = %v", count)

			checkpoint.Count = count

			// history
			var histories []interface{}
			histories, err = jobDetail.checkPoints.Get("history").Array()
			if err != nil {
				log.Errorf("js.Get 'history' = %v", err)
				return []CheckpointMetrics{}
			}
			log.Debugf("history = %v", histories)

			if len(histories) > 0 {
				latest, _ := histories[len(histories)-1].(map[string]interface{})
				checkpoint.Duration, _ = strconv.Atoi(fmt.Sprint(latest["duration"]))
				checkpoint.Size, _ = strconv.ParseInt(fmt.Sprint(latest["size"]), 10, 64)
			}
		}

		checkpoints = append(checkpoints, checkpoint)
	}

	log.Debugf("checkpoints = %v", checkpoints)

	return checkpoints
}

func (j *Job) getExceptions(jobDetails map[string]jobDetail) []ExceptionMetrics {
	exceptions := []ExceptionMetrics{}
	for _, jobDetail := range jobDetails {
		// exceptions
		var allExceptions []interface{}
		allExceptions, err := jobDetail.exceptions.Get("all-exceptions").Array()
		if err != nil {
			log.Errorf("js.Get 'all-exceptions' = %v", err)
			return []ExceptionMetrics{}
		}
		log.Debugf("allExceptions = %v", allExceptions)

		exceptions = append(exceptions,
			ExceptionMetrics{
				JobName: jobDetail.name,
				Count:   len(allExceptions),
			})
	}

	log.Debugf("exceptions = %v", exceptions)

	return exceptions
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
)

func sumList(inputList []interface{}) float64 {
	total := 0.0
	for _, v := range inputList {
		switch vType := v.(type) {
		case float64:
			total += v.(float64)
		case []interface{}:
			total += sumList(v.([]interface{}))
		case map[string]interface{}:
			total += sumMap(v.(map[string]interface{}))
		case string: // Don't care.
		default:
			panic(fmt.Sprintf("Unexpected type %T with value \"%v\"\n", vType, v))
		}
	}
	return total
}

func sumMap(inputMap map[string]interface{}) float64 {
	total := 0.0
	for _, v := range inputMap {
		switch vType := v.(type) {
		case float64:
			total += v.(float64)
		case []interface{}:
			total += sumList(v.([]interface{}))
		case map[string]interface{}:
			total += sumMap(v.(map[string]interface{}))
		case string: // Don't care.
		default:
			panic(fmt.Sprintf("Unexpected type %T with value \"%v\"\n", vType, v))

		}
	}
	return total
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func main() {
	file, e := ioutil.ReadFile("input.json")
	check(e)
	var dat map[string]interface{}
	err := json.Unmarshal(file, &dat)
	check(err)
	log.Printf("%v", sumMap(dat))
}
<commit_msg>Day 12 part 2 solved.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
)

func sumList(inputList []interface{}) float64 {
	total := 0.0
	for _, v := range inputList {
		switch vType := v.(type) {
		case float64:
			total += v.(float64)
		case []interface{}:
			total += sumList(v.([]interface{}))
		case map[string]interface{}:
			total += sumMap(v.(map[string]interface{}))
		case string: // Don't care.
		default:
			panic(fmt.Sprintf("Unexpected type %T with value \"%v\"\n", vType, v))
		}
	}
	return total
}

func sumMap(inputMap map[string]interface{}) float64 {
	total := 0.0
	for k, v := range inputMap {
		if k == "red" {
			continue
		}
		switch vType := v.(type) {
		case float64:
			total += v.(float64)
		case []interface{}:
			total += sumList(v.([]interface{}))
		case map[string]interface{}:
			total += sumMap(v.(map[string]interface{}))
		case string:
			if v.(string) == "red" {
				return 0
			}
		default:
			panic(fmt.Sprintf("Unexpected type %T with value \"%v\"\n", vType, v))

		}
	}
	return total
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func main() {
	file, e := ioutil.ReadFile("input.json")
	check(e)
	var dat map[string]interface{}
	err := json.Unmarshal(file, &dat)
	check(err)
	log.Printf("%v", sumMap(dat))
}
<|endoftext|>


<commit_before>package goinsta

import (
	"encoding/json"
	"fmt"
	"strconv"
)

// Item represents media items
type Item struct {
	TakenAt          int     `json:"taken_at"`
	ID               int64   `json:"pk"`
	IDStr            string  `json:"id"`
	DeviceTimestamp  int64   `json:"device_timestamp"`
	MediaType        int     `json:"media_type"`
	Code             string  `json:"code"`
	ClientCacheKey   string  `json:"client_cache_key"`
	FilterType       int     `json:"filter_type"`
	CarouselParentID string  `json:"carousel_parent_id"`
	CarouselMedia    []Item  `json:"carousel_media,omitempty"`
	User             User    `json:"user"`
	CanViewerReshare bool    `json:"can_viewer_reshare"`
	Caption          Caption `json:"caption"`
	CaptionIsEdited  bool    `json:"caption_is_edited"`
	Likes            int     `json:"like_count"`
	HasLiked         bool    `json:"has_liked"`
	// TopLikers can be multiple data
	TopLikersStr                 string `json:"top_likers,string"`
	TopLikers                    []User `json:"top_likers"`
	CommentLikesEnabled          bool   `json:"comment_likes_enabled"`
	CommentThreadingEnabled      bool   `json:"comment_threading_enabled"`
	HasMoreComments              bool   `json:"has_more_comments"`
	MaxNumVisiblePreviewComments int    `json:"max_num_visible_preview_comments"`
	// PreviewComments can be `string` or `[]string`
	PreviewComments      []interface{} `json:"preview_comments,omitempty"`
	CommentCount         int           `json:"comment_count"`
	PhotoOfYou           bool          `json:"photo_of_you"`
	Usertags             Tag           `json:"usertags,omitempty"`
	FbUserTags           Tag           `json:"fb_user_tags"`
	CanViewerSave        bool          `json:"can_viewer_save"`
	OrganicTrackingToken string        `json:"organic_tracking_token"`
	Images               Images        `json:"image_versions2,omitempty"`
	OriginalWidth        int           `json:"original_width,omitempty"`
	OriginalHeight       int           `json:"original_height,omitempty"`
	ImportedTakenAt      int           `json:"imported_taken_at,omitempty"`

	// Only for stories
	StoryEvents              []interface{} `json:"story_events"`
	StoryHashtags            []interface{} `json:"story_hashtags"`
	StoryPolls               []interface{} `json:"story_polls"`
	StoryFeedMedia           []interface{} `json:"story_feed_media"`
	StorySoundOn             []interface{} `json:"story_sound_on"`
	CreativeConfig           interface{}   `json:"creative_config"`
	StoryLocations           []interface{} `json:"story_locations"`
	StorySliders             []interface{} `json:"story_sliders"`
	StoryQuestions           []interface{} `json:"story_questions"`
	StoryProductItems        []interface{} `json:"story_product_items"`
	SupportsReelReactions    bool          `json:"supports_reel_reactions"`
	ShowOneTapFbShareTooltip bool          `json:"show_one_tap_fb_share_tooltip"`
	HasSharedToFb            int           `json:"has_shared_to_fb"`
	Mentions                 []Mentions
	Videos                   []Videos `json:"video_versions,omitempty"`
	HasAudio                 bool     `json:"has_audio,omitempty"`
	VideoDuration            float64  `json:"video_duration,omitempty"`
	IsDashEligible           int      `json:"is_dash_eligible,omitempty"`
	VideoDashManifest        string   `json:"video_dash_manifest,omitempty"`
	NumberOfQualities        int      `json:"number_of_qualities,omitempty"`
}

type Media interface {
	Next() error
}

type StoryMedia struct {
	inst     *Instagram
	endpoint string
	uid      int64

	ID              int      `json:"id"`
	LatestReelMedia int      `json:"latest_reel_media"`
	ExpiringAt      int      `json:"expiring_at"`
	Seen            float64  `json:"seen"`
	CanReply        bool     `json:"can_reply"`
	CanReshare      bool     `json:"can_reshare"`
	ReelType        string   `json:"reel_type"`
	User            User     `json:"user"`
	Items           []Item   `json:"items"`
	ReelMentions    []string `json:"reel_mentions"`
	PrefetchCount   int      `json:"prefetch_count"`
	HasBestiesMedia int      `json:"has_besties_media"`
	Status          string   `json:"status"`
}

// Next allows to paginate after calling:
// User.Stories
func (media *StoryMedia) Next() (err error) {
	var body []byte
	insta := media.inst
	endpoint := media.endpoint

	body, err = insta.sendSimpleRequest(
		endpoint, media.uid,
	)
	if err == nil {
		m := StoryMedia{}
		err = json.Unmarshal(body, &m)
		if err == nil {
			err = ErrNoMore
			*media = m
			media.inst = insta
			media.endpoint = endpoint
			// TODO check NextID media
		}
	}
	return err
}

// Media represent a set of media items
type FeedMedia struct {
	inst *Instagram

	uid      int64
	endpoint string

	Items               []Item `json:"items"`
	NumResults          int    `json:"num_results"`
	MoreAvailable       bool   `json:"more_available"`
	AutoLoadMoreEnabled bool   `json:"auto_load_more_enabled"`
	Status              string `json:"status"`
	NextID              int64  `json:"next_max_id"`
	NextIDStr           string `json:"next_max_id,string"`
}

// Next allows to paginate after calling:
// User.Feed
//
// returns ErrNoMore when list reach the end.
func (media *FeedMedia) Next() (err error) {
	var body []byte
	insta := media.inst
	endpoint := media.endpoint
	next := ""

	switch {
	case media.NextID == 0:
		next = media.NextIDStr
	case media.NextIDStr == "":
		next = strconv.FormatInt(media.NextID, 10)
	}

	if media.uid != 0 {
		endpoint = fmt.Sprintf(endpoint, media.uid)
	}

	body, err = insta.sendRequest(
		&reqOptions{
			Endpoint: endpoint,
			Query: map[string]string{
				"max_id":         next,
				"rank_token":     insta.rankToken,
				"min_timestamp":  "",
				"ranked_content": "true",
			},
		},
	)
	if err == nil {
		m := FeedMedia{}
		err = json.Unmarshal(body, &m)
		if err == nil {
			*media = m
			media.inst = insta
			media.endpoint = endpoint
			if m.NextID == 0 || m.MoreAvailable {
				err = ErrNoMore
			}
		}
	}
	return err
}
<commit_msg>Change to interface{}<commit_after>package goinsta

import (
	"encoding/json"
	"fmt"
	"strconv"
)

// Item represents media items
type Item struct {
	TakenAt          int     `json:"taken_at"`
	ID               int64   `json:"pk"`
	IDStr            string  `json:"id"`
	DeviceTimestamp  int64   `json:"device_timestamp"`
	MediaType        int     `json:"media_type"`
	Code             string  `json:"code"`
	ClientCacheKey   string  `json:"client_cache_key"`
	FilterType       int     `json:"filter_type"`
	CarouselParentID string  `json:"carousel_parent_id"`
	CarouselMedia    []Item  `json:"carousel_media,omitempty"`
	User             User    `json:"user"`
	CanViewerReshare bool    `json:"can_viewer_reshare"`
	Caption          Caption `json:"caption"`
	CaptionIsEdited  bool    `json:"caption_is_edited"`
	Likes            int     `json:"like_count"`
	HasLiked         bool    `json:"has_liked"`
	// TopLikers can be multiple data
	TopLikersStr                 string `json:"top_likers,string"`
	TopLikers                    []User `json:"top_likers"`
	CommentLikesEnabled          bool   `json:"comment_likes_enabled"`
	CommentThreadingEnabled      bool   `json:"comment_threading_enabled"`
	HasMoreComments              bool   `json:"has_more_comments"`
	MaxNumVisiblePreviewComments int    `json:"max_num_visible_preview_comments"`
	// PreviewComments can be `string` or `[]string`
	PreviewComments      []interface{} `json:"preview_comments,omitempty"`
	CommentCount         int           `json:"comment_count"`
	PhotoOfYou           bool          `json:"photo_of_you"`
	Usertags             Tag           `json:"usertags,omitempty"`
	FbUserTags           Tag           `json:"fb_user_tags"`
	CanViewerSave        bool          `json:"can_viewer_save"`
	OrganicTrackingToken string        `json:"organic_tracking_token"`
	Images               Images        `json:"image_versions2,omitempty"`
	OriginalWidth        int           `json:"original_width,omitempty"`
	OriginalHeight       int           `json:"original_height,omitempty"`
	ImportedTakenAt      int           `json:"imported_taken_at,omitempty"`

	// Only for stories
	StoryEvents              []interface{} `json:"story_events"`
	StoryHashtags            []interface{} `json:"story_hashtags"`
	StoryPolls               []interface{} `json:"story_polls"`
	StoryFeedMedia           []interface{} `json:"story_feed_media"`
	StorySoundOn             []interface{} `json:"story_sound_on"`
	CreativeConfig           interface{}   `json:"creative_config"`
	StoryLocations           []interface{} `json:"story_locations"`
	StorySliders             []interface{} `json:"story_sliders"`
	StoryQuestions           []interface{} `json:"story_questions"`
	StoryProductItems        []interface{} `json:"story_product_items"`
	SupportsReelReactions    bool          `json:"supports_reel_reactions"`
	ShowOneTapFbShareTooltip bool          `json:"show_one_tap_fb_share_tooltip"`
	HasSharedToFb            int           `json:"has_shared_to_fb"`
	Mentions                 []Mentions
	Videos                   []Videos `json:"video_versions,omitempty"`
	HasAudio                 bool     `json:"has_audio,omitempty"`
	VideoDuration            float64  `json:"video_duration,omitempty"`
	IsDashEligible           int      `json:"is_dash_eligible,omitempty"`
	VideoDashManifest        string   `json:"video_dash_manifest,omitempty"`
	NumberOfQualities        int      `json:"number_of_qualities,omitempty"`
}

type Media interface {
	Next() error
}

type StoryMedia struct {
	inst     *Instagram
	endpoint string
	uid      int64

	ID              int      `json:"id"`
	LatestReelMedia int      `json:"latest_reel_media"`
	ExpiringAt      int      `json:"expiring_at"`
	Seen            float64  `json:"seen"`
	CanReply        bool     `json:"can_reply"`
	CanReshare      bool     `json:"can_reshare"`
	ReelType        string   `json:"reel_type"`
	User            User     `json:"user"`
	Items           []Item   `json:"items"`
	ReelMentions    []string `json:"reel_mentions"`
	PrefetchCount   int      `json:"prefetch_count"`
	HasBestiesMedia int      `json:"has_besties_media"`
	Status          string   `json:"status"`
}

// Next allows to paginate after calling:
// User.Stories
func (media *StoryMedia) Next() (err error) {
	var body []byte
	insta := media.inst
	endpoint := media.endpoint

	body, err = insta.sendSimpleRequest(
		endpoint, media.uid,
	)
	if err == nil {
		m := StoryMedia{}
		err = json.Unmarshal(body, &m)
		if err == nil {
			err = ErrNoMore
			*media = m
			media.inst = insta
			media.endpoint = endpoint
			// TODO check NextID media
		}
	}
	return err
}

// Media represent a set of media items
type FeedMedia struct {
	inst *Instagram

	uid      int64
	endpoint string

	Items               []Item `json:"items"`
	NumResults          int    `json:"num_results"`
	MoreAvailable       bool   `json:"more_available"`
	AutoLoadMoreEnabled bool   `json:"auto_load_more_enabled"`
	Status              string `json:"status"`
	// Can be int64 and string
	// this is why recomend Next() usage :')
	NextID interface{} `json:"next_max_id"`
}

// Next allows to paginate after calling:
// User.Feed
//
// returns ErrNoMore when list reach the end.
func (media *FeedMedia) Next() (err error) {
	var body []byte
	insta := media.inst
	endpoint := media.endpoint
	next := ""

	switch s := media.NextID.(type) {
	case string:
		next = s
	case int64:
		next = strconv.FormatInt(s, 10)
	}

	if media.uid != 0 {
		endpoint = fmt.Sprintf(endpoint, media.uid)
	}

	body, err = insta.sendRequest(
		&reqOptions{
			Endpoint: endpoint,
			Query: map[string]string{
				"max_id":         next,
				"rank_token":     insta.rankToken,
				"min_timestamp":  "",
				"ranked_content": "true",
			},
		},
	)
	if err == nil {
		m := FeedMedia{}
		err = json.Unmarshal(body, &m)
		if err == nil {
			*media = m
			media.inst = insta
			media.endpoint = endpoint
			if m.NextID == 0 || !m.MoreAvailable {
				err = ErrNoMore
			}
		}
	}
	return err
}
<|endoftext|>


<commit_before>package main

import (
  . "github.com/franela/goblin"

  "errors"
  "testing"
)

func Test(t *testing.T) {
  g := Goblin(t)

  g.Describe("Run", func() {
    extensionError := errors.New("run could not determine how to run this file because it does not have a known extension")

    g.Describe(".command_for_file", func() {
      g.Describe("when a filename is given with a known extension", func() {
        g.It("should be a valid command", func() {
          command, err := commandForFile("hello.rb")
          g.Assert(command).Equal("ruby hello.rb")
          g.Assert(err).Equal(nil)
        })
      })

      g.Describe("when a filename is given without a known extension", func() {
        g.It("should return an error", func() {
          _, err := commandForFile("hello.unknown")
          g.Assert(err).Equal(extensionError)
        })
      })

      g.Describe("when a filename is given without any extension", func() {
        g.It("should return an error", func() {
          _, err := commandForFile("hello")
          g.Assert(err).Equal(extensionError)
        })
      })
    })
  })
}
<commit_msg>Switch from goblin to ginkgo for testing<commit_after>package main

import (
  . "github.com/onsi/ginkgo"
  . "github.com/onsi/gomega"

  "errors"
  "testing"
)

func Test(t *testing.T) {
  RegisterFailHandler(Fail)
  RunSpecs(t, "Run")
}

var _ = Describe("Run", func() {
  extensionError := errors.New("run could not determine how to run this file because it does not have a known extension")

  Describe(".command_for_file", func() {
    Context("when a filename is given with a known extension", func() {
      It("should be a valid command", func() {
        command, err := commandForFile("hello.rb")
        Expect(command).To(Equal("ruby hello.rb"))
        Expect(err).To(BeNil())
      })
    })

    Context("when a filename is given without a known extension", func() {
      It("should return an error", func() {
        _, err := commandForFile("hello.unknown")
        Expect(err).To(Equal(extensionError))
      })
    })

    Context("when a filename is given without any extension", func() {
      It("should return an error", func() {
        _, err := commandForFile("hello")
        Expect(err).To(Equal(extensionError))
      })
    })
  })
})
<|endoftext|>


<commit_before>package command

import (
	"flag"
	"fmt"
	"strings"

	"github.com/mitchellh/cli"
	"github.com/mohae/rancher/ranchr"
)

// BuildCommand is a Command implementation that generates Packer templates
// from named named builds and passed build arguments.
type BuildCommand struct {
	Ui cli.Ui
}

// Rancher help text.
func (c *BuildCommand) Help() string {
	helpText := `
Usage: rancher build [options]

Rancher creates Packer templates. At minimum, this command needs to be run with
either the -distro flag or a build name. The simplest way to generate a Packer
template is to run Rancher with just the target distribution name, which must
be supported, i.e. exists within Rancher's supported.toml file:

	% rancher build -distro=<distro name>
	% rancher build -distro=ubuntu

The above command generates a Packer template, targeting Ubuntu, using the
defaults for that distribution. See the options section for the other flags.

Rancher can also generate Packer templates using preconfigured Rancher build
templates via the builds.toml file. The name of the build is used to specify
which build configuration should be used:

	% rancher build <buildName...>
	% rancher build 1204-amd64-server 1404-amd64-desktop

The above command generates two Packer templates using the 1204-amd64-server
and 1404-amd64-desktop build configurations. The list of build names is
variadic, accepting 1 or more build names.

For builds using the -distro flag, the -arch, -image, and -release flags are
optional. If any of them are missing, the distribution's default value for that
flag will be used.

Options:
-distro=<distroName>	If provided, Rancher will create a Packer template for
			the passed distro, e.g. ubuntu. This flag can be used
			with the -arch, -image, and -release flags to override
			the distro's default values for those settings.

-arch=<architecture>	Specify whether 32 or 64 bit code should be used. These
			values are distro dependent. This flag is only used
			with the -distro flag.

-image=<imageType>	The ISO image that the Packer template will use, e.g.
			server or desktop. These values are distro dependent.
			This flag is only used with the -distro flag.

-release=<releaseNum>	The release number that the Packer template will use,
			e.g. 12.04, etc. Only the targeted distro's currently
			supported releases are valid. This flag is only used
			with the -distro flag.
`
	return strings.TrimSpace(helpText)
}

func (c *BuildCommand) Run(args []string) int {
	var distroFilter, archFilter, imageFilter, releaseFilter, logDirFilter string
	cmdFlags := flag.NewFlagSet("build", flag.ContinueOnError)
	cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
	cmdFlags.StringVar(&distroFilter, "distro", "", "distro filter")
	cmdFlags.StringVar(&archFilter, "arch", "", "arch filter")
	cmdFlags.StringVar(&imageFilter, "image", "", "image filter")
	cmdFlags.StringVar(&releaseFilter, "release", "", "release filter")
	cmdFlags.StringVar(&logDirFilter, "log_dir", "", "log directory")
	if err := cmdFlags.Parse(args); err != nil {
		c.Ui.Error(fmt.Sprintf("Parse of command-line arguments failed: %s", err))
		return 1
	}
	buildArgs := cmdFlags.Args()
	if distroFilter != "" {
		args := ranchr.ArgsFilter{Arch: archFilter, Distro: distroFilter, Image: imageFilter, Release: releaseFilter}
		// TODO go it
		if err := ranchr.BuildDistro(args); err != nil {
			c.Ui.Output(err.Error())
			return 1
		}
	}
	if len(buildArgs) > 0 {
		var message string
		var err error
		if message, err = ranchr.BuildBuilds(buildArgs...); err != nil {
			c.Ui.Error(err.Error())
			return 1
		}
		c.Ui.Output(message)
	}
	c.Ui.Output("Rancher Build complete.")
	return 0
}

func (c *BuildCommand) Synopsis() string {
	return "Create a Packer template from either distribution defaults or pre-defined Rancher Build templates."
}
<commit_msg>updated build synopsis<commit_after>package command

import (
	"flag"
	"fmt"
	"strings"

	"github.com/mitchellh/cli"
	"github.com/mohae/rancher/ranchr"
)

// BuildCommand is a Command implementation that generates Packer templates
// from named named builds and passed build arguments.
type BuildCommand struct {
	Ui cli.Ui
}

// Rancher help text.
func (c *BuildCommand) Help() string {
	helpText := `
Usage: rancher build [options]

Rancher creates Packer templates. At minimum, this command needs to be run with
either the -distro flag or a build name. The simplest way to generate a Packer
template is to run Rancher with just the target distribution name, which must
be supported, i.e. exists within Rancher's supported.toml file:

	% rancher build -distro=<distro name>
	% rancher build -distro=ubuntu

The above command generates a Packer template, targeting Ubuntu, using the
defaults for that distribution. See the options section for the other flags.

Rancher can also generate Packer templates using preconfigured Rancher build
templates via the builds.toml file. The name of the build is used to specify
which build configuration should be used:

	% rancher build <buildName...>
	% rancher build 1204-amd64-server 1404-amd64-desktop

The above command generates two Packer templates using the 1204-amd64-server
and 1404-amd64-desktop build configurations. The list of build names is
variadic, accepting 1 or more build names.

For builds using the -distro flag, the -arch, -image, and -release flags are
optional. If any of them are missing, the distribution's default value for that
flag will be used.

Options:
-distro=<distroName>	If provided, Rancher will create a Packer template for
			the passed distro, e.g. ubuntu. This flag can be used
			with the -arch, -image, and -release flags to override
			the distro's default values for those settings.

-arch=<architecture>	Specify whether 32 or 64 bit code should be used. These
			values are distro dependent. This flag is only used
			with the -distro flag.

-image=<imageType>	The ISO image that the Packer template will use, e.g.
			server or desktop. These values are distro dependent.
			This flag is only used with the -distro flag.

-release=<releaseNum>	The release number that the Packer template will use,
			e.g. 12.04, etc. Only the targeted distro's currently
			supported releases are valid. This flag is only used
			with the -distro flag.
`
	return strings.TrimSpace(helpText)
}

func (c *BuildCommand) Run(args []string) int {
	var distroFilter, archFilter, imageFilter, releaseFilter, logDirFilter string
	cmdFlags := flag.NewFlagSet("build", flag.ContinueOnError)
	cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
	cmdFlags.StringVar(&distroFilter, "distro", "", "distro filter")
	cmdFlags.StringVar(&archFilter, "arch", "", "arch filter")
	cmdFlags.StringVar(&imageFilter, "image", "", "image filter")
	cmdFlags.StringVar(&releaseFilter, "release", "", "release filter")
	cmdFlags.StringVar(&logDirFilter, "log_dir", "", "log directory")
	if err := cmdFlags.Parse(args); err != nil {
		c.Ui.Error(fmt.Sprintf("Parse of command-line arguments failed: %s", err))
		return 1
	}
	buildArgs := cmdFlags.Args()
	if distroFilter != "" {
		args := ranchr.ArgsFilter{Arch: archFilter, Distro: distroFilter, Image: imageFilter, Release: releaseFilter}
		if err := ranchr.BuildDistro(args); err != nil {
			c.Ui.Output(err.Error())
			return 1
		}
	}
	if len(buildArgs) > 0 {
		var message string
		var err error
		if message, err = ranchr.BuildBuilds(buildArgs...); err != nil {
			c.Ui.Error(err.Error())
			return 1
		}
		c.Ui.Output(message)
	}
	c.Ui.Output("Rancher Build complete.")
	return 0
}

func (c *BuildCommand) Synopsis() string {
	return "Create a Packer template from either supported distro defaults or pre-defined Rancher build configurations."
}
<|endoftext|>


<commit_before>package command

import (
	"flag"
	"fmt"
	"github.com/hashicorp/serf/client"
	"github.com/hashicorp/serf/command/agent"
	"github.com/mitchellh/cli"
	"strings"
	"time"
)

// QueryCommand is a Command implementation that is used to trigger a new
// query and wait for responses and acks
type QueryCommand struct {
	ShutdownCh <-chan struct{}
	Ui         cli.Ui
}

func (c *QueryCommand) Help() string {
	helpText := `
Usage: serf query [options] name payload

  Dispatches a query to the Serf cluster.

Options:

  -node=NAME                This flag can be provided multiple times to filter
                            responses to only named nodes.

  -tag key=regexp           This flag can be provided multiple times to filter
                            responses to only nodes matching the tags

  -timeout="15s"            Providing a timeout overrides the default timeout

  -no-ack                   Setting this prevents nodes from sending an acknowledgement
                            of the query

  -rpc-addr=127.0.0.1:7373  RPC address of the Serf agent.

  -rpc-auth=""              RPC auth token of the Serf agent.
`
	return strings.TrimSpace(helpText)
}

func (c *QueryCommand) Run(args []string) int {
	var noAck bool
	var nodes []string
	var tags []string
	var timeout time.Duration
	cmdFlags := flag.NewFlagSet("event", flag.ContinueOnError)
	cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
	cmdFlags.Var((*agent.AppendSliceValue)(&nodes), "node", "node filter")
	cmdFlags.Var((*agent.AppendSliceValue)(&tags), "tag", "tag filter")
	cmdFlags.DurationVar(&timeout, "timeout", 0, "query timeout")
	cmdFlags.BoolVar(&noAck, "no-ack", false, "no-ack")
	rpcAddr := RPCAddrFlag(cmdFlags)
	rpcAuth := RPCAuthFlag(cmdFlags)
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	// Setup the filter tags
	filterTags, err := agent.UnmarshalTags(tags)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", err))
		return 1
	}

	args = cmdFlags.Args()
	if len(args) < 1 {
		c.Ui.Error("A query name must be specified.")
		c.Ui.Error("")
		c.Ui.Error(c.Help())
		return 1
	} else if len(args) > 2 {
		c.Ui.Error("Too many command line arguments. Only a name and payload must be specified.")
		c.Ui.Error("")
		c.Ui.Error(c.Help())
		return 1
	}

	name := args[0]
	var payload []byte
	if len(args) == 2 {
		payload = []byte(args[1])
	}

	cl, err := RPCClient(*rpcAddr, *rpcAuth)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error connecting to Serf agent: %s", err))
		return 1
	}
	defer cl.Close()

	ackCh := make(chan string, 128)
	respCh := make(chan client.NodeResponse, 128)

	params := client.QueryParam{
		FilterNodes: nodes,
		FilterTags:  filterTags,
		RequestAck:  !noAck,
		Timeout:     timeout,
		Name:        name,
		Payload:     payload,
		AckCh:       ackCh,
		RespCh:      respCh,
	}
	if err := cl.Query(&params); err != nil {
		c.Ui.Error(fmt.Sprintf("Error sending query: %s", err))
		return 1
	}
	c.Ui.Output(fmt.Sprintf("Query '%s' dispatched", name))

	// Track responses and acknowledgements
	numAcks := 0
	numResp := 0

OUTER:
	for {
		select {
		case a := <-ackCh:
			if a == "" {
				break OUTER
			}
			numAcks++
			c.Ui.Info(fmt.Sprintf("Ack from '%s'", a))

		case r := <-respCh:
			if r.From == "" {
				break OUTER
			}
			numResp++

			// Remove the trailing newline if there is one
			payload := r.Payload
			if n := len(payload); n > 0 && payload[n-1] == '\n' {
				payload = payload[:n-1]
			}

			c.Ui.Info(fmt.Sprintf("Response from '%s': %s", r.From, payload))

		case <-c.ShutdownCh:
			return 1
		}
	}

	if !noAck {
		c.Ui.Output(fmt.Sprintf("Total Acks: %d", numAcks))
	}
	c.Ui.Output(fmt.Sprintf("Total Responses: %d", numResp))
	return 0
}

func (c *QueryCommand) Synopsis() string {
	return "Send a query to the Serf cluster"
}
<commit_msg>command/query: Refactor output handling into interface<commit_after>package command

import (
	"flag"
	"fmt"
	"github.com/hashicorp/serf/client"
	"github.com/hashicorp/serf/command/agent"
	"github.com/mitchellh/cli"
	"strings"
	"time"
)

// QueryCommand is a Command implementation that is used to trigger a new
// query and wait for responses and acks
type QueryCommand struct {
	ShutdownCh <-chan struct{}
	Ui         cli.Ui
}

func (c *QueryCommand) Help() string {
	helpText := `
Usage: serf query [options] name payload

  Dispatches a query to the Serf cluster.

Options:

  -node=NAME                This flag can be provided multiple times to filter
                            responses to only named nodes.

  -tag key=regexp           This flag can be provided multiple times to filter
                            responses to only nodes matching the tags

  -timeout="15s"            Providing a timeout overrides the default timeout

  -no-ack                   Setting this prevents nodes from sending an acknowledgement
                            of the query

  -rpc-addr=127.0.0.1:7373  RPC address of the Serf agent.

  -rpc-auth=""              RPC auth token of the Serf agent.
`
	return strings.TrimSpace(helpText)
}

func (c *QueryCommand) Run(args []string) int {
	var noAck bool
	var nodes []string
	var tags []string
	var timeout time.Duration
	cmdFlags := flag.NewFlagSet("event", flag.ContinueOnError)
	cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
	cmdFlags.Var((*agent.AppendSliceValue)(&nodes), "node", "node filter")
	cmdFlags.Var((*agent.AppendSliceValue)(&tags), "tag", "tag filter")
	cmdFlags.DurationVar(&timeout, "timeout", 0, "query timeout")
	cmdFlags.BoolVar(&noAck, "no-ack", false, "no-ack")
	rpcAddr := RPCAddrFlag(cmdFlags)
	rpcAuth := RPCAuthFlag(cmdFlags)
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	// Setup the filter tags
	filterTags, err := agent.UnmarshalTags(tags)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", err))
		return 1
	}

	args = cmdFlags.Args()
	if len(args) < 1 {
		c.Ui.Error("A query name must be specified.")
		c.Ui.Error("")
		c.Ui.Error(c.Help())
		return 1
	} else if len(args) > 2 {
		c.Ui.Error("Too many command line arguments. Only a name and payload must be specified.")
		c.Ui.Error("")
		c.Ui.Error(c.Help())
		return 1
	}

	name := args[0]
	var payload []byte
	if len(args) == 2 {
		payload = []byte(args[1])
	}

	cl, err := RPCClient(*rpcAddr, *rpcAuth)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error connecting to Serf agent: %s", err))
		return 1
	}
	defer cl.Close()

	// Setup the the response handler
	var handler queryRespFormat
	handler = &textQueryRespFormat{
		ui:    c.Ui,
		name:  name,
		noAck: noAck,
	}

	ackCh := make(chan string, 128)
	respCh := make(chan client.NodeResponse, 128)

	params := client.QueryParam{
		FilterNodes: nodes,
		FilterTags:  filterTags,
		RequestAck:  !noAck,
		Timeout:     timeout,
		Name:        name,
		Payload:     payload,
		AckCh:       ackCh,
		RespCh:      respCh,
	}
	if err := cl.Query(&params); err != nil {
		c.Ui.Error(fmt.Sprintf("Error sending query: %s", err))
		return 1
	}
	handler.Started()

OUTER:
	for {
		select {
		case a := <-ackCh:
			if a == "" {
				break OUTER
			}
			handler.AckReceived(a)

		case r := <-respCh:
			if r.From == "" {
				break OUTER
			}
			handler.ResponseReceived(r)

		case <-c.ShutdownCh:
			return 1
		}
	}

	handler.Finished()
	return 0
}

func (c *QueryCommand) Synopsis() string {
	return "Send a query to the Serf cluster"
}

// queryRespFormat is used to switch our handler based on the format
type queryRespFormat interface {
	Started()
	AckReceived(from string)
	ResponseReceived(resp client.NodeResponse)
	Finished()
}

type textQueryRespFormat struct {
	ui      cli.Ui
	name    string
	noAck   bool
	numAcks int
	numResp int
}

func (t *textQueryRespFormat) Started() {
	t.ui.Output(fmt.Sprintf("Query '%s' dispatched", t.name))
}

func (t *textQueryRespFormat) AckReceived(from string) {
	t.numAcks++
	t.ui.Info(fmt.Sprintf("Ack from '%s'", from))
}

func (t *textQueryRespFormat) ResponseReceived(r client.NodeResponse) {
	t.numResp++

	// Remove the trailing newline if there is one
	payload := r.Payload
	if n := len(payload); n > 0 && payload[n-1] == '\n' {
		payload = payload[:n-1]
	}

	t.ui.Info(fmt.Sprintf("Response from '%s': %s", r.From, payload))
}

func (t *textQueryRespFormat) Finished() {
	if !t.noAck {
		t.ui.Output(fmt.Sprintf("Total Acks: %d", t.numAcks))
	}
	t.ui.Output(fmt.Sprintf("Total Responses: %d", t.numResp))
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"io"
	"os"
	"path/filepath"

	"../conio"
	"../dos"
	. "../interpreter"
)

func cmd_copy(cmd *Interpreter) (ErrorLevel, error) {
	return cmd_xxxx(cmd.Args,
		cmd.Stderr,
		func(src, dst string) error {
			return dos.Copy(src, dst, false)
		},
		false)
}

func cmd_move(cmd *Interpreter) (ErrorLevel, error) {
	return cmd_xxxx(cmd.Args,
		cmd.Stderr,
		func(src, dst string) error {
			return dos.Move(src, dst)
		},
		true)
}

func cmd_ln(cmd *Interpreter) (ErrorLevel, error) {
	if len(cmd.Args) >= 2 && cmd.Args[1] == "-s" {
		args := make([]string, 0, len(cmd.Args)-1)
		args = append(args, cmd.Args[0])
		args = append(args, cmd.Args[2:]...)
		return cmd_xxxx(
			args,
			cmd.Stderr,
			func(src, dst string) error {
				return os.Symlink(src, dst)
			},
			true)
	} else {
		return cmd_xxxx(
			cmd.Args,
			cmd.Stderr,
			func(src, dst string) error {
				return os.Link(src, dst)
			},
			false)
	}
}

func cmd_xxxx(args []string,
	out io.Writer,
	action func(src, dst string) error,
	isDirOk bool) (ErrorLevel, error) {
	if len(args) <= 2 {
		fmt.Fprintf(out,
			"Usage: %s [/y] SOURCE-FILENAME DESITINATE-FILENAME\n"+
				"       %s [/y] FILENAMES... DESINATE-DIRECTORY\n",
			args[0], args[0])
		return NOERROR, nil
	}
	fi, err := os.Stat(args[len(args)-1])
	isDir := err == nil && fi.Mode().IsDir()
	all := false
	for i, n := 1, len(args)-1; i < n; i++ {
		if args[i] == "/y" {
			all = true
			continue
		}
		src := args[i]
		dst := args[n]
		if isDir {
			dst = dos.Join(dst, filepath.Base(src))
		}
		if !isDirOk {
			fi, err := os.Stat(src)
			if err == nil && fi.Mode().IsDir() {
				fmt.Fprintf(out, "%s is directory and passed.\n", src)
				continue
			}
		}

		fmt.Fprintf(out, "%s -> %s\n", src, dst)
		if !all {
			fi, err := os.Stat(dst)
			if fi != nil && err == nil {
				fmt.Fprintf(out,
					"%s: override? [Yes/No/All/Quit] ",
					dst)
				ch := conio.GetCh()
				fmt.Fprintf(out, "%c\n", ch)
				switch ch {
				case 'y', 'Y':

				case 'a', 'A':
					all = true
				case 'q', 'Q':
					return NOERROR, nil
				default:
					continue
				}
			}
		}
		err := action(src, dst)
		if err != nil {
			return ErrorLevel(1), err
		}
	}
	return NOERROR, nil
}
<commit_msg>Query continue or not when copy/move failed on one of parameters.<commit_after>package commands

import (
	"fmt"
	"io"
	"os"
	"path/filepath"

	"../conio"
	"../dos"
	. "../interpreter"
)

func cmd_copy(cmd *Interpreter) (ErrorLevel, error) {
	return cmd_xxxx(cmd.Args,
		cmd.Stderr,
		func(src, dst string) error {
			return dos.Copy(src, dst, false)
		},
		false)
}

func cmd_move(cmd *Interpreter) (ErrorLevel, error) {
	return cmd_xxxx(cmd.Args,
		cmd.Stderr,
		func(src, dst string) error {
			return dos.Move(src, dst)
		},
		true)
}

func cmd_ln(cmd *Interpreter) (ErrorLevel, error) {
	if len(cmd.Args) >= 2 && cmd.Args[1] == "-s" {
		args := make([]string, 0, len(cmd.Args)-1)
		args = append(args, cmd.Args[0])
		args = append(args, cmd.Args[2:]...)
		return cmd_xxxx(
			args,
			cmd.Stderr,
			func(src, dst string) error {
				return os.Symlink(src, dst)
			},
			true)
	} else {
		return cmd_xxxx(
			cmd.Args,
			cmd.Stderr,
			func(src, dst string) error {
				return os.Link(src, dst)
			},
			false)
	}
}

func cmd_xxxx(args []string,
	out io.Writer,
	action func(src, dst string) error,
	isDirOk bool) (ErrorLevel, error) {
	if len(args) <= 2 {
		fmt.Fprintf(out,
			"Usage: %s [/y] SOURCE-FILENAME DESITINATE-FILENAME\n"+
				"       %s [/y] FILENAMES... DESINATE-DIRECTORY\n",
			args[0], args[0])
		return NOERROR, nil
	}
	fi, err := os.Stat(args[len(args)-1])
	isDir := err == nil && fi.Mode().IsDir()
	all := false
	for i, n := 1, len(args)-1; i < n; i++ {
		if args[i] == "/y" {
			all = true
			continue
		}
		src := args[i]
		dst := args[n]
		if isDir {
			dst = dos.Join(dst, filepath.Base(src))
		}
		if !isDirOk {
			fi, err := os.Stat(src)
			if err == nil && fi.Mode().IsDir() {
				fmt.Fprintf(out, "%s is directory and passed.\n", src)
				continue
			}
		}

		fmt.Fprintf(out, "%s -> %s\n", src, dst)
		if !all {
			fi, err := os.Stat(dst)
			if fi != nil && err == nil {
				fmt.Fprintf(out,
					"%s: override? [Yes/No/All/Quit] ",
					dst)
				ch := conio.GetCh()
				fmt.Fprintf(out, "%c\n", ch)
				switch ch {
				case 'y', 'Y':

				case 'a', 'A':
					all = true
				case 'q', 'Q':
					return NOERROR, nil
				default:
					continue
				}
			}
		}
		err := action(src, dst)
		if err != nil {
			if i == n-1 {
				return ErrorLevel(1), err
			}
			fmt.Fprintf(out, "%s\nContinue? [Yes/No] ", err.Error())
			ch := conio.GetCh()
			fmt.Fprintf(out, "%c\n", ch)
			switch ch {
			case 'y', 'Y':

			default:
				return NOERROR, nil
			}
		}
	}
	return NOERROR, nil
}
<|endoftext|>


<commit_before>package common

import (
	"database/sql"
	"fmt"
	"reflect"
	"strings"

	"github.com/admpub/nging/v4/application/dbschema"
	"github.com/admpub/null"
	"github.com/webx-top/db"
	"github.com/webx-top/db/lib/factory"
	"github.com/webx-top/echo"
	"github.com/webx-top/echo/param"
)

func NewSQLQueryLimit(ctx echo.Context, offset int, limit int, linkIDs ...int) *SQLQuery {
	var linkID int
	if len(linkIDs) > 0 {
		linkID = linkIDs[0]
	}
	return &SQLQuery{
		ctx:    ctx,
		link:   linkID,
		offset: offset,
		limit:  limit,
		cacher: factory.GetCacher(),
	}
}

func NewSQLQuery(ctx echo.Context) *SQLQuery {
	return &SQLQuery{
		ctx:    ctx,
		cacher: factory.GetCacher(),
	}
}

type SQLQuery struct {
	ctx      echo.Context
	link     int
	offset   int
	limit    int
	sorts    []interface{}
	cacheKey string
	cacheTTL int64
	cacher   factory.Cacher
}

func (s *SQLQuery) LinkID(dbLinkID int) *SQLQuery {
	s.link = dbLinkID
	return s
}

func (s *SQLQuery) LinkName(dbLinkName string) *SQLQuery {
	s.link = factory.IndexByName(dbLinkName)
	return s
}

func (s *SQLQuery) Limit(limit int) *SQLQuery {
	s.limit = limit
	return s
}

func (s *SQLQuery) Offset(offset int) *SQLQuery {
	s.offset = offset
	return s
}

func (s *SQLQuery) OrderBy(sorts ...interface{}) *SQLQuery {
	s.sorts = sorts
	return s
}

func (s *SQLQuery) CacheKey(cacheKey string) *SQLQuery {
	s.cacheKey = cacheKey
	return s
}

func (s *SQLQuery) CacheTTL(ttlSeconds int64) *SQLQuery {
	s.cacheTTL = ttlSeconds
	return s
}

func (s *SQLQuery) repair(query string) string { //[link1] SELECT ...
	query = strings.TrimSpace(query)
	if len(query) < 3 || query[0] != '[' {
		return query
	}
	_query := query[1:]
	qs := strings.SplitN(_query, `]`, 2)
	if len(qs) != 2 {
		return query
	}
	linkName := qs[0]
	if len(linkName) > 0 {
		s.link = factory.IndexByName(linkName)
	}
	query = strings.TrimLeft(qs[1], ` `)
	return query
}

type SetContext interface {
	SetContext(echo.Context)
}

func (s *SQLQuery) query(recv interface{}, fn func() error, args ...interface{}) error {
	if s.cacher != nil && len(s.cacheKey) > 0 {
		cacheKey := s.cacheKey + fmt.Sprintf(`.%d.%d`, s.offset, s.limit) + `:` + strings.TrimSuffix(fmt.Sprintf(`%T`, recv), ` {}`)
		if len(args) > 0 {
			format := strings.Repeat(`%+v,`, len(args))
			cacheKey += `:args(` + fmt.Sprintf(format, args...) + `)`
		}
		//cacheKey=com.Md5(cacheKey)
		defer func() {
			if sc, ok := recv.(SetContext); ok {
				sc.SetContext(s.ctx)
			}
		}()
		return s.cacher.Do(`SQLQuery.`+cacheKey, recv, fn, s.cacheTTL)
	}
	return fn()
}

// GetValue 查询单个字段值
func (s *SQLQuery) GetValue(recv interface{}, query string, args ...interface{}) error {
	fn := func() error {
		query = s.repair(query)
		row := factory.NewParam().SetIndex(s.link).DB().QueryRow(query, args...)
		err := row.Scan(recv)
		if err != nil {
			if err == sql.ErrNoRows {
				err = nil
			}
		}
		return err
	}
	return s.query(recv, fn, args...)
}

func (s *SQLQuery) GetString(query string, args ...interface{}) (null.String, error) {
	result := null.String{}
	err := s.GetValue(&result, query, args...)
	return result, err
}

func (s *SQLQuery) GetUint(query string, args ...interface{}) (null.Uint, error) {
	result := null.Uint{}
	err := s.GetValue(&result, query, args...)
	return result, err
}

func (s *SQLQuery) GetUint32(query string, args ...interface{}) (null.Uint32, error) {
	result := null.Uint32{}
	err := s.GetValue(&result, query, args...)
	return result, err
}

func (s *SQLQuery) GetUint64(query string, args ...interface{}) (null.Uint64, error) {
	result := null.Uint64{}
	err := s.GetValue(&result, query, args...)
	return result, err
}

func (s *SQLQuery) GetInt(query string, args ...interface{}) (null.Int, error) {
	result := null.Int{}
	err := s.GetValue(&result, query, args...)
	return result, err
}

func (s *SQLQuery) GetInt32(query string, args ...interface{}) (null.Int32, error) {
	result := null.Int32{}
	err := s.GetValue(&result, query, args...)
	return result, err
}

func (s *SQLQuery) GetInt64(query string, args ...interface{}) (null.Int64, error) {
	result := null.Int64{}
	err := s.GetValue(&result, query, args...)
	return result, err
}

func (s *SQLQuery) GetFloat64(query string, args ...interface{}) (null.Float64, error) {
	result := null.Float64{}
	err := s.GetValue(&result, query, args...)
	return result, err
}

func (s *SQLQuery) GetModel(structName string, args ...interface{}) (factory.Model, error) {
	m := dbschema.DBI.NewModel(structName, s.link)
	m.SetContext(s.ctx)
	if len(args) == 0 {
		return m, nil
	}
	fn := func() error {
		err := m.Get(func(r db.Result) db.Result {
			return r.OrderBy(s.sorts...)
		}, args...)
		if err != nil {
			if err == sql.ErrNoRows {
				err = nil
			}
		}
		return err
	}
	return m, s.query(m, fn, args...)
}

func ModelObjects(m factory.Model) []interface{} {
	rv := reflect.ValueOf(m).MethodByName(`Objects`)
	if rv.Kind() != reflect.Func {
		return nil
	}
	values := rv.Call(nil)
	rows := make([]interface{}, len(values))
	for index, value := range values {
		rows[index] = value.Interface()
	}
	return rows
}

func (s *SQLQuery) GetModels(structName string, args ...interface{}) ([]interface{}, error) {
	if len(args) == 0 {
		return nil, nil
	}
	var results []interface{}
	fn := func() error {
		m := dbschema.DBI.NewModel(structName, s.link)
		m.SetContext(s.ctx)
		cond := db.NewCompounds()
		var k string
		for i, j := 0, len(args)-1; i <= j; i++ {
			if i%2 == 0 {
				k = param.AsString(args[i])
				continue
			}
			cond.AddKV(k, args[i])
		}
		_, err := m.ListByOffset(nil, func(r db.Result) db.Result {
			return r.OrderBy(s.sorts...)
		}, s.offset, s.limit, cond.And())
		results = ModelObjects(m)
		return err
	}

	return results, s.query(&results, fn, args...)
}

func (s *SQLQuery) GetModelsWithPaging(structName string, args ...interface{}) ([]interface{}, error) {
	if len(args) == 0 {
		return nil, nil
	}
	var results []interface{}
	fn := func() error {
		m := dbschema.DBI.NewModel(structName, s.link)
		m.SetContext(s.ctx)
		cond := db.NewCompounds()
		var k string
		for i, j := 0, len(args)-1; i <= j; i++ {
			if i%2 == 0 {
				k = param.AsString(args[i])
				continue
			}
			cond.AddKV(k, args[i])
		}
		err := m.ListPage(cond, s.sorts...)
		if err != nil {
			return err
		}
		results = ModelObjects(m)
		return nil
	}
	return results, s.query(&results, fn, args...)
}

func (s *SQLQuery) MustGetString(query string, args ...interface{}) null.String {
	result, err := s.GetString(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetUint(query string, args ...interface{}) null.Uint {
	result, err := s.GetUint(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetUint32(query string, args ...interface{}) null.Uint32 {
	result, err := s.GetUint32(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetUint64(query string, args ...interface{}) null.Uint64 {
	result, err := s.GetUint64(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetInt(query string, args ...interface{}) null.Int {
	result, err := s.GetInt(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetInt32(query string, args ...interface{}) null.Int32 {
	result, err := s.GetInt32(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetInt64(query string, args ...interface{}) null.Int64 {
	result, err := s.GetInt64(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetFloat64(query string, args ...interface{}) null.Float64 {
	result, err := s.GetFloat64(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetModel(structName string, args ...interface{}) factory.Model {
	result, err := s.GetModel(structName, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetModels(structName string, args ...interface{}) []interface{} {
	result, err := s.GetModels(structName, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetModelsWithPaging(structName string, args ...interface{}) []interface{} {
	result, err := s.GetModelsWithPaging(structName, args...)
	if err != nil {
		panic(err)
	}
	return result
}

// GetRow 查询一行多个字段值
func (s *SQLQuery) GetRow(query string, args ...interface{}) (null.StringMap, error) {
	result := null.StringMap{}
	fn := func() error {
		query = s.repair(query)
		rows, err := factory.NewParam().SetIndex(s.link).DB().Query(query, args...)
		if err != nil {
			return err
		}
		defer rows.Close()
		columns, err := rows.Columns()
		if err != nil {
			return err
		}
		size := len(columns)
		if rows.Next() {
			values := make([]interface{}, size)
			for k := range columns {
				values[k] = &null.String{}
			}
			err = rows.Scan(values...)
			if err != nil {
				if err == sql.ErrNoRows {
					err = nil
				}
				return err
			}
			for k, colName := range columns {
				result[colName] = *values[k].(*null.String)
			}
		}
		return err
	}
	return result, s.query(&result, fn, args...)
}

func (s *SQLQuery) MustGetRow(query string, args ...interface{}) null.StringMap {
	result, err := s.GetRow(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}

// GetRows 查询多行
func (s *SQLQuery) GetRows(query string, args ...interface{}) (null.StringMapSlice, error) {
	result := null.StringMapSlice{}
	fn := func() error {
		query = s.repair(query)
		rows, err := factory.NewParam().SetIndex(s.link).DB().Query(query, args...)
		if err != nil {
			return err
		}
		defer rows.Close()
		columns, err := rows.Columns()
		if err != nil {
			return err
		}
		size := len(columns)
		read := func(rows *sql.Rows) error {
			values := make([]interface{}, size)
			for k := range columns {
				values[k] = &null.String{}
			}
			err := rows.Scan(values...)
			if err != nil {
				return err
			}
			v := null.StringMap{}
			for k, colName := range columns {
				v[colName] = *values[k].(*null.String)
			}
			result = append(result, v)
			return nil
		}
		if s.limit > 0 {
			for i := 0; i < s.limit && rows.Next(); i++ {
				err = read(rows)
				if err != nil {
					if err == sql.ErrNoRows {
						err = nil
					}
					return err
				}
			}
		} else {
			for rows.Next() {
				err = read(rows)
				if err != nil {
					if err == sql.ErrNoRows {
						err = nil
					}
					return err
				}
			}
		}
		return err
	}
	return result, s.query(&result, fn, args...)
}

func (s *SQLQuery) MustGetRows(query string, args ...interface{}) null.StringMapSlice {
	result, err := s.GetRows(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}
<commit_msg>update<commit_after>package common

import (
	"database/sql"
	"fmt"
	"reflect"
	"strings"

	"github.com/admpub/nging/v4/application/dbschema"
	"github.com/admpub/null"
	"github.com/webx-top/db"
	"github.com/webx-top/db/lib/factory"
	"github.com/webx-top/echo"
	"github.com/webx-top/echo/param"
)

func NewSQLQueryLimit(ctx echo.Context, offset int, limit int, linkIDs ...int) *SQLQuery {
	var linkID int
	if len(linkIDs) > 0 {
		linkID = linkIDs[0]
	}
	return &SQLQuery{
		ctx:    ctx,
		link:   linkID,
		offset: offset,
		limit:  limit,
		cacher: factory.GetCacher(),
	}
}

func NewSQLQuery(ctx echo.Context) *SQLQuery {
	return &SQLQuery{
		ctx:    ctx,
		cacher: factory.GetCacher(),
	}
}

type SQLQuery struct {
	ctx      echo.Context
	link     int
	offset   int
	limit    int
	sorts    []interface{}
	cacheKey string
	cacheTTL int64
	cacher   factory.Cacher
}

func (s *SQLQuery) LinkID(dbLinkID int) *SQLQuery {
	s.link = dbLinkID
	return s
}

func (s *SQLQuery) LinkName(dbLinkName string) *SQLQuery {
	s.link = factory.IndexByName(dbLinkName)
	return s
}

func (s *SQLQuery) Limit(limit int) *SQLQuery {
	s.limit = limit
	return s
}

func (s *SQLQuery) Offset(offset int) *SQLQuery {
	s.offset = offset
	return s
}

func (s *SQLQuery) OrderBy(sorts ...interface{}) *SQLQuery {
	s.sorts = sorts
	return s
}

func (s *SQLQuery) CacheKey(cacheKey string) *SQLQuery {
	s.cacheKey = cacheKey
	return s
}

func (s *SQLQuery) CacheTTL(ttlSeconds int64) *SQLQuery {
	s.cacheTTL = ttlSeconds
	return s
}

func (s *SQLQuery) repair(query string) string { //[link1] SELECT ...
	query = strings.TrimSpace(query)
	if len(query) < 3 || query[0] != '[' {
		return query
	}
	_query := query[1:]
	qs := strings.SplitN(_query, `]`, 2)
	if len(qs) != 2 {
		return query
	}
	linkName := qs[0]
	if len(linkName) > 0 {
		s.link = factory.IndexByName(linkName)
	}
	query = strings.TrimLeft(qs[1], ` `)
	return query
}

type SetContext interface {
	SetContext(echo.Context)
}

func (s *SQLQuery) query(recv interface{}, fn func() error, args ...interface{}) error {
	if s.cacher != nil && len(s.cacheKey) > 0 {
		cacheKey := s.cacheKey + fmt.Sprintf(`.%d.%d`, s.offset, s.limit) + `:` + strings.TrimSuffix(fmt.Sprintf(`%T`, recv), ` {}`)
		if len(args) > 0 {
			format := strings.Repeat(`%+v,`, len(args))
			cacheKey += `:args(` + fmt.Sprintf(format, args...) + `)`
		}
		//cacheKey=com.Md5(cacheKey)
		defer func() {
			if sc, ok := recv.(SetContext); ok {
				sc.SetContext(s.ctx)
			}
		}()
		return s.cacher.Do(`SQLQuery.`+cacheKey, recv, fn, s.cacheTTL)
	}
	return fn()
}

// GetValue 查询单个字段值
func (s *SQLQuery) GetValue(recv interface{}, query string, args ...interface{}) error {
	fn := func() error {
		query = s.repair(query)
		row := factory.NewParam().SetIndex(s.link).DB().QueryRow(query, args...)
		err := row.Scan(recv)
		if err != nil {
			if err == sql.ErrNoRows {
				err = nil
			}
		}
		return err
	}
	return s.query(recv, fn, args...)
}

func (s *SQLQuery) GetString(query string, args ...interface{}) (null.String, error) {
	result := null.String{}
	err := s.GetValue(&result, query, args...)
	return result, err
}

func (s *SQLQuery) GetUint(query string, args ...interface{}) (null.Uint, error) {
	result := null.Uint{}
	err := s.GetValue(&result, query, args...)
	return result, err
}

func (s *SQLQuery) GetUint32(query string, args ...interface{}) (null.Uint32, error) {
	result := null.Uint32{}
	err := s.GetValue(&result, query, args...)
	return result, err
}

func (s *SQLQuery) GetUint64(query string, args ...interface{}) (null.Uint64, error) {
	result := null.Uint64{}
	err := s.GetValue(&result, query, args...)
	return result, err
}

func (s *SQLQuery) GetInt(query string, args ...interface{}) (null.Int, error) {
	result := null.Int{}
	err := s.GetValue(&result, query, args...)
	return result, err
}

func (s *SQLQuery) GetInt32(query string, args ...interface{}) (null.Int32, error) {
	result := null.Int32{}
	err := s.GetValue(&result, query, args...)
	return result, err
}

func (s *SQLQuery) GetInt64(query string, args ...interface{}) (null.Int64, error) {
	result := null.Int64{}
	err := s.GetValue(&result, query, args...)
	return result, err
}

func (s *SQLQuery) GetFloat64(query string, args ...interface{}) (null.Float64, error) {
	result := null.Float64{}
	err := s.GetValue(&result, query, args...)
	return result, err
}

func (s *SQLQuery) GetModel(structName string, args ...interface{}) (factory.Model, error) {
	m := dbschema.DBI.NewModel(structName, s.link)
	m.SetContext(s.ctx)
	if len(args) == 0 {
		return m, nil
	}
	fn := func() error {
		err := m.Get(func(r db.Result) db.Result {
			return r.OrderBy(s.sorts...)
		}, args...)
		if err != nil {
			if err == sql.ErrNoRows {
				err = nil
			}
		}
		return err
	}
	return m, s.query(m, fn, args...)
}

func ModelObjects(m factory.Model) []interface{} {
	rv := reflect.ValueOf(m).MethodByName(`Objects`)
	if rv.Kind() != reflect.Func {
		return nil
	}
	values := rv.Call(nil)
	rows := make([]interface{}, len(values))
	for index, value := range values {
		rows[index] = value.Interface()
	}
	return rows
}

func (s *SQLQuery) GetModels(structName string, args ...interface{}) ([]interface{}, error) {
	if len(args) == 0 {
		return nil, nil
	}
	var results []interface{}
	fn := func() error {
		m := dbschema.DBI.NewModel(structName, s.link)
		m.SetContext(s.ctx)
		cond := db.NewCompounds()
		var k string
		for i, j := 0, len(args)-1; i <= j; i++ {
			if i%2 == 0 {
				k = param.AsString(args[i])
				continue
			}
			cond.AddKV(k, args[i])
		}
		_, err := m.ListByOffset(nil, func(r db.Result) db.Result {
			return r.OrderBy(s.sorts...)
		}, s.offset, s.limit, cond.And())
		if err != nil {
			return err
		}
		results = ModelObjects(m)
		return err
	}

	return results, s.query(&results, fn, args...)
}

func (s *SQLQuery) GetModelsWithPaging(structName string, args ...interface{}) ([]interface{}, error) {
	if len(args) == 0 {
		return nil, nil
	}
	var results []interface{}
	fn := func() error {
		m := dbschema.DBI.NewModel(structName, s.link)
		m.SetContext(s.ctx)
		cond := db.NewCompounds()
		var k string
		for i, j := 0, len(args)-1; i <= j; i++ {
			if i%2 == 0 {
				k = param.AsString(args[i])
				continue
			}
			cond.AddKV(k, args[i])
		}
		err := m.ListPage(cond, s.sorts...)
		if err != nil {
			return err
		}
		results = ModelObjects(m)
		return nil
	}
	return results, s.query(&results, fn, args...)
}

func (s *SQLQuery) MustGetString(query string, args ...interface{}) null.String {
	result, err := s.GetString(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetUint(query string, args ...interface{}) null.Uint {
	result, err := s.GetUint(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetUint32(query string, args ...interface{}) null.Uint32 {
	result, err := s.GetUint32(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetUint64(query string, args ...interface{}) null.Uint64 {
	result, err := s.GetUint64(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetInt(query string, args ...interface{}) null.Int {
	result, err := s.GetInt(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetInt32(query string, args ...interface{}) null.Int32 {
	result, err := s.GetInt32(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetInt64(query string, args ...interface{}) null.Int64 {
	result, err := s.GetInt64(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetFloat64(query string, args ...interface{}) null.Float64 {
	result, err := s.GetFloat64(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetModel(structName string, args ...interface{}) factory.Model {
	result, err := s.GetModel(structName, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetModels(structName string, args ...interface{}) []interface{} {
	result, err := s.GetModels(structName, args...)
	if err != nil {
		panic(err)
	}
	return result
}

func (s *SQLQuery) MustGetModelsWithPaging(structName string, args ...interface{}) []interface{} {
	result, err := s.GetModelsWithPaging(structName, args...)
	if err != nil {
		panic(err)
	}
	return result
}

// GetRow 查询一行多个字段值
func (s *SQLQuery) GetRow(query string, args ...interface{}) (null.StringMap, error) {
	result := null.StringMap{}
	fn := func() error {
		query = s.repair(query)
		rows, err := factory.NewParam().SetIndex(s.link).DB().Query(query, args...)
		if err != nil {
			return err
		}
		defer rows.Close()
		columns, err := rows.Columns()
		if err != nil {
			return err
		}
		size := len(columns)
		if rows.Next() {
			values := make([]interface{}, size)
			for k := range columns {
				values[k] = &null.String{}
			}
			err = rows.Scan(values...)
			if err != nil {
				if err == sql.ErrNoRows {
					err = nil
				}
				return err
			}
			for k, colName := range columns {
				result[colName] = *values[k].(*null.String)
			}
		}
		return err
	}
	return result, s.query(&result, fn, args...)
}

func (s *SQLQuery) MustGetRow(query string, args ...interface{}) null.StringMap {
	result, err := s.GetRow(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}

// GetRows 查询多行
func (s *SQLQuery) GetRows(query string, args ...interface{}) (null.StringMapSlice, error) {
	result := null.StringMapSlice{}
	fn := func() error {
		query = s.repair(query)
		rows, err := factory.NewParam().SetIndex(s.link).DB().Query(query, args...)
		if err != nil {
			return err
		}
		defer rows.Close()
		columns, err := rows.Columns()
		if err != nil {
			return err
		}
		size := len(columns)
		read := func(rows *sql.Rows) error {
			values := make([]interface{}, size)
			for k := range columns {
				values[k] = &null.String{}
			}
			err := rows.Scan(values...)
			if err != nil {
				return err
			}
			v := null.StringMap{}
			for k, colName := range columns {
				v[colName] = *values[k].(*null.String)
			}
			result = append(result, v)
			return nil
		}
		if s.limit > 0 {
			for i := 0; i < s.limit && rows.Next(); i++ {
				err = read(rows)
				if err != nil {
					if err == sql.ErrNoRows {
						err = nil
					}
					return err
				}
			}
		} else {
			for rows.Next() {
				err = read(rows)
				if err != nil {
					if err == sql.ErrNoRows {
						err = nil
					}
					return err
				}
			}
		}
		return err
	}
	return result, s.query(&result, fn, args...)
}

func (s *SQLQuery) MustGetRows(query string, args ...interface{}) null.StringMapSlice {
	result, err := s.GetRows(query, args...)
	if err != nil {
		panic(err)
	}
	return result
}
<|endoftext|>