func NewQingStorService() (storage.Servicer, error) {
	return qingstor.NewServicer(getQsServicePairs()...)
}
NewQingStorService will create a new qingstor service.
package utils
import (
	"errors"
	"fmt"
	"os"
	"strings"
	"github.com/aos-dev/go-service-fs"
	"github.com/aos-dev/go-service-qingstor"
	"github.com/aos-dev/go-storage/v2"
	"github.com/aos-dev/go-storage/v2/pkg/credential"
	"github.com/aos-dev/go-storage/v2/pkg/endpoint"
	typ "github.com/aos-dev/go-storage/v2/types"
	"github.com/aos-dev/go-storage/v2/types/pairs"
	"github.com/qingstor/noah/pkg/types"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/viper"
	"github.com/qingstor/qsctl/v2/constants"
)
var ErrStoragerTypeInvalid = errors.New("storager type no valid")
var ErrInvalidFlow = errors.New("invalid flow")
type StoragerType = string
func ParseFlow(src, dst string) (flow constants.FlowType) {
	if dst == "" {
		return constants.FlowAtRemote
	}
	if strings.HasPrefix(src, "qs://") == strings.HasPrefix(dst, "qs://") {
		log.Errorf("Action between <%s> and <%s> is invalid", src, dst)
		return constants.FlowInvalid
	}
	if strings.HasPrefix(src, "qs://") {
		return constants.FlowToLocal
	}
	return constants.FlowToRemote
}
func ParseLocalPath(p string) (pathType typ.ObjectType, err error) {
	if p == "-" {
		return typ.ObjectTypeStream, nil
	}
	fi, err := os.Stat(p)
	if os.IsNotExist(err) {
		if strings.HasSuffix(p, string(os.PathSeparator)) {
			return typ.ObjectTypeDir, nil
		}
		return typ.ObjectTypeFile, nil
	}
	if err != nil {
		return typ.ObjectTypeInvalid, fmt.Errorf("parse path failed: {%w}", types.NewErrUnhandled(err))
	}
	if fi.IsDir() {
		return typ.ObjectTypeDir, nil
	}
	return typ.ObjectTypeFile, nil
}
func ParseQsPath(p string) (keyType typ.ObjectType, bucketName, objectKey string, err error) {
	p = strings.TrimPrefix(p, "qs://")
	s := strings.SplitN(p, "/", 2)
	if len(s) == 1 || s[1] == "" {
		return typ.ObjectTypeDir, s[0], "", nil
	}
	if strings.HasSuffix(p, "/") {
		return typ.ObjectTypeDir, s[0], s[1], nil
	}
	return typ.ObjectTypeFile, s[0], s[1], nil
}
func ParseStorageInput(input string, storageType StoragerType) (
	workDir, path string, objectType typ.ObjectType, store storage.Storager, err error) {
	switch storageType {
	case fs.Type:
		objectType, err = ParseLocalPath(input)
		if err != nil {
			return
		}
		workDir, path, err = ParseFsWorkDir(input)
		if err != nil {
			return
		}
		log.Debugf("%s work dir: %s", fs.Type, workDir)
		store, err = fs.NewStorager(pairs.WithWorkDir(workDir))
		if err != nil {
			return
		}
	case qingstor.Type:
		var bucketName, objectKey string
		objectType, bucketName, objectKey, err = ParseQsPath(input)
		if err != nil {
			return
		}
		workDir, path = ParseQsWorkDir(objectKey)
		log.Debugf("%s work dir: %s", qingstor.Type, workDir)
		store, err = NewQingStorStorage(pairs.WithName(bucketName), pairs.WithWorkDir(workDir))
		if err != nil {
			return
		}
	default:
		err = fmt.Errorf("%w %s", ErrStoragerTypeInvalid, storageType)
	}
	return
}
func ParseServiceInput(serviceType StoragerType) (service storage.Servicer, err error) {
	switch serviceType {
	case qingstor.Type:
		service, err = NewQingStorService()
		if err != nil {
			return
		}
	default:
		err = fmt.Errorf("%w %s", ErrStoragerTypeInvalid, serviceType)
	}
	return
}
func ParseAtServiceInput(t interface {
	types.ServiceSetter
}) (err error) {
	service, err := ParseServiceInput(qingstor.Type)
	if err != nil {
		return
	}
	setupService(t, service)
	return
}
func ParseAtStorageInput(t interface {
	types.PathSetter
	types.StorageSetter
	types.TypeSetter
}, input string) (dstWorkDir string, err error) {
	flow := ParseFlow(input, "")
	if flow != constants.FlowAtRemote {
		err = ErrInvalidFlow
		return
	}
	var (
		dstPath  string
		dstType  typ.ObjectType
		dstStore storage.Storager
	)
	dstWorkDir, dstPath, dstType, dstStore, err = ParseStorageInput(input, qingstor.Type)
	if err != nil {
		return
	}
	setupStorage(t, dstPath, dstType, dstStore)
	return
}
func ParseBetweenStorageInput(t interface {
	types.SourcePathSetter
	types.SourceStorageSetter
	types.SourceTypeSetter
	types.DestinationPathSetter
	types.DestinationStorageSetter
	types.DestinationTypeSetter
}, src, dst string) (srcWorkDir, dstWorkDir string, err error) {
	flow := ParseFlow(src, dst)
	var (
		srcPath, dstPath   string
		srcType, dstType   typ.ObjectType
		srcStore, dstStore storage.Storager
	)
	switch flow {
	case constants.FlowToRemote:
		srcWorkDir, srcPath, srcType, srcStore, err = ParseStorageInput(src, fs.Type)
		if err != nil {
			return
		}
		dstWorkDir, dstPath, dstType, dstStore, err = ParseStorageInput(dst, qingstor.Type)
		if err != nil {
			return
		}
	case constants.FlowToLocal:
		srcWorkDir, srcPath, srcType, srcStore, err = ParseStorageInput(src, qingstor.Type)
		if err != nil {
			return
		}
		dstWorkDir, dstPath, dstType, dstStore, err = ParseStorageInput(dst, fs.Type)
		if err != nil {
			return
		}
	default:
		err = ErrInvalidFlow
		return
	}
	if dstPath == "" && srcPath != "" {
		dstPath = srcPath
	}
	setupSourceStorage(t, srcPath, srcType, srcStore)
	setupDestinationStorage(t, dstPath, dstType, dstStore)
	return
}
func setupSourceStorage(t interface {
	types.SourcePathSetter
	types.SourceStorageSetter
	types.SourceTypeSetter
}, path string, objectType typ.ObjectType, store storage.Storager) {
	t.SetSourcePath(path)
	t.SetSourceType(objectType)
	t.SetSourceStorage(store)
}
func setupDestinationStorage(t interface {
	types.DestinationPathSetter
	types.DestinationStorageSetter
	types.DestinationTypeSetter
}, path string, objectType typ.ObjectType, store storage.Storager) {
	t.SetDestinationPath(path)
	t.SetDestinationType(objectType)
	t.SetDestinationStorage(store)
}
func setupStorage(t interface {
	types.PathSetter
	types.StorageSetter
	types.TypeSetter
}, path string, objectType typ.ObjectType, store storage.Storager) {
	t.SetPath(path)
	t.SetType(objectType)
	t.SetStorage(store)
}
func setupService(t interface {
	types.ServiceSetter
}, store storage.Servicer) {
	t.SetService(store)
}