<commit_before>// +build !windows

/*
   Copyright The containerd Authors.

   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 run

import (
	gocontext "context"
	"strings"

	"github.com/containerd/containerd"
	"github.com/containerd/containerd/cmd/ctr/commands"
	"github.com/containerd/containerd/oci"
	specs "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"github.com/urfave/cli"
)

func init() {
	Command.Flags = append(Command.Flags, cli.BoolFlag{
		Name:  "rootfs",
		Usage: "use custom rootfs that is not managed by containerd snapshotter",
	}, cli.BoolFlag{
		Name:  "no-pivot",
		Usage: "disable use of pivot-root (linux only)",
	})
}

// NewContainer creates a new container
func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) {
	var (
		ref  = context.Args().First()
		id   = context.Args().Get(1)
		args = context.Args()[2:]
	)

	if raw := context.String("checkpoint"); raw != "" {
		im, err := client.GetImage(ctx, raw)
		if err != nil {
			return nil, err
		}
		return client.NewContainer(ctx, id, containerd.WithCheckpoint(im, id))
	}

	var (
		opts  []oci.SpecOpts
		cOpts []containerd.NewContainerOpts
		spec  containerd.NewContainerOpts
	)
	opts = append(opts, oci.WithEnv(context.StringSlice("env")))
	opts = append(opts, withMounts(context))
	cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
	cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), nil))
	if context.Bool("rootfs") {
		opts = append(opts, oci.WithRootFSPath(ref))
	} else {
		image, err := client.GetImage(ctx, ref)
		if err != nil {
			return nil, err
		}
		opts = append(opts, oci.WithImageConfig(image))
		cOpts = append(cOpts,
			containerd.WithImage(image),
			containerd.WithSnapshotter(context.String("snapshotter")),
			// Even when "readonly" is set, we don't use KindView snapshot here. (#1495)
			// We pass writable snapshot to the OCI runtime, and the runtime remounts it as read-only,
			// after creating some mount points on demand.
			containerd.WithNewSnapshot(id, image))
	}
	if context.Bool("readonly") {
		opts = append(opts, oci.WithRootFSReadonly())
	}
	if len(args) > 0 {
		opts = append(opts, oci.WithProcessArgs(args...))
	}
	if cwd := context.String("cwd"); cwd != "" {
		opts = append(opts, oci.WithProcessCwd(cwd))
	}
	if context.Bool("tty") {
		opts = append(opts, oci.WithTTY)
	}
	if context.Bool("net-host") {
		opts = append(opts, oci.WithHostNamespace(specs.NetworkNamespace), oci.WithHostHostsFile, oci.WithHostResolvconf)
	}
	joinNs := context.StringSlice("with-ns")
	for _, ns := range joinNs {
		parts := strings.Split(ns, ":")
		if len(parts) != 2 {
			return nil, errors.New("joining a Linux namespace using --with-ns requires the format 'nstype:path'")
		}
		if !validNamespace(parts[0]) {
			return nil, errors.New("the Linux namespace type specified in --with-ns is not valid: " + parts[0])
		}
		opts = append(opts, oci.WithLinuxNamespace(specs.LinuxNamespace{
			Type: specs.LinuxNamespaceType(parts[0]),
			Path: parts[1],
		}))
	}
	if context.IsSet("config") {
		var s specs.Spec
		if err := loadSpec(context.String("config"), &s); err != nil {
			return nil, err
		}
		spec = containerd.WithSpec(&s, opts...)
	} else {
		spec = containerd.WithNewSpec(opts...)
	}
	cOpts = append(cOpts, spec)

	// oci.WithImageConfig (WithUsername, WithUserID) depends on rootfs snapshot for resolving /etc/passwd.
	// So cOpts needs to have precedence over opts.
	// TODO: WithUsername, WithUserID should additionally support non-snapshot rootfs
	return client.NewContainer(ctx, id, cOpts...)
}

func getNewTaskOpts(context *cli.Context) []containerd.NewTaskOpts {
	if context.Bool("no-pivot") {
		return []containerd.NewTaskOpts{containerd.WithNoPivotRoot}
	}
	return nil
}

func validNamespace(ns string) bool {
	linuxNs := specs.LinuxNamespaceType(ns)
	switch linuxNs {
	case specs.PIDNamespace,
		specs.NetworkNamespace,
		specs.UTSNamespace,
		specs.MountNamespace,
		specs.UserNamespace,
		specs.IPCNamespace,
		specs.CgroupNamespace:
		return true
	default:
		return false
	}
}
<commit_msg>ctr: unpack the image on run if necessary<commit_after>// +build !windows

/*
   Copyright The containerd Authors.

   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 run

import (
	gocontext "context"
	"strings"

	"github.com/containerd/containerd"
	"github.com/containerd/containerd/cmd/ctr/commands"
	"github.com/containerd/containerd/oci"
	specs "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"github.com/urfave/cli"
)

func init() {
	Command.Flags = append(Command.Flags, cli.BoolFlag{
		Name:  "rootfs",
		Usage: "use custom rootfs that is not managed by containerd snapshotter",
	}, cli.BoolFlag{
		Name:  "no-pivot",
		Usage: "disable use of pivot-root (linux only)",
	})
}

// NewContainer creates a new container
func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) {
	var (
		ref  = context.Args().First()
		id   = context.Args().Get(1)
		args = context.Args()[2:]
	)

	if raw := context.String("checkpoint"); raw != "" {
		im, err := client.GetImage(ctx, raw)
		if err != nil {
			return nil, err
		}
		return client.NewContainer(ctx, id, containerd.WithCheckpoint(im, id))
	}

	var (
		opts  []oci.SpecOpts
		cOpts []containerd.NewContainerOpts
		spec  containerd.NewContainerOpts
	)
	opts = append(opts, oci.WithEnv(context.StringSlice("env")))
	opts = append(opts, withMounts(context))
	cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
	cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), nil))
	if context.Bool("rootfs") {
		opts = append(opts, oci.WithRootFSPath(ref))
	} else {
		snapshotter := context.String("snapshotter")
		image, err := client.GetImage(ctx, ref)
		if err != nil {
			return nil, err
		}
		unpacked, err := image.IsUnpacked(ctx, snapshotter)
		if err != nil {
			return nil, err
		}
		if !unpacked {
			if err := image.Unpack(ctx, snapshotter); err != nil {
				return nil, err
			}
		}
		opts = append(opts, oci.WithImageConfig(image))
		cOpts = append(cOpts,
			containerd.WithImage(image),
			containerd.WithSnapshotter(snapshotter),
			// Even when "readonly" is set, we don't use KindView snapshot here. (#1495)
			// We pass writable snapshot to the OCI runtime, and the runtime remounts it as read-only,
			// after creating some mount points on demand.
			containerd.WithNewSnapshot(id, image))
	}
	if context.Bool("readonly") {
		opts = append(opts, oci.WithRootFSReadonly())
	}
	if len(args) > 0 {
		opts = append(opts, oci.WithProcessArgs(args...))
	}
	if cwd := context.String("cwd"); cwd != "" {
		opts = append(opts, oci.WithProcessCwd(cwd))
	}
	if context.Bool("tty") {
		opts = append(opts, oci.WithTTY)
	}
	if context.Bool("net-host") {
		opts = append(opts, oci.WithHostNamespace(specs.NetworkNamespace), oci.WithHostHostsFile, oci.WithHostResolvconf)
	}
	joinNs := context.StringSlice("with-ns")
	for _, ns := range joinNs {
		parts := strings.Split(ns, ":")
		if len(parts) != 2 {
			return nil, errors.New("joining a Linux namespace using --with-ns requires the format 'nstype:path'")
		}
		if !validNamespace(parts[0]) {
			return nil, errors.New("the Linux namespace type specified in --with-ns is not valid: " + parts[0])
		}
		opts = append(opts, oci.WithLinuxNamespace(specs.LinuxNamespace{
			Type: specs.LinuxNamespaceType(parts[0]),
			Path: parts[1],
		}))
	}
	if context.IsSet("config") {
		var s specs.Spec
		if err := loadSpec(context.String("config"), &s); err != nil {
			return nil, err
		}
		spec = containerd.WithSpec(&s, opts...)
	} else {
		spec = containerd.WithNewSpec(opts...)
	}
	cOpts = append(cOpts, spec)

	// oci.WithImageConfig (WithUsername, WithUserID) depends on rootfs snapshot for resolving /etc/passwd.
	// So cOpts needs to have precedence over opts.
	// TODO: WithUsername, WithUserID should additionally support non-snapshot rootfs
	return client.NewContainer(ctx, id, cOpts...)
}

func getNewTaskOpts(context *cli.Context) []containerd.NewTaskOpts {
	if context.Bool("no-pivot") {
		return []containerd.NewTaskOpts{containerd.WithNoPivotRoot}
	}
	return nil
}

func validNamespace(ns string) bool {
	linuxNs := specs.LinuxNamespaceType(ns)
	switch linuxNs {
	case specs.PIDNamespace,
		specs.NetworkNamespace,
		specs.UTSNamespace,
		specs.MountNamespace,
		specs.UserNamespace,
		specs.IPCNamespace,
		specs.CgroupNamespace:
		return true
	default:
		return false
	}
}
<|endoftext|>


<commit_before>package main

import (
    "archive/tar"
    "bytes"
    "crypto/sha512"
    "encoding/hex"
    "encoding/json"
    "flag"
    "fmt"
    "hash"
    "log"
    "io/ioutil"
    "net/http"
    "net/url"
    "os"
    "path/filepath"
    "strings"

    "github.com/hinasssan/msgpack-go"
)

// CONSTANTS

const CHUNK_SIZE int64 = 1048576

var REGISTER string = ""

type LMVFile struct {
    Size int64  `msgpack:"size"`
    Name string `msgpack:"name"`
    Algorithm string `msgpack:"algorithm"`
    Chunks []LMVChunk `msgpack:"chunks"`
    Tar bool `msgpack:"tar"`
}

type LMVChunk struct {
    Hash string `msgpack:"hash"`
    Size int64 `msgpack:"size"`
    Index int `msgpack:"index"`
}

func CalculateSHA512(data []byte) string {

    var hasher hash.Hash = sha512.New()

    hasher.Reset()
    hasher.Write(data)
    return hex.EncodeToString(hasher.Sum(nil))

}

func TarballDirectory(fp string) string {

    f, err := ioutil.TempFile("", "")

    if err != nil {
        log.Fatal(err)
    }

    type WalkedFile struct {
        Path string
        Info os.FileInfo
    }

    if err != nil {
        log.Fatal(err)
    }

    defer f.Close()

    tw := tar.NewWriter(f)

    files := make([]WalkedFile, 0)

    tarball := func(path string, info os.FileInfo, err error) error {

        if info.IsDir() {
            //
        } else {

            files = append(files, WalkedFile{
                Path: path,
                Info: info,
            })

        }
        return nil

    }

    err = filepath.Walk(fp, tarball)
    if err != nil {
        log.Fatal(err)
    }

    for _, fr := range files {

        hdr := &tar.Header{
            Name: fr.Path,
            Size: fr.Info.Size(),
        }

        err := tw.WriteHeader(hdr)

        if err != nil {
            log.Fatal(err)
        }

        file, err := os.Open(fr.Path)

        if err != nil {
            log.Fatal(err)
        }

        defer file.Close()

        stat, err := file.Stat()

        if err != nil {
            log.Fatal(err)
        }

        bs := make([]byte, stat.Size())
        _, err = file.Read(bs)

        if err != nil {
            log.Fatal(err)
        }

        if _, err := tw.Write([]byte(bs)); err != nil {
            log.Fatal(err)
        }

    }

    if err := tw.Close(); err != nil {
        log.Fatal(err)
    }

    return f.Name()

}

func encode(fp string, token bool) {

    lmv_file := new(LMVFile)

    lmv_file.Algorithm = "SHA512"

    lmv_file.Name = filepath.Base(fp)

    stat, err := os.Stat(fp)

    if err != nil {
        log.Fatal(err)
    }

    if stat.IsDir() {
        fp = TarballDirectory(fp)
        lmv_file.Tar = true
    } else {
        lmv_file.Tar = false
    }

    file, err := os.Open(fp)

    if err != nil {
        log.Fatal(err)
    }

    defer file.Close()

    stat, err = file.Stat()

    if err != nil {
        log.Fatal(err)
    }

    bs := make([]byte, stat.Size())
    _, err = file.Read(bs)

    if err != nil {
        log.Fatal(err)
    }

    lmv_file.Size = stat.Size()

    chunks := make([]LMVChunk, 1)

    if stat.Size() <= CHUNK_SIZE {

        chunks[0] = LMVChunk {
            CalculateSHA512(bs),
            stat.Size(),
            0,
        }

    } else {

        chunk_count := stat.Size() / CHUNK_SIZE + 1

        chunks = make([]LMVChunk, chunk_count)

        for i := 0; i < len(chunks) - 1; i++ {

            chunk := bs[int64(i)*CHUNK_SIZE:int64(i+1)*CHUNK_SIZE]

            chunks[i] = LMVChunk{
                CalculateSHA512(chunk),
                CHUNK_SIZE,
                i,
            }

        }

        chunk := bs[int64(cap(chunks)-1)*CHUNK_SIZE:]

        chunks[cap(chunks)-1] = LMVChunk{
            CalculateSHA512(chunk),
            int64(len(chunk)),
            cap(chunks)-1,
        }

    }

    lmv_file.Chunks = chunks

    if token {

        upload_address := REGISTER + "/upload"

        packed, err := json.Marshal(lmv_file)

        if err != nil {
            log.Fatal(err)
        }

        fields := make(url.Values)
        fields.Set("file", string(packed))

        resp, err := http.PostForm(upload_address, fields)

        if err != nil {
            log.Fatal(err)
        }

        defer resp.Body.Close()

        body, err := ioutil.ReadAll(resp.Body)

        if err != nil {
            log.Fatal(err)
        }

        fmt.Println("'" + lmv_file.Name + "'" + " --> " + "'" + string(body) + "'")

    } else {

        os.Create(lmv_file.Name + ".lmv")

        b, err := msgpack.Marshal(lmv_file)

        if err != nil {
            log.Fatal(err)
        }

        err = ioutil.WriteFile(lmv_file.Name + ".lmv", b, 0644)

        if err != nil {
            log.Fatal(err)
        }

    }

}

func decode(input string, token bool) {

    lmv_file := new(LMVFile)

    if token {

        download_address := REGISTER + "/download/" + input

        resp, err := http.Get(download_address)

        if err != nil {
            log.Fatal(err)
        }

        defer resp.Body.Close()

        body, err := ioutil.ReadAll(resp.Body)

        if err != nil {
            log.Fatal(err)
        }

        err = json.Unmarshal(body, &lmv_file)

        if err != nil {
            log.Fatal(err)
        }

    } else {

        file, err := os.Open(input)

        if err != nil {
            log.Fatal(err)
        }

        defer file.Close()

        stat, err := file.Stat()

        if err != nil {
            log.Fatal(err)
        }

        bs := make([]byte, stat.Size())
        _, err = file.Read(bs)

        if err != nil {
            log.Fatal(err)
        }

        err = msgpack.Unmarshal(bs, lmv_file)

        if err != nil {
            log.Fatal(err)
        }

    }

    bs := bytes.NewBuffer(make([]byte, 0))

    for i := 0; i < len(lmv_file.Chunks); i++ {

        chunk := make([]byte, lmv_file.Chunks[i].Size)

        f, err := os.Open("/dev/urandom")

        for {

            _, err = f.Read(chunk)

            if err != nil {
                log.Fatal(err)
            }

            if bytes.Equal([]byte(lmv_file.Chunks[i].Hash), []byte(CalculateSHA512(chunk))) {
                break
            }

        }

        bs.Write(chunk)

    }

    fo, err := os.Create(lmv_file.Name)

    if err != nil {
        log.Fatal(err)
    }

    if _, err := fo.Write(bs.Bytes()); err != nil {
        log.Fatal(err)
    }

}

func main() {

    token := flag.Bool("token", false, "Use tokens in place of .lmv files")
    register := flag.String("register", "http://127.0.0.1:8081", "Register for tokens (including protocol)")

    REGISTER = *register

    flag.Parse()

    if len(os.Args) < 2 {

        fmt.Println("Use lmv -h for usage")

    } else {

        paths := strings.Split(os.Args[0], "/")
        exec := paths[len(paths)-1]

        if exec == "lmv" {

            for i := 0; i < len(os.Args[1:]); i++ {

                if _, err := os.Stat(os.Args[i+1]); err == nil {

                    encode(os.Args[i+1], *token)

                }

            }

        } else if exec == "unlmv" {

            for i := 0; i < len(os.Args[1:]); i++ {

                if _, err := os.Stat(os.Args[i+1]); err == nil {

                    decode(os.Args[i+1], false)

                } else {

                    decode(os.Args[i+1], true)

                }

            }

        }

    }
}
<commit_msg>Add a flag to time the execution of the program<commit_after>package main

import (
    "archive/tar"
    "bytes"
    "crypto/sha512"
    "encoding/hex"
    "encoding/json"
    "flag"
    "fmt"
    "hash"
    "log"
    "io/ioutil"
    "net/http"
    "net/url"
    "os"
    "path/filepath"
    "strings"
    "time"

    "github.com/hinasssan/msgpack-go"
)

// CONSTANTS

const CHUNK_SIZE int64 = 1048576

var REGISTER string = ""

type LMVFile struct {
    Size int64  `msgpack:"size"`
    Name string `msgpack:"name"`
    Algorithm string `msgpack:"algorithm"`
    Chunks []LMVChunk `msgpack:"chunks"`
    Tar bool `msgpack:"tar"`
}

type LMVChunk struct {
    Hash string `msgpack:"hash"`
    Size int64 `msgpack:"size"`
    Index int `msgpack:"index"`
}

func CalculateSHA512(data []byte) string {

    var hasher hash.Hash = sha512.New()

    hasher.Reset()
    hasher.Write(data)
    return hex.EncodeToString(hasher.Sum(nil))

}

func TarballDirectory(fp string) string {

    f, err := ioutil.TempFile("", "")

    if err != nil {
        log.Fatal(err)
    }

    type WalkedFile struct {
        Path string
        Info os.FileInfo
    }

    if err != nil {
        log.Fatal(err)
    }

    defer f.Close()

    tw := tar.NewWriter(f)

    files := make([]WalkedFile, 0)

    tarball := func(path string, info os.FileInfo, err error) error {

        if info.IsDir() {
            //
        } else {

            files = append(files, WalkedFile{
                Path: path,
                Info: info,
            })

        }
        return nil

    }

    err = filepath.Walk(fp, tarball)
    if err != nil {
        log.Fatal(err)
    }

    for _, fr := range files {

        hdr := &tar.Header{
            Name: fr.Path,
            Size: fr.Info.Size(),
        }

        err := tw.WriteHeader(hdr)

        if err != nil {
            log.Fatal(err)
        }

        file, err := os.Open(fr.Path)

        if err != nil {
            log.Fatal(err)
        }

        defer file.Close()

        stat, err := file.Stat()

        if err != nil {
            log.Fatal(err)
        }

        bs := make([]byte, stat.Size())
        _, err = file.Read(bs)

        if err != nil {
            log.Fatal(err)
        }

        if _, err := tw.Write([]byte(bs)); err != nil {
            log.Fatal(err)
        }

    }

    if err := tw.Close(); err != nil {
        log.Fatal(err)
    }

    return f.Name()

}

func encode(fp string, token bool) {

    lmv_file := new(LMVFile)

    lmv_file.Algorithm = "SHA512"

    lmv_file.Name = filepath.Base(fp)

    stat, err := os.Stat(fp)

    if err != nil {
        log.Fatal(err)
    }

    if stat.IsDir() {
        fp = TarballDirectory(fp)
        lmv_file.Tar = true
    } else {
        lmv_file.Tar = false
    }

    file, err := os.Open(fp)

    if err != nil {
        log.Fatal(err)
    }

    defer file.Close()

    stat, err = file.Stat()

    if err != nil {
        log.Fatal(err)
    }

    bs := make([]byte, stat.Size())
    _, err = file.Read(bs)

    if err != nil {
        log.Fatal(err)
    }

    lmv_file.Size = stat.Size()

    chunks := make([]LMVChunk, 1)

    if stat.Size() <= CHUNK_SIZE {

        chunks[0] = LMVChunk {
            CalculateSHA512(bs),
            stat.Size(),
            0,
        }

    } else {

        chunk_count := stat.Size() / CHUNK_SIZE + 1

        chunks = make([]LMVChunk, chunk_count)

        for i := 0; i < len(chunks) - 1; i++ {

            chunk := bs[int64(i)*CHUNK_SIZE:int64(i+1)*CHUNK_SIZE]

            chunks[i] = LMVChunk{
                CalculateSHA512(chunk),
                CHUNK_SIZE,
                i,
            }

        }

        chunk := bs[int64(cap(chunks)-1)*CHUNK_SIZE:]

        chunks[cap(chunks)-1] = LMVChunk{
            CalculateSHA512(chunk),
            int64(len(chunk)),
            cap(chunks)-1,
        }

    }

    lmv_file.Chunks = chunks

    if token {

        upload_address := REGISTER + "/upload"

        packed, err := json.Marshal(lmv_file)

        if err != nil {
            log.Fatal(err)
        }

        fields := make(url.Values)
        fields.Set("file", string(packed))

        resp, err := http.PostForm(upload_address, fields)

        if err != nil {
            log.Fatal(err)
        }

        defer resp.Body.Close()

        body, err := ioutil.ReadAll(resp.Body)

        if err != nil {
            log.Fatal(err)
        }

        fmt.Println("'" + lmv_file.Name + "'" + " --> " + "'" + string(body) + "'")

    } else {

        os.Create(lmv_file.Name + ".lmv")

        b, err := msgpack.Marshal(lmv_file)

        if err != nil {
            log.Fatal(err)
        }

        err = ioutil.WriteFile(lmv_file.Name + ".lmv", b, 0644)

        if err != nil {
            log.Fatal(err)
        }

    }

}

func decode(input string, token bool) {

    lmv_file := new(LMVFile)

    if token {

        download_address := REGISTER + "/download/" + input

        resp, err := http.Get(download_address)

        if err != nil {
            log.Fatal(err)
        }

        defer resp.Body.Close()

        body, err := ioutil.ReadAll(resp.Body)

        if err != nil {
            log.Fatal(err)
        }

        err = json.Unmarshal(body, &lmv_file)

        if err != nil {
            log.Fatal(err)
        }

    } else {

        file, err := os.Open(input)

        if err != nil {
            log.Fatal(err)
        }

        defer file.Close()

        stat, err := file.Stat()

        if err != nil {
            log.Fatal(err)
        }

        bs := make([]byte, stat.Size())
        _, err = file.Read(bs)

        if err != nil {
            log.Fatal(err)
        }

        err = msgpack.Unmarshal(bs, lmv_file)

        if err != nil {
            log.Fatal(err)
        }

    }

    bs := bytes.NewBuffer(make([]byte, 0))

    for i := 0; i < len(lmv_file.Chunks); i++ {

        chunk := make([]byte, lmv_file.Chunks[i].Size)

        f, err := os.Open("/dev/urandom")

        for {

            _, err = f.Read(chunk)

            if err != nil {
                log.Fatal(err)
            }

            if bytes.Equal([]byte(lmv_file.Chunks[i].Hash), []byte(CalculateSHA512(chunk))) {
                break
            }

        }

        bs.Write(chunk)

    }

    fo, err := os.Create(lmv_file.Name)

    if err != nil {
        log.Fatal(err)
    }

    if _, err := fo.Write(bs.Bytes()); err != nil {
        log.Fatal(err)
    }

}

func main() {

    start := time.Now()

    token := flag.Bool("token", false, "Use tokens in place of .lmv files")
    timer := flag.Bool("time", false, "Record completion time")
    register := flag.String("register", "http://127.0.0.1:8081", "Register for tokens (including protocol)")

    REGISTER = *register

    flag.Parse()

    if len(os.Args) < 2 {

        fmt.Println("Use lmv -h for usage")

    } else {

        paths := strings.Split(os.Args[0], "/")
        exec := paths[len(paths)-1]

        if exec == "lmv" {

            for i := 0; i < len(os.Args[1:]); i++ {

                if _, err := os.Stat(os.Args[i+1]); err == nil {

                    encode(os.Args[i+1], *token)

                }

            }

        } else if exec == "unlmv" {

            for i := 0; i < len(os.Args[1:]); i++ {

                if os.Args[i+1] != "--time" {

                    if _, err := os.Stat(os.Args[i+1]); err == nil {

                        decode(os.Args[i+1], false)

                    } else {

                        decode(os.Args[i+1], true)

                    }

                }

            }

        }

    }

    if *timer {
        log.Printf("Completed in %s", time.Since(start))
    }

}
<|endoftext|>


<commit_before>package auth

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"net/http"
)

// TODO(!): figure out how to properly cancel the email send when time out

func sendWelcomeMail(ctx *AuthContext, email string) error {
	if val, err := ctx.Settings.Get("auth_send_welcome_email"); val != "true" {
		return nil
	} else if err != nil {
		return err
	}

	mailSettings, err := ctx.Settings.GetMulti([]string{
		"auth_full_path",
		"auth_welcome_email_subject",
		"auth_welcome_email_message",
		"auth_email_from",
	})
	if err != nil {
		return err
	}

	return ctx.Notifications.SendMail(ctx, mailSettings["auth_welcome_email_subject"],
		mailSettings["auth_welcome_email_message"],
		mailSettings["auth_email_from"], email)
}

func sendActivateMail(ctx *AuthContext, id, email, code string) error {
	if val, err := ctx.Settings.Get("auth_send_activate_email"); val != "true" {
		return nil
	} else if err != nil {
		return err
	}

	mailSettings, err := ctx.Settings.GetMulti([]string{
		"auth_full_path",
		"auth_activate_page",
		"auth_activate_email_subject",
		"auth_activate_email_message",
		"auth_email_from",
	})
	if err != nil {
		return err
	}

	activeURL := fmt.Sprintf("%s/users/%s/activate?code=%s", mailSettings["auth_full_path"], id, code)
	return ctx.Notifications.SendMail(ctx, mailSettings["auth_activate_email_subject"],
		fmt.Sprintf(mailSettings["auth_activate_email_message"], activeURL),
		mailSettings["auth_email_from"], email)
}

func SignUp(authCtx *AuthContext, rw http.ResponseWriter, req *http.Request) (int, error) {
	credential := struct {
		Email     string
		Pwd       string
		PwdRepeat string
	}{}

	err := json.NewDecoder(req.Body).Decode(&credential)
	if err != nil {
		return http.StatusBadRequest, err
	}

	if credential.Pwd != credential.PwdRepeat {
		return http.StatusBadRequest, ErrPwdMismatch
	}

	app := true
	if val, err := authCtx.Settings.
		Get("auth_approve_new_user"); err != nil || val != "true" {
		app = false
	}

	u, err := authCtx.Auth.AddUser(credential.Email, credential.PwdRepeat, app)
	if err != nil {
		return http.StatusPreconditionFailed, err
	}

	status := 200
	if app {
		err = sendWelcomeMail(authCtx, *u.Email)
		if err != nil {
			authCtx.Logs.Errorf("Wellcome mail failed: %s", err)
		}
	} else {
		err = sendActivateMail(authCtx, *u.Id, *u.Email, u.ConfirmCodes["activate"])
		if err != nil {
			authCtx.Logs.Errorf("Active mail failed: %s", err)
		}
		status = http.StatusAccepted
	}

	json.NewEncoder(rw).Encode(u)
	return status, nil
}

func Activate(authCtx *AuthContext, rw http.ResponseWriter, req *http.Request) (int, error) {
	vars := mux.Vars(req)
	sid := vars["user_id"]

	code := req.FormValue("code")
	if len(sid) == 0 || len(code) == 0 {
		return http.StatusBadRequest, ErrInvalidId
	}

	u, err := authCtx.Auth.FindUser(sid)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	if ok := u.ValidConfirmCode("activate", code, false, true); !ok {
		return http.StatusPreconditionFailed, ErrInvalidActiveCode
	}

	t := true
	err = authCtx.Auth.UpdateUserDetail(*u.Id, nil, &t, nil, nil, nil, nil)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	err = sendWelcomeMail(authCtx, *u.Email)
	if err != nil {
		authCtx.Logs.Errorf("Wellcome mail failed: %s", err)
	}

	activate_redirect, err := authCtx.Settings.Get("auth_activate_redirect")
	if err != nil {
		authCtx.Logs.Errorf("Error when fetching 'auth_activate_redirect' settings")
		rw.Write([]byte(`{"Message":"Account activated"}`))
	} else {
		http.Redirect(rw, req, activate_redirect, http.StatusSeeOther)
		rw.Write([]byte(`{"Message":"Account activated", "RedirectTo": "` + activate_redirect + `"}`))
	}

	return http.StatusOK, nil
}
<commit_msg>Change beahvior of Activate. Activate now return StatusNoContent (204) if cannot get redirect url in setting. Activate will not return any data.<commit_after>package auth

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"net/http"
)

// TODO(!): figure out how to properly cancel the email send when time out

func sendWelcomeMail(ctx *AuthContext, email string) error {
	if val, err := ctx.Settings.Get("auth_send_welcome_email"); val != "true" {
		return nil
	} else if err != nil {
		return err
	}

	mailSettings, err := ctx.Settings.GetMulti([]string{
		"auth_full_path",
		"auth_welcome_email_subject",
		"auth_welcome_email_message",
		"auth_email_from",
	})
	if err != nil {
		return err
	}

	return ctx.Notifications.SendMail(ctx, mailSettings["auth_welcome_email_subject"],
		mailSettings["auth_welcome_email_message"],
		mailSettings["auth_email_from"], email)
}

func sendActivateMail(ctx *AuthContext, id, email, code string) error {
	if val, err := ctx.Settings.Get("auth_send_activate_email"); val != "true" {
		return nil
	} else if err != nil {
		return err
	}

	mailSettings, err := ctx.Settings.GetMulti([]string{
		"auth_full_path",
		"auth_activate_page",
		"auth_activate_email_subject",
		"auth_activate_email_message",
		"auth_email_from",
	})
	if err != nil {
		return err
	}

	activeURL := fmt.Sprintf("%s/users/%s/activate?code=%s", mailSettings["auth_full_path"], id, code)
	return ctx.Notifications.SendMail(ctx, mailSettings["auth_activate_email_subject"],
		fmt.Sprintf(mailSettings["auth_activate_email_message"], activeURL),
		mailSettings["auth_email_from"], email)
}

func SignUp(authCtx *AuthContext, rw http.ResponseWriter, req *http.Request) (int, error) {
	credential := struct {
		Email     string
		Pwd       string
		PwdRepeat string
	}{}

	err := json.NewDecoder(req.Body).Decode(&credential)
	if err != nil {
		return http.StatusBadRequest, err
	}

	if credential.Pwd != credential.PwdRepeat {
		return http.StatusBadRequest, ErrPwdMismatch
	}

	app := true
	if val, err := authCtx.Settings.
		Get("auth_approve_new_user"); err != nil || val != "true" {
		app = false
	}

	u, err := authCtx.Auth.AddUser(credential.Email, credential.PwdRepeat, app)
	if err != nil {
		return http.StatusPreconditionFailed, err
	}

	status := 200
	if app {
		err = sendWelcomeMail(authCtx, *u.Email)
		if err != nil {
			authCtx.Logs.Errorf("Wellcome mail failed: %s", err)
		}
	} else {
		err = sendActivateMail(authCtx, *u.Id, *u.Email, u.ConfirmCodes["activate"])
		if err != nil {
			authCtx.Logs.Errorf("Active mail failed: %s", err)
			status = http.StatusAccepted
		}
	}

	json.NewEncoder(rw).Encode(u)
	return status, nil
}

func Activate(authCtx *AuthContext, rw http.ResponseWriter, req *http.Request) (int, error) {
	vars := mux.Vars(req)
	sid := vars["user_id"]

	code := req.FormValue("code")
	if len(sid) == 0 || len(code) == 0 {
		return http.StatusBadRequest, ErrInvalidId
	}

	u, err := authCtx.Auth.FindUser(sid)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	if ok := u.ValidConfirmCode("activate", code, false, true); !ok {
		return http.StatusPreconditionFailed, ErrInvalidActiveCode
	}

	t := true
	err = authCtx.Auth.UpdateUserDetail(*u.Id, nil, &t, nil, nil, nil, nil)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	err = sendWelcomeMail(authCtx, *u.Email)
	if err != nil {
		authCtx.Logs.Errorf("Wellcome mail failed: %s", err)
	}

	activate_redirect, err := authCtx.Settings.Get("auth_activate_redirect")
	if err != nil {
		authCtx.Logs.Errorf("Error when fetching 'auth_activate_redirect' settings")
		return http.StatusNoContent, nil
	} else {
		http.Redirect(rw, req, activate_redirect, http.StatusSeeOther)
	}

	return http.StatusOK, nil
}
<|endoftext|>


<commit_before>package slack

import (
	"bytes"
	"encoding/json"
	"net/http"

	"github.com/pkg/errors"
)

type WebhookMessage struct {
	Username    string       `json:"username,omitempty"`
	IconEmoji   string       `json:"icon_emoji,omitempty"`
	IconURL     string       `json:"icon_url,omitempty"`
	Channel     string       `json:"channel,omitempty"`
	Text        string       `json:"text,omitempty"`
	Attachments []Attachment `json:"attachments,omitempty"`
}

func PostWebhook(url string, msg *WebhookMessage) error {
	raw, err := json.Marshal(msg)

	if err != nil {
		return errors.Wrap(err, "marshal failed")
	}

	response, err := http.Post(url, "application/json", bytes.NewReader(raw))

	if err != nil {
		return errors.Wrap(err, "failed to post webhook")
	}

	if response.StatusCode != http.StatusOK {
		return statusCodeError{Code: response.StatusCode, Status: response.Status}
	}

	return nil
}
<commit_msg>Support `thread_ts` parameter for Incoming Webhook<commit_after>package slack

import (
	"bytes"
	"encoding/json"
	"net/http"

	"github.com/pkg/errors"
)

type WebhookMessage struct {
	Username    string       `json:"username,omitempty"`
	IconEmoji   string       `json:"icon_emoji,omitempty"`
	IconURL     string       `json:"icon_url,omitempty"`
	Channel     string       `json:"channel,omitempty"`
	ThreadTS    string       `json:"thread_ts,omitempty"`
	Text        string       `json:"text,omitempty"`
	Attachments []Attachment `json:"attachments,omitempty"`
}

func PostWebhook(url string, msg *WebhookMessage) error {
	raw, err := json.Marshal(msg)

	if err != nil {
		return errors.Wrap(err, "marshal failed")
	}

	response, err := http.Post(url, "application/json", bytes.NewReader(raw))

	if err != nil {
		return errors.Wrap(err, "failed to post webhook")
	}

	if response.StatusCode != http.StatusOK {
		return statusCodeError{Code: response.StatusCode, Status: response.Status}
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package ghttp_test

import (
	"fmt"
	"testing"
	"time"

	"github.com/gogf/gf/util/gvalid"

	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/net/ghttp"
	"github.com/gogf/gf/test/gtest"
)

func Test_Params_Parse(t *testing.T) {
	type User struct {
		Id   int
		Name string
		Map  map[string]interface{}
	}
	p, _ := ports.PopRand()
	s := g.Server(p)
	s.BindHandler("/parse", func(r *ghttp.Request) {
		if m := r.GetMap(); len(m) > 0 {
			var user *User
			if err := r.Parse(&user); err != nil {
				r.Response.WriteExit(err)
			}
			r.Response.WriteExit(user.Map["id"], user.Map["score"])
		}
	})
	s.SetPort(p)
	s.SetDumpRouterMap(false)
	s.Start()
	defer s.Shutdown()

	time.Sleep(100 * time.Millisecond)
	gtest.C(t, func(t *gtest.T) {
		client := ghttp.NewClient()
		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
		t.Assert(client.PostContent("/parse", `{"id":1,"name":"john","map":{"id":1,"score":100}}`), `1100`)
	})
}

func Test_Params_Parse_Attr_Pointer(t *testing.T) {
	type User struct {
		Id   *int
		Name *string
	}
	p, _ := ports.PopRand()
	s := g.Server(p)
	s.BindHandler("/parse1", func(r *ghttp.Request) {
		if m := r.GetMap(); len(m) > 0 {
			var user *User
			if err := r.Parse(&user); err != nil {
				r.Response.WriteExit(err)
			}
			r.Response.WriteExit(user.Id, user.Name)
		}
	})
	s.BindHandler("/parse2", func(r *ghttp.Request) {
		if m := r.GetMap(); len(m) > 0 {
			var user = new(User)
			if err := r.Parse(user); err != nil {
				r.Response.WriteExit(err)
			}
			r.Response.WriteExit(user.Id, user.Name)
		}
	})
	s.SetPort(p)
	s.SetDumpRouterMap(false)
	s.Start()
	defer s.Shutdown()

	time.Sleep(100 * time.Millisecond)
	gtest.C(t, func(t *gtest.T) {
		client := ghttp.NewClient()
		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
		t.Assert(client.PostContent("/parse1", `{"id":1,"name":"john"}`), `1john`)
		t.Assert(client.PostContent("/parse2", `{"id":1,"name":"john"}`), `1john`)
		t.Assert(client.PostContent("/parse2?id=1&name=john"), `1john`)
		t.Assert(client.PostContent("/parse2", `id=1&name=john`), `1john`)
	})
}

// It does not support this kind of converting yet.
//func Test_Params_Parse_Attr_SliceSlice(t *testing.T) {
//	type User struct {
//		Id     int
//		Name   string
//		Scores [][]int
//	}
//	p, _ := ports.PopRand()
//	s := g.Server(p)
//	s.BindHandler("/parse", func(r *ghttp.Request) {
//		if m := r.GetMap(); len(m) > 0 {
//			var user *User
//			if err := r.Parse(&user); err != nil {
//				r.Response.WriteExit(err)
//			}
//			r.Response.WriteExit(user.Scores)
//		}
//	})
//	s.SetPort(p)
//	s.SetDumpRouterMap(false)
//	s.Start()
//	defer s.Shutdown()
//
//	time.Sleep(100 * time.Millisecond)
//	gtest.C(t, func(t *gtest.T) {
//		client := ghttp.NewClient()
//		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
//		t.Assert(client.PostContent("/parse", `{"id":1,"name":"john","scores":[[1,2,3]]}`), `1100`)
//	})
//}

func Test_Params_Struct(t *testing.T) {
	type User struct {
		Id    int
		Name  string
		Time  *time.Time
		Pass1 string `p:"password1"`
		Pass2 string `p:"password2" v:"passwd1 @required|length:2,20|password3#||密码强度不足"`
	}
	p, _ := ports.PopRand()
	s := g.Server(p)
	s.BindHandler("/struct1", func(r *ghttp.Request) {
		if m := r.GetMap(); len(m) > 0 {
			user := new(User)
			if err := r.GetStruct(user); err != nil {
				r.Response.WriteExit(err)
			}
			r.Response.WriteExit(user.Id, user.Name, user.Pass1, user.Pass2)
		}
	})
	s.BindHandler("/struct2", func(r *ghttp.Request) {
		if m := r.GetMap(); len(m) > 0 {
			user := (*User)(nil)
			if err := r.GetStruct(&user); err != nil {
				r.Response.WriteExit(err)
			}
			if user != nil {
				r.Response.WriteExit(user.Id, user.Name, user.Pass1, user.Pass2)
			}
		}
	})
	s.BindHandler("/struct-valid", func(r *ghttp.Request) {
		if m := r.GetMap(); len(m) > 0 {
			user := new(User)
			if err := r.GetStruct(user); err != nil {
				r.Response.WriteExit(err)
			}
			if err := gvalid.CheckStruct(user, nil); err != nil {
				r.Response.WriteExit(err)
			}
		}
	})
	s.BindHandler("/parse", func(r *ghttp.Request) {
		if m := r.GetMap(); len(m) > 0 {
			var user *User
			if err := r.Parse(&user); err != nil {
				r.Response.WriteExit(err)
			}
			r.Response.WriteExit(user.Id, user.Name, user.Pass1, user.Pass2)
		}
	})
	s.SetPort(p)
	s.SetDumpRouterMap(false)
	s.Start()
	defer s.Shutdown()

	time.Sleep(100 * time.Millisecond)
	gtest.C(t, func(t *gtest.T) {
		client := ghttp.NewClient()
		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
		t.Assert(client.GetContent("/struct1", `id=1&name=john&password1=123&password2=456`), `1john123456`)
		t.Assert(client.PostContent("/struct1", `id=1&name=john&password1=123&password2=456`), `1john123456`)
		t.Assert(client.PostContent("/struct2", `id=1&name=john&password1=123&password2=456`), `1john123456`)
		t.Assert(client.PostContent("/struct2", ``), ``)
		t.Assert(client.PostContent("/struct-valid", `id=1&name=john&password1=123&password2=0`), `The value length must be between 2 and 20; 密码强度不足`)
		t.Assert(client.PostContent("/parse", `id=1&name=john&password1=123&password2=0`), `The value length must be between 2 and 20; 密码强度不足`)
		t.Assert(client.GetContent("/parse", `id=1&name=john&password1=123&password2=456`), `密码强度不足`)
		t.Assert(client.GetContent("/parse", `id=1&name=john&password1=123Abc!@#&password2=123Abc!@#`), `1john123Abc!@#123Abc!@#`)
		t.Assert(client.PostContent("/parse", `{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}`), `1john123Abc!@#123Abc!@#`)
	})
}

func Test_Params_Structs(t *testing.T) {
	type User struct {
		Id    int
		Name  string
		Time  *time.Time
		Pass1 string `p:"password1"`
		Pass2 string `p:"password2" v:"passwd1 @required|length:2,20|password3#||密码强度不足"`
	}
	p, _ := ports.PopRand()
	s := g.Server(p)
	s.BindHandler("/parse1", func(r *ghttp.Request) {
		var users []*User
		if err := r.Parse(&users); err != nil {
			r.Response.WriteExit(err)
		}
		r.Response.WriteExit(users[0].Id, users[1].Id)
	})
	s.SetPort(p)
	s.SetDumpRouterMap(false)
	s.Start()
	defer s.Shutdown()

	time.Sleep(100 * time.Millisecond)
	gtest.C(t, func(t *gtest.T) {
		client := ghttp.NewClient()
		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
		t.Assert(client.PostContent(
			"/parse1",
			`[{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}, {"id":2,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}]`),
			`12`,
		)
	})
}
<commit_msg>fix issue in unit testing case for package ghttp<commit_after>// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package ghttp_test

import (
	"fmt"
	"testing"
	"time"

	"github.com/gogf/gf/util/gvalid"

	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/net/ghttp"
	"github.com/gogf/gf/test/gtest"
)

func Test_Params_Parse(t *testing.T) {
	type User struct {
		Id   int
		Name string
		Map  map[string]interface{}
	}
	p, _ := ports.PopRand()
	s := g.Server(p)
	s.BindHandler("/parse", func(r *ghttp.Request) {
		if m := r.GetMap(); len(m) > 0 {
			var user *User
			if err := r.Parse(&user); err != nil {
				r.Response.WriteExit(err)
			}
			r.Response.WriteExit(user.Map["id"], user.Map["score"])
		}
	})
	s.SetPort(p)
	s.SetDumpRouterMap(false)
	s.Start()
	defer s.Shutdown()

	time.Sleep(100 * time.Millisecond)
	gtest.C(t, func(t *gtest.T) {
		client := ghttp.NewClient()
		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
		t.Assert(client.PostContent("/parse", `{"id":1,"name":"john","map":{"id":1,"score":100}}`), `1100`)
	})
}

func Test_Params_Parse_Attr_Pointer(t *testing.T) {
	type User struct {
		Id   *int
		Name *string
	}
	p, _ := ports.PopRand()
	s := g.Server(p)
	s.BindHandler("/parse1", func(r *ghttp.Request) {
		if m := r.GetMap(); len(m) > 0 {
			var user *User
			if err := r.Parse(&user); err != nil {
				r.Response.WriteExit(err)
			}
			r.Response.WriteExit(user.Id, user.Name)
		}
	})
	s.BindHandler("/parse2", func(r *ghttp.Request) {
		if m := r.GetMap(); len(m) > 0 {
			var user = new(User)
			if err := r.Parse(user); err != nil {
				r.Response.WriteExit(err)
			}
			r.Response.WriteExit(user.Id, user.Name)
		}
	})
	s.SetPort(p)
	s.SetDumpRouterMap(false)
	s.Start()
	defer s.Shutdown()

	time.Sleep(100 * time.Millisecond)
	gtest.C(t, func(t *gtest.T) {
		client := ghttp.NewClient()
		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
		t.Assert(client.PostContent("/parse1", `{"id":1,"name":"john"}`), `1john`)
		t.Assert(client.PostContent("/parse2", `{"id":1,"name":"john"}`), `1john`)
		t.Assert(client.PostContent("/parse2?id=1&name=john"), `1john`)
		t.Assert(client.PostContent("/parse2", `id=1&name=john`), `1john`)
	})
}

// It does not support this kind of converting yet.
//func Test_Params_Parse_Attr_SliceSlice(t *testing.T) {
//	type User struct {
//		Id     int
//		Name   string
//		Scores [][]int
//	}
//	p, _ := ports.PopRand()
//	s := g.Server(p)
//	s.BindHandler("/parse", func(r *ghttp.Request) {
//		if m := r.GetMap(); len(m) > 0 {
//			var user *User
//			if err := r.Parse(&user); err != nil {
//				r.Response.WriteExit(err)
//			}
//			r.Response.WriteExit(user.Scores)
//		}
//	})
//	s.SetPort(p)
//	s.SetDumpRouterMap(false)
//	s.Start()
//	defer s.Shutdown()
//
//	time.Sleep(100 * time.Millisecond)
//	gtest.C(t, func(t *gtest.T) {
//		client := ghttp.NewClient()
//		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
//		t.Assert(client.PostContent("/parse", `{"id":1,"name":"john","scores":[[1,2,3]]}`), `1100`)
//	})
//}

func Test_Params_Struct(t *testing.T) {
	type User struct {
		Id    int
		Name  string
		Time  *time.Time
		Pass1 string `p:"password1"`
		Pass2 string `p:"password2" v:"passwd1 @required|length:2,20|password3#||密码强度不足"`
	}
	p, _ := ports.PopRand()
	s := g.Server(p)
	s.BindHandler("/struct1", func(r *ghttp.Request) {
		if m := r.GetMap(); len(m) > 0 {
			user := new(User)
			if err := r.GetStruct(user); err != nil {
				r.Response.WriteExit(err)
			}
			r.Response.WriteExit(user.Id, user.Name, user.Pass1, user.Pass2)
		}
	})
	s.BindHandler("/struct2", func(r *ghttp.Request) {
		if m := r.GetMap(); len(m) > 0 {
			user := (*User)(nil)
			if err := r.GetStruct(&user); err != nil {
				r.Response.WriteExit(err)
			}
			if user != nil {
				r.Response.WriteExit(user.Id, user.Name, user.Pass1, user.Pass2)
			}
		}
	})
	s.BindHandler("/struct-valid", func(r *ghttp.Request) {
		if m := r.GetMap(); len(m) > 0 {
			user := new(User)
			if err := r.GetStruct(user); err != nil {
				r.Response.WriteExit(err)
			}
			if err := gvalid.CheckStruct(user, nil); err != nil {
				r.Response.WriteExit(err)
			}
		}
	})
	s.BindHandler("/parse", func(r *ghttp.Request) {
		if m := r.GetMap(); len(m) > 0 {
			var user *User
			if err := r.Parse(&user); err != nil {
				r.Response.WriteExit(err)
			}
			r.Response.WriteExit(user.Id, user.Name, user.Pass1, user.Pass2)
		}
	})
	s.SetPort(p)
	s.SetDumpRouterMap(false)
	s.Start()
	defer s.Shutdown()

	time.Sleep(100 * time.Millisecond)
	gtest.C(t, func(t *gtest.T) {
		client := ghttp.NewClient()
		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
		t.Assert(client.GetContent("/struct1", `id=1&name=john&password1=123&password2=456`), `1john123456`)
		t.Assert(client.PostContent("/struct1", `id=1&name=john&password1=123&password2=456`), `1john123456`)
		t.Assert(client.PostContent("/struct2", `id=1&name=john&password1=123&password2=456`), `1john123456`)
		t.Assert(client.PostContent("/struct2", ``), ``)
		t.Assert(client.PostContent("/struct-valid", `id=1&name=john&password1=123&password2=0`), `The passwd1 value length must be between 2 and 20; 密码强度不足`)
		t.Assert(client.PostContent("/parse", `id=1&name=john&password1=123&password2=0`), `The passwd1 value length must be between 2 and 20; 密码强度不足`)
		t.Assert(client.GetContent("/parse", `id=1&name=john&password1=123&password2=456`), `密码强度不足`)
		t.Assert(client.GetContent("/parse", `id=1&name=john&password1=123Abc!@#&password2=123Abc!@#`), `1john123Abc!@#123Abc!@#`)
		t.Assert(client.PostContent("/parse", `{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}`), `1john123Abc!@#123Abc!@#`)
	})
}

func Test_Params_Structs(t *testing.T) {
	type User struct {
		Id    int
		Name  string
		Time  *time.Time
		Pass1 string `p:"password1"`
		Pass2 string `p:"password2" v:"passwd1 @required|length:2,20|password3#||密码强度不足"`
	}
	p, _ := ports.PopRand()
	s := g.Server(p)
	s.BindHandler("/parse1", func(r *ghttp.Request) {
		var users []*User
		if err := r.Parse(&users); err != nil {
			r.Response.WriteExit(err)
		}
		r.Response.WriteExit(users[0].Id, users[1].Id)
	})
	s.SetPort(p)
	s.SetDumpRouterMap(false)
	s.Start()
	defer s.Shutdown()

	time.Sleep(100 * time.Millisecond)
	gtest.C(t, func(t *gtest.T) {
		client := ghttp.NewClient()
		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
		t.Assert(client.PostContent(
			"/parse1",
			`[{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}, {"id":2,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}]`),
			`12`,
		)
	})
}
<|endoftext|>


<commit_before>package collectors

import (
	"fmt"
	"io/ioutil"
	"os"
	"sync"
	"time"

	"github.com/StackExchange/scollector/metadata"
	"github.com/StackExchange/scollector/opentsdb"
	"github.com/StackExchange/slog"
	"gopkg.in/yaml.v1"
)

func init() {
	collectors = append(collectors, &IntervalCollector{F: puppet_disabled_linux, init: puppetInit})
	//TODO Currently No Init
	collectors = append(collectors, &IntervalCollector{F: puppet_run_summary_linux})
}

var (
	puppetEnable bool
	puppetLock   sync.Mutex
)

const (
	puppetPath       = "/var/lib/puppet/"
	puppetRunSummary = "./puppet.yaml"
	//puppetRunSummary = "/var/lib/puppet/state/last_run_summary.yaml"
	puppetDisabled = "/var/lib/puppet/state/agent_disabled.lock"
)

func puppetEnabled() (b bool) {
	puppetLock.Lock()
	b = puppetEnable
	puppetLock.Unlock()
	return
}

func puppetInit() {
	update := func() {
		_, err := os.Stat(puppetPath)
		puppetLock.Lock()
		puppetEnable = err == nil
		puppetLock.Unlock()
	}
	update()
	go func() {
		for _ = range time.Tick(time.Minute * 5) {
			update()
		}
	}()
}

func puppet_disabled_linux() opentsdb.MultiDataPoint {
	if !puppetEnabled() {
		return nil
	}
	var md opentsdb.MultiDataPoint
	disabled := 0
	if _, err := os.Stat(puppetDisabled); !os.IsNotExist(err) {
		disabled = 1
	}
	Add(&md, "puppet.disabled", disabled, nil, metadata.Unknown, metadata.None, "")
	return md
}

type PRSummary struct {
	Changes struct {
		Total float64 `yaml:"total"`
	} `yaml:"changes"`
	Events struct {
		Failure float64 `yaml:"failure"`
		Success float64 `yaml:"success"`
		Total   float64 `yaml:"total"`
	} `yaml:"events"`
	Resources struct {
		Changed         float64 `yaml:"changed"`
		Failed          float64 `yaml:"failed"`
		FailedToRestart float64 `yaml:"failed_to_restart"`
		OutOfSync       float64 `yaml:"out_of_sync"`
		Restarted       float64 `yaml:"restarted"`
		Scheduled       float64 `yaml:"scheduled"`
		Skipped         float64 `yaml:"skipped"`
		Total           float64 `yaml:"total"`
	} `yaml:"resources"`
	Time struct {
		Augeas           float64 `yaml:"augeas"`
		ConfigRetrieval  float64 `yaml:"config_retrieval"`
		Cron             float64 `yaml:"cron"`
		Exec             float64 `yaml:"exec"`
		File             float64 `yaml:"file"`
		Filebucket       float64 `yaml:"filebucket"`
		Group            float64 `yaml:"group"`
		IniSetting       float64 `yaml:"ini_setting"`
		LastRun          float64 `yaml:"last_run"`
		Package          float64 `yaml:"package"`
		Schedule         float64 `yaml:"schedule"`
		Service          float64 `yaml:"service"`
		SshAuthorizedKey float64 `yaml:"ssh_authorized_key"`
		Total            float64 `yaml:"total"`
		User             float64 `yaml:"user"`
		Yumrepo          float64 `yaml:"yumrepo"`
	} `yaml:"time"`
	Version struct {
		Config int64  `yaml:"config"`
		Puppet string `yaml:"puppet"`
	} `yaml:"version"`
}

func puppet_run_summary_linux() opentsdb.MultiDataPoint {
	s, err := ioutil.ReadFile(puppetRunSummary)
	if err != nil {
		slog.Errorln(err)
		return nil
	}
	var m PRSummary
	err = yaml.Unmarshal(s, &m)
	if err != nil {
		slog.Errorln(err)
		return nil
	}
	fmt.Println(m.Resources.Changed)
	var md opentsdb.MultiDataPoint
	//m.Version.Config appears to be the unix timestamp
	AddTS(&md, "puppet.run.resources", m.Version.Config, m.Resources.Changed, opentsdb.TagSet{"resource": "changed"}, metadata.Unknown, metadata.None, "")
	AddTS(&md, "puppet.run.resources", m.Version.Config, m.Resources.Failed, opentsdb.TagSet{"resource": "failed"}, metadata.Unknown, metadata.None, "")
	AddTS(&md, "puppet.run.resources", m.Version.Config, m.Resources.FailedToRestart, opentsdb.TagSet{"resource": "failed_to_restart"}, metadata.Unknown, metadata.None, "")
	AddTS(&md, "puppet.run.resources", m.Version.Config, m.Resources.OutOfSync, opentsdb.TagSet{"resource": "out_of_sync"}, metadata.Unknown, metadata.None, "")
	AddTS(&md, "puppet.run.resources", m.Version.Config, m.Resources.Restarted, opentsdb.TagSet{"resource": "restarted"}, metadata.Unknown, metadata.None, "")
	AddTS(&md, "puppet.run.resources", m.Version.Config, m.Resources.Scheduled, opentsdb.TagSet{"resource": "scheduled"}, metadata.Unknown, metadata.None, "")
	AddTS(&md, "puppet.run.resources", m.Version.Config, m.Resources.Changed, opentsdb.TagSet{"resource": "skipped"}, metadata.Unknown, metadata.None, "")
	AddTS(&md, "puppet.run.resources_total", m.Version.Config, m.Resources.Total, nil, metadata.Unknown, metadata.None, "")
	AddTS(&md, "puppet.run.changes", m.Version.Config, m.Changes, nil, metadata.Unknown, metadata.None, "")
	return md
}
<commit_msg>cmd/scollector: Unify puppet collectors so only one init is needed<commit_after>package collectors

import (
	"fmt"
	"io/ioutil"
	"os"
	"sync"
	"time"

	"github.com/StackExchange/scollector/metadata"
	"github.com/StackExchange/scollector/opentsdb"
	"github.com/StackExchange/slog"
	"gopkg.in/yaml.v1"
)

func init() {
	collectors = append(collectors, &IntervalCollector{F: puppet_linux, init: puppetInit})
}

var (
	puppetEnable bool
	puppetLock   sync.Mutex
)

const (
	puppetPath       = "/var/lib/puppet/"
	puppetRunSummary = "/var/lib/puppet/state/last_run_summary.yaml"
	puppetDisabled   = "/var/lib/puppet/state/agent_disabled.lock"
)

func puppetEnabled() (b bool) {
	puppetLock.Lock()
	b = puppetEnable
	puppetLock.Unlock()
	return
}

func puppetInit() {
	update := func() {
		_, err := os.Stat(puppetPath)
		puppetLock.Lock()
		puppetEnable = err == nil
		puppetLock.Unlock()
	}
	update()
	go func() {
		for _ = range time.Tick(time.Minute * 5) {
			update()
		}
	}()
}

type PRSummary struct {
	Changes struct {
		Total float64 `yaml:"total"`
	} `yaml:"changes"`
	Events struct {
		Failure float64 `yaml:"failure"`
		Success float64 `yaml:"success"`
		Total   float64 `yaml:"total"`
	} `yaml:"events"`
	Resources struct {
		Changed         float64 `yaml:"changed"`
		Failed          float64 `yaml:"failed"`
		FailedToRestart float64 `yaml:"failed_to_restart"`
		OutOfSync       float64 `yaml:"out_of_sync"`
		Restarted       float64 `yaml:"restarted"`
		Scheduled       float64 `yaml:"scheduled"`
		Skipped         float64 `yaml:"skipped"`
		Total           float64 `yaml:"total"`
	} `yaml:"resources"`
	Time struct {
		Augeas           float64 `yaml:"augeas"`
		ConfigRetrieval  float64 `yaml:"config_retrieval"`
		Cron             float64 `yaml:"cron"`
		Exec             float64 `yaml:"exec"`
		File             float64 `yaml:"file"`
		Filebucket       float64 `yaml:"filebucket"`
		Group            float64 `yaml:"group"`
		IniSetting       float64 `yaml:"ini_setting"`
		LastRun          float64 `yaml:"last_run"`
		Package          float64 `yaml:"package"`
		Schedule         float64 `yaml:"schedule"`
		Service          float64 `yaml:"service"`
		SshAuthorizedKey float64 `yaml:"ssh_authorized_key"`
		Total            float64 `yaml:"total"`
		User             float64 `yaml:"user"`
		Yumrepo          float64 `yaml:"yumrepo"`
	} `yaml:"time"`
	Version struct {
		Config int64  `yaml:"config"`
		Puppet string `yaml:"puppet"`
	} `yaml:"version"`
}

func puppet_linux() opentsdb.MultiDataPoint {
	if !puppetEnabled() {
		return nil
	}
	var md opentsdb.MultiDataPoint
	// See if puppet has been disabled (i.e. `puppet agent --disable 'Reason'`)
	disabled := 0
	if _, err := os.Stat(puppetDisabled); !os.IsNotExist(err) {
		disabled = 1
	}
	Add(&md, "puppet.disabled", disabled, nil, metadata.Unknown, metadata.None, "")
	// Gather stats from the run summary
	s, err := ioutil.ReadFile(puppetRunSummary)
	if err != nil {
		slog.Errorln(err)
		return nil
	}
	var m PRSummary
	if err = yaml.Unmarshal(s, &m); err != nil {
		slog.Errorln(err)
		return nil
	}
	fmt.Println(m.Resources.Changed)
	//m.Version.Config appears to be the unix timestamp
	AddTS(&md, "puppet.run.resources", m.Version.Config, m.Resources.Changed, opentsdb.TagSet{"resource": "changed"}, metadata.Unknown, metadata.None, "")
	AddTS(&md, "puppet.run.resources", m.Version.Config, m.Resources.Failed, opentsdb.TagSet{"resource": "failed"}, metadata.Unknown, metadata.None, "")
	AddTS(&md, "puppet.run.resources", m.Version.Config, m.Resources.FailedToRestart, opentsdb.TagSet{"resource": "failed_to_restart"}, metadata.Unknown, metadata.None, "")
	AddTS(&md, "puppet.run.resources", m.Version.Config, m.Resources.OutOfSync, opentsdb.TagSet{"resource": "out_of_sync"}, metadata.Unknown, metadata.None, "")
	AddTS(&md, "puppet.run.resources", m.Version.Config, m.Resources.Restarted, opentsdb.TagSet{"resource": "restarted"}, metadata.Unknown, metadata.None, "")
	AddTS(&md, "puppet.run.resources", m.Version.Config, m.Resources.Scheduled, opentsdb.TagSet{"resource": "scheduled"}, metadata.Unknown, metadata.None, "")
	AddTS(&md, "puppet.run.resources", m.Version.Config, m.Resources.Changed, opentsdb.TagSet{"resource": "skipped"}, metadata.Unknown, metadata.None, "")
	AddTS(&md, "puppet.run.resources_total", m.Version.Config, m.Resources.Total, nil, metadata.Unknown, metadata.None, "")
	AddTS(&md, "puppet.run.changes", m.Version.Config, m.Changes.Total, nil, metadata.Unknown, metadata.None, "")
	return md
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/nsf/termbox-go"
	"unicode/utf8"
)

func tbprint(x, y int, fg, bg termbox.Attribute, msg string) {
	for _, c := range msg {
		termbox.SetCell(x, y, c, fg, bg)
		x++
	}
}

func fill(x, y, w, h int, cell termbox.Cell) {
	for ly := 0; ly < h; ly++ {
		for lx := 0; lx < w; lx++ {
			termbox.SetCell(x+lx, y+ly, cell.Ch, cell.Fg, cell.Bg)
		}
	}
}

func rune_advance_len(r rune, pos int) int {
	if r == '\t' {
		return tabstop_length - pos % tabstop_length
	}
	return 1
}

func voffset_coffset(text []byte, boffset int) (voffset, coffset int) {
	text = text[:boffset]
	for len(text) > 0 {
		r, size := utf8.DecodeRune(text)
		text = text[size:]
		coffset += 1
		voffset += rune_advance_len(r, voffset)
	}
	return
}

func byte_slice_grow(s []byte, desired_cap int) []byte {
	if cap(s) < desired_cap {
		ns := make([]byte, len(s), desired_cap)
		copy(ns, s)
		return ns
	}
	return s
}

func byte_slice_remove(text []byte, from, to int) []byte {
	size := to - from
	copy(text[from:], text[to:])
	text = text[:len(text)-size]
	return text
}

func byte_slice_insert(text []byte, offset int, what []byte) []byte {
	n := len(text) + len(what)
	text = byte_slice_grow(text, n)
	text = text[:n]
	copy(text[offset+len(what):], text[offset:])
	copy(text[offset:], what)
	return text
}

const preferred_horizontal_threshold = 5
const tabstop_length = 8

type EditBox struct {
	text []byte
	line_voffset int
	cursor_boffset int // cursor offset in bytes
	cursor_voffset int // visual cursor offset in termbox cells
	cursor_coffset int // cursor offset in unicode code points
}

// Draws the EditBox in the given location, 'h' is not used at the moment
func (eb *EditBox) Draw(x, y, w, h int) {
	eb.AdjustVOffset(w)

	const coldef = termbox.ColorDefault
	fill(x, y, w, h, termbox.Cell{Ch:' '})

	t := eb.text
	lx := 0
	tabstop := 0
	for {
		rx := lx - eb.line_voffset
		if len(t) == 0 {
			break
		}

		if lx == tabstop {
			tabstop += tabstop_length
		}

		if rx >= w {
			termbox.SetCell(x + w - 1, y, '→',
				coldef, coldef)
			break
		}

		r, size := utf8.DecodeRune(t)
		if r == '\t' {
			for ; lx < tabstop; lx++ {
				rx = lx - eb.line_voffset
				if rx >= w {
					goto next
				}

				if rx >= 0 {
					termbox.SetCell(x + rx, y, ' ', coldef, coldef)
				}
			}
		} else {
			if rx >= 0 {
				termbox.SetCell(x + rx, y, r, coldef, coldef)
			}
			lx += 1
		}
next:
		t = t[size:]
	}

	if eb.line_voffset != 0 {
		termbox.SetCell(x, y, '←', coldef, coldef)
	}
}

// Adjusts line visual offset to a proper value depending on width
func (eb *EditBox) AdjustVOffset(width int) {
	ht := preferred_horizontal_threshold
	max_h_threshold := (width - 1) / 2
	if ht > max_h_threshold {
		ht = max_h_threshold
	}

	threshold := width - 1
	if eb.line_voffset != 0 {
		threshold = width - ht
	}
	if eb.cursor_voffset - eb.line_voffset >= threshold {
		eb.line_voffset = eb.cursor_voffset + (ht - width + 1)
	}

	if eb.line_voffset != 0 && eb.cursor_voffset - eb.line_voffset < ht {
		eb.line_voffset = eb.cursor_voffset - ht
		if eb.line_voffset < 0 {
			eb.line_voffset = 0
		}
	}
}

func (eb *EditBox) MoveCursorTo(boffset int) {
	eb.cursor_boffset = boffset
	eb.cursor_voffset, eb.cursor_coffset = voffset_coffset(eb.text, boffset)
}

func (eb *EditBox) RuneUnderCursor() (rune, int) {
	return utf8.DecodeRune(eb.text[eb.cursor_boffset:])
}

func (eb *EditBox) RuneBeforeCursor() (rune, int) {
	return utf8.DecodeLastRune(eb.text[:eb.cursor_boffset])
}

func (eb *EditBox) MoveCursorOneRuneBackward() {
	if eb.cursor_boffset == 0 {
		return
	}
	_, size := eb.RuneBeforeCursor()
	eb.MoveCursorTo(eb.cursor_boffset - size)
}

func (eb *EditBox) MoveCursorOneRuneForward() {
	if eb.cursor_boffset == len(eb.text) {
		return
	}
	_, size := eb.RuneUnderCursor()
	eb.MoveCursorTo(eb.cursor_boffset + size)
}

func (eb *EditBox) MoveCursorToBeginningOfTheLine() {
	eb.MoveCursorTo(0)
}

func (eb *EditBox) MoveCursorToEndOfTheLine() {
	eb.MoveCursorTo(len(eb.text))
}

func (eb *EditBox) DeleteRuneBackward() {
	if eb.cursor_boffset == 0 {
		return
	}

	eb.MoveCursorOneRuneBackward()
	_, size := eb.RuneUnderCursor()
	eb.text = byte_slice_remove(eb.text, eb.cursor_boffset, eb.cursor_boffset + size)
}

func (eb *EditBox) DeleteRuneForward() {
	if eb.cursor_boffset == len(eb.text) {
		return
	}
	_, size := eb.RuneUnderCursor()
	eb.text = byte_slice_remove(eb.text, eb.cursor_boffset, eb.cursor_boffset + size)
}

func (eb *EditBox) DeleteTheRestOfTheLine() {
	eb.text = eb.text[:eb.cursor_boffset]
}

func (eb *EditBox) InsertRune(r rune) {
	var buf [utf8.UTFMax]byte
	n := utf8.EncodeRune(buf[:], r)
	eb.text = byte_slice_insert(eb.text, eb.cursor_boffset, buf[:n])
	eb.MoveCursorOneRuneForward()
}

// Please, keep in mind that cursor depends on the value of line_voffset, which
// is being set on Draw() call, so.. call this method after Draw() one.
func (eb *EditBox) CursorX() int {
	return eb.cursor_voffset - eb.line_voffset
}

var edit_box EditBox
const edit_box_width = 30

func redraw_all() {
	const coldef = termbox.ColorDefault
	termbox.Clear(coldef, coldef)
	w, h := termbox.Size()

	midy := h / 2
	midx := (w - edit_box_width) / 2

	// unicode box drawing chars around the edit box
	termbox.SetCell(midx - 1, midy, '│', coldef, coldef)
	termbox.SetCell(midx + edit_box_width, midy, '│', coldef, coldef)
	termbox.SetCell(midx - 1, midy-1, '┌', coldef, coldef)
	termbox.SetCell(midx - 1, midy+1, '└', coldef, coldef)
	termbox.SetCell(midx + edit_box_width, midy-1, '┐', coldef, coldef)
	termbox.SetCell(midx + edit_box_width, midy+1, '┘', coldef, coldef)
	fill(midx, midy-1, edit_box_width, 1, termbox.Cell{Ch:'─'})
	fill(midx, midy+1, edit_box_width, 1, termbox.Cell{Ch:'─'})

	edit_box.Draw(midx, midy, edit_box_width, 1)
	termbox.SetCursor(midx + edit_box.CursorX(), midy)

	tbprint(midx+6, midy+3, coldef, coldef, "Press ESC to quit")
	termbox.Flush()
}

func main() {
	err := termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()
	termbox.SetInputMode(termbox.InputEsc)

	redraw_all()
mainloop:
	for {
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			switch ev.Key {
			case termbox.KeyEsc:
				break mainloop
			case termbox.KeyArrowLeft, termbox.KeyCtrlB:
				edit_box.MoveCursorOneRuneBackward()
			case termbox.KeyArrowRight, termbox.KeyCtrlF:
				edit_box.MoveCursorOneRuneForward()
			case termbox.KeyBackspace, termbox.KeyBackspace2:
				edit_box.DeleteRuneBackward()
			case termbox.KeyDelete, termbox.KeyCtrlD:
				edit_box.DeleteRuneForward()
			case termbox.KeyTab:
				edit_box.InsertRune('\t')
			case termbox.KeySpace:
				edit_box.InsertRune(' ')
			case termbox.KeyCtrlK:
				edit_box.DeleteTheRestOfTheLine()
			case termbox.KeyHome, termbox.KeyCtrlA:
				edit_box.MoveCursorToBeginningOfTheLine()
			case termbox.KeyEnd, termbox.KeyCtrlE:
				edit_box.MoveCursorToEndOfTheLine()
			default:
				if ev.Ch != 0 {
					edit_box.InsertRune(ev.Ch)
				}
			}
		case termbox.EventError:
			panic(ev.Err)
		}
		redraw_all()
	}
}
<commit_msg>Filter editbox.go through gofmt.<commit_after>package main

import (
	"github.com/nsf/termbox-go"
	"unicode/utf8"
)

func tbprint(x, y int, fg, bg termbox.Attribute, msg string) {
	for _, c := range msg {
		termbox.SetCell(x, y, c, fg, bg)
		x++
	}
}

func fill(x, y, w, h int, cell termbox.Cell) {
	for ly := 0; ly < h; ly++ {
		for lx := 0; lx < w; lx++ {
			termbox.SetCell(x+lx, y+ly, cell.Ch, cell.Fg, cell.Bg)
		}
	}
}

func rune_advance_len(r rune, pos int) int {
	if r == '\t' {
		return tabstop_length - pos%tabstop_length
	}
	return 1
}

func voffset_coffset(text []byte, boffset int) (voffset, coffset int) {
	text = text[:boffset]
	for len(text) > 0 {
		r, size := utf8.DecodeRune(text)
		text = text[size:]
		coffset += 1
		voffset += rune_advance_len(r, voffset)
	}
	return
}

func byte_slice_grow(s []byte, desired_cap int) []byte {
	if cap(s) < desired_cap {
		ns := make([]byte, len(s), desired_cap)
		copy(ns, s)
		return ns
	}
	return s
}

func byte_slice_remove(text []byte, from, to int) []byte {
	size := to - from
	copy(text[from:], text[to:])
	text = text[:len(text)-size]
	return text
}

func byte_slice_insert(text []byte, offset int, what []byte) []byte {
	n := len(text) + len(what)
	text = byte_slice_grow(text, n)
	text = text[:n]
	copy(text[offset+len(what):], text[offset:])
	copy(text[offset:], what)
	return text
}

const preferred_horizontal_threshold = 5
const tabstop_length = 8

type EditBox struct {
	text           []byte
	line_voffset   int
	cursor_boffset int // cursor offset in bytes
	cursor_voffset int // visual cursor offset in termbox cells
	cursor_coffset int // cursor offset in unicode code points
}

// Draws the EditBox in the given location, 'h' is not used at the moment
func (eb *EditBox) Draw(x, y, w, h int) {
	eb.AdjustVOffset(w)

	const coldef = termbox.ColorDefault
	fill(x, y, w, h, termbox.Cell{Ch: ' '})

	t := eb.text
	lx := 0
	tabstop := 0
	for {
		rx := lx - eb.line_voffset
		if len(t) == 0 {
			break
		}

		if lx == tabstop {
			tabstop += tabstop_length
		}

		if rx >= w {
			termbox.SetCell(x+w-1, y, '→',
				coldef, coldef)
			break
		}

		r, size := utf8.DecodeRune(t)
		if r == '\t' {
			for ; lx < tabstop; lx++ {
				rx = lx - eb.line_voffset
				if rx >= w {
					goto next
				}

				if rx >= 0 {
					termbox.SetCell(x+rx, y, ' ', coldef, coldef)
				}
			}
		} else {
			if rx >= 0 {
				termbox.SetCell(x+rx, y, r, coldef, coldef)
			}
			lx += 1
		}
	next:
		t = t[size:]
	}

	if eb.line_voffset != 0 {
		termbox.SetCell(x, y, '←', coldef, coldef)
	}
}

// Adjusts line visual offset to a proper value depending on width
func (eb *EditBox) AdjustVOffset(width int) {
	ht := preferred_horizontal_threshold
	max_h_threshold := (width - 1) / 2
	if ht > max_h_threshold {
		ht = max_h_threshold
	}

	threshold := width - 1
	if eb.line_voffset != 0 {
		threshold = width - ht
	}
	if eb.cursor_voffset-eb.line_voffset >= threshold {
		eb.line_voffset = eb.cursor_voffset + (ht - width + 1)
	}

	if eb.line_voffset != 0 && eb.cursor_voffset-eb.line_voffset < ht {
		eb.line_voffset = eb.cursor_voffset - ht
		if eb.line_voffset < 0 {
			eb.line_voffset = 0
		}
	}
}

func (eb *EditBox) MoveCursorTo(boffset int) {
	eb.cursor_boffset = boffset
	eb.cursor_voffset, eb.cursor_coffset = voffset_coffset(eb.text, boffset)
}

func (eb *EditBox) RuneUnderCursor() (rune, int) {
	return utf8.DecodeRune(eb.text[eb.cursor_boffset:])
}

func (eb *EditBox) RuneBeforeCursor() (rune, int) {
	return utf8.DecodeLastRune(eb.text[:eb.cursor_boffset])
}

func (eb *EditBox) MoveCursorOneRuneBackward() {
	if eb.cursor_boffset == 0 {
		return
	}
	_, size := eb.RuneBeforeCursor()
	eb.MoveCursorTo(eb.cursor_boffset - size)
}

func (eb *EditBox) MoveCursorOneRuneForward() {
	if eb.cursor_boffset == len(eb.text) {
		return
	}
	_, size := eb.RuneUnderCursor()
	eb.MoveCursorTo(eb.cursor_boffset + size)
}

func (eb *EditBox) MoveCursorToBeginningOfTheLine() {
	eb.MoveCursorTo(0)
}

func (eb *EditBox) MoveCursorToEndOfTheLine() {
	eb.MoveCursorTo(len(eb.text))
}

func (eb *EditBox) DeleteRuneBackward() {
	if eb.cursor_boffset == 0 {
		return
	}

	eb.MoveCursorOneRuneBackward()
	_, size := eb.RuneUnderCursor()
	eb.text = byte_slice_remove(eb.text, eb.cursor_boffset, eb.cursor_boffset+size)
}

func (eb *EditBox) DeleteRuneForward() {
	if eb.cursor_boffset == len(eb.text) {
		return
	}
	_, size := eb.RuneUnderCursor()
	eb.text = byte_slice_remove(eb.text, eb.cursor_boffset, eb.cursor_boffset+size)
}

func (eb *EditBox) DeleteTheRestOfTheLine() {
	eb.text = eb.text[:eb.cursor_boffset]
}

func (eb *EditBox) InsertRune(r rune) {
	var buf [utf8.UTFMax]byte
	n := utf8.EncodeRune(buf[:], r)
	eb.text = byte_slice_insert(eb.text, eb.cursor_boffset, buf[:n])
	eb.MoveCursorOneRuneForward()
}

// Please, keep in mind that cursor depends on the value of line_voffset, which
// is being set on Draw() call, so.. call this method after Draw() one.
func (eb *EditBox) CursorX() int {
	return eb.cursor_voffset - eb.line_voffset
}

var edit_box EditBox

const edit_box_width = 30

func redraw_all() {
	const coldef = termbox.ColorDefault
	termbox.Clear(coldef, coldef)
	w, h := termbox.Size()

	midy := h / 2
	midx := (w - edit_box_width) / 2

	// unicode box drawing chars around the edit box
	termbox.SetCell(midx-1, midy, '│', coldef, coldef)
	termbox.SetCell(midx+edit_box_width, midy, '│', coldef, coldef)
	termbox.SetCell(midx-1, midy-1, '┌', coldef, coldef)
	termbox.SetCell(midx-1, midy+1, '└', coldef, coldef)
	termbox.SetCell(midx+edit_box_width, midy-1, '┐', coldef, coldef)
	termbox.SetCell(midx+edit_box_width, midy+1, '┘', coldef, coldef)
	fill(midx, midy-1, edit_box_width, 1, termbox.Cell{Ch: '─'})
	fill(midx, midy+1, edit_box_width, 1, termbox.Cell{Ch: '─'})

	edit_box.Draw(midx, midy, edit_box_width, 1)
	termbox.SetCursor(midx+edit_box.CursorX(), midy)

	tbprint(midx+6, midy+3, coldef, coldef, "Press ESC to quit")
	termbox.Flush()
}

func main() {
	err := termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()
	termbox.SetInputMode(termbox.InputEsc)

	redraw_all()
mainloop:
	for {
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			switch ev.Key {
			case termbox.KeyEsc:
				break mainloop
			case termbox.KeyArrowLeft, termbox.KeyCtrlB:
				edit_box.MoveCursorOneRuneBackward()
			case termbox.KeyArrowRight, termbox.KeyCtrlF:
				edit_box.MoveCursorOneRuneForward()
			case termbox.KeyBackspace, termbox.KeyBackspace2:
				edit_box.DeleteRuneBackward()
			case termbox.KeyDelete, termbox.KeyCtrlD:
				edit_box.DeleteRuneForward()
			case termbox.KeyTab:
				edit_box.InsertRune('\t')
			case termbox.KeySpace:
				edit_box.InsertRune(' ')
			case termbox.KeyCtrlK:
				edit_box.DeleteTheRestOfTheLine()
			case termbox.KeyHome, termbox.KeyCtrlA:
				edit_box.MoveCursorToBeginningOfTheLine()
			case termbox.KeyEnd, termbox.KeyCtrlE:
				edit_box.MoveCursorToEndOfTheLine()
			default:
				if ev.Ch != 0 {
					edit_box.InsertRune(ev.Ch)
				}
			}
		case termbox.EventError:
			panic(ev.Err)
		}
		redraw_all()
	}
}
<|endoftext|>


<commit_before>package metadata

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"time"

	"github.com/mackerelio/mackerel-agent/config"
	"github.com/mackerelio/mackerel-agent/logging"
)

var logger = logging.GetLogger("metadata")

// Generator generates metadata
type Generator struct {
	Name         string
	Config       *config.MetadataPlugin
	Tempfile     string
	PrevMetadata interface{}
}

// Fetch invokes the command and returns the result
func (g *Generator) Fetch() (interface{}, error) {
	message, stderr, exitCode, err := g.Config.Run()

	if err != nil {
		logger.Warningf("Error occurred while executing a metadata plugin %q: %s", g.Name, err.Error())
		return nil, err
	}

	if stderr != "" {
		logger.Warningf("metadata plugin %q outputs stderr: %s", g.Name, stderr)
	}

	if exitCode != 0 {
		return nil, fmt.Errorf("exits with: %d", exitCode)
	}

	var metadata interface{}
	if err := json.Unmarshal([]byte(message), &metadata); err != nil {
		return nil, fmt.Errorf("outputs invalid JSON: %v", message)
	}

	return metadata, nil
}

// Differs returns whether the metadata has been changed or not
func (g *Generator) Differs(metadata interface{}) bool {
	if g.PrevMetadata == nil {
		g.LoadFromFile()
	}
	return !reflect.DeepEqual(g.PrevMetadata, metadata)
}

// LoadFromFile loads the previous metadata from file
func (g *Generator) LoadFromFile() {
	data, err := ioutil.ReadFile(g.Tempfile)
	if err != nil { // maybe initial state
		return
	}
	var metadata interface{}
	if err := json.Unmarshal(data, &metadata); err != nil {
		logger.Warningf("metadata plugin %q detected a invalid json in temporary file: %s", g.Name, string(data))
		return
	}
	g.PrevMetadata = metadata
}

// Save stores the metadata locally
func (g *Generator) Save(metadata interface{}) error {
	g.PrevMetadata = metadata
	data, err := json.Marshal(metadata)
	if err != nil {
		return fmt.Errorf("failed to marshal the metadata to json: %v %s", metadata, err.Error())
	}
	if err = writeFileAtomically(g.Tempfile, data); err != nil {
		return fmt.Errorf("failed to write the metadata to temporary file: %v %s", metadata, err.Error())
	}
	return nil
}

func writeFileAtomically(f string, contents []byte) error {
	tmpf, err := ioutil.TempFile("", "")
	if err != nil {
		return err
	}
	defer os.Remove(tmpf.Name())
	_, err = tmpf.Write(contents)
	if err != nil {
		return err
	}
	tmpf.Close()
	return os.Rename(tmpf.Name(), f)
}

const defaultExecutionInterval = 10 * time.Minute

// Interval calculates the time interval of command execution
func (g *Generator) Interval() time.Duration {
	if g.Config.ExecutionInterval == nil {
		return defaultExecutionInterval
	}
	interval := time.Duration(*g.Config.ExecutionInterval) * time.Minute
	if interval < 1*time.Minute {
		return 1 * time.Minute
	}
	return interval
}
<commit_msg>error when temporary file is not specified<commit_after>package metadata

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"time"

	"github.com/mackerelio/mackerel-agent/config"
	"github.com/mackerelio/mackerel-agent/logging"
)

var logger = logging.GetLogger("metadata")

// Generator generates metadata
type Generator struct {
	Name         string
	Config       *config.MetadataPlugin
	Tempfile     string
	PrevMetadata interface{}
}

// Fetch invokes the command and returns the result
func (g *Generator) Fetch() (interface{}, error) {
	message, stderr, exitCode, err := g.Config.Run()

	if err != nil {
		logger.Warningf("Error occurred while executing a metadata plugin %q: %s", g.Name, err.Error())
		return nil, err
	}

	if stderr != "" {
		logger.Warningf("metadata plugin %q outputs stderr: %s", g.Name, stderr)
	}

	if exitCode != 0 {
		return nil, fmt.Errorf("exits with: %d", exitCode)
	}

	var metadata interface{}
	if err := json.Unmarshal([]byte(message), &metadata); err != nil {
		return nil, fmt.Errorf("outputs invalid JSON: %v", message)
	}

	return metadata, nil
}

// Differs returns whether the metadata has been changed or not
func (g *Generator) Differs(metadata interface{}) bool {
	if g.PrevMetadata == nil {
		g.LoadFromFile()
	}
	return !reflect.DeepEqual(g.PrevMetadata, metadata)
}

// LoadFromFile loads the previous metadata from file
func (g *Generator) LoadFromFile() {
	data, err := ioutil.ReadFile(g.Tempfile)
	if err != nil { // maybe initial state
		return
	}
	var metadata interface{}
	if err := json.Unmarshal(data, &metadata); err != nil {
		logger.Warningf("metadata plugin %q detected a invalid json in temporary file: %s", g.Name, string(data))
		return
	}
	g.PrevMetadata = metadata
}

// Save stores the metadata locally
func (g *Generator) Save(metadata interface{}) error {
	g.PrevMetadata = metadata
	data, err := json.Marshal(metadata)
	if err != nil {
		return fmt.Errorf("failed to marshal the metadata to json: %v %s", metadata, err.Error())
	}
	if g.Tempfile == "" {
		return fmt.Errorf("specify the name of temporary file")
	}
	if err = writeFileAtomically(g.Tempfile, data); err != nil {
		return fmt.Errorf("failed to write the metadata to temporary file: %v %s", metadata, err.Error())
	}
	return nil
}

func writeFileAtomically(f string, contents []byte) error {
	tmpf, err := ioutil.TempFile("", "")
	if err != nil {
		return err
	}
	defer os.Remove(tmpf.Name())
	_, err = tmpf.Write(contents)
	if err != nil {
		return err
	}
	tmpf.Close()
	return os.Rename(tmpf.Name(), f)
}

const defaultExecutionInterval = 10 * time.Minute

// Interval calculates the time interval of command execution
func (g *Generator) Interval() time.Duration {
	if g.Config.ExecutionInterval == nil {
		return defaultExecutionInterval
	}
	interval := time.Duration(*g.Config.ExecutionInterval) * time.Minute
	if interval < 1*time.Minute {
		return 1 * time.Minute
	}
	return interval
}
<|endoftext|>


<commit_before>package core

import (
	"github.com/af83/edwig/logger"
	"github.com/af83/edwig/model"
	"github.com/af83/edwig/siri"
)

type BroadcastStopMonitoringBuilder struct {
	model.ClockConsumer
	model.UUIDConsumer

	StopVisitTypes string
	MonitoringRef  string

	tx                         *model.Transaction
	siriPartner                *SIRIPartner
	referenceGenerator         *IdentifierGenerator
	stopAreareferenceGenerator *IdentifierGenerator
	dataFrameGenerator         *IdentifierGenerator
	remoteObjectidKind         string
}

func NewBroadcastStopMonitoringBuilder(tx *model.Transaction, siriPartner *SIRIPartner, connector string) *BroadcastStopMonitoringBuilder {
	return &BroadcastStopMonitoringBuilder{
		tx:                         tx,
		siriPartner:                siriPartner,
		referenceGenerator:         siriPartner.IdentifierGenerator("reference_identifier"),
		stopAreareferenceGenerator: siriPartner.IdentifierGenerator("reference_stop_area_identifier"),
		dataFrameGenerator:         siriPartner.IdentifierGenerator("data_frame_identifier"),
		remoteObjectidKind:         siriPartner.Partner().RemoteObjectIDKind(connector),
	}
}

func (builder *BroadcastStopMonitoringBuilder) BuildCancelledStopVisit(stopVisit model.StopVisit) *siri.SIRICancelledStopVisit {
	vehicleJourney, ok := builder.tx.Model().VehicleJourneys().Find(stopVisit.VehicleJourneyId)
	if !ok {
		logger.Log.Printf("Ignore CancelledStopVisit %s without Vehiclejourney", stopVisit.Id())
		return nil
	}
	line, ok := builder.tx.Model().Lines().Find(vehicleJourney.LineId)
	if !ok {
		logger.Log.Printf("Ignore CancelledStopVisit %s without Line", stopVisit.Id())
		return nil
	}
	lineObjectId, ok := line.ObjectID(builder.remoteObjectidKind)
	if !ok {
		logger.Log.Printf("Ignore CancelledStopVisit %s with Line without correct ObjectID", stopVisit.Id())
		return nil
	}

	itemIdentifier, ok := builder.getItemIdentifier(stopVisit)
	if !ok {
		return nil
	}

	dataVehicleJourneyRef, ok := builder.dataVehicleJourneyRef(vehicleJourney)
	if !ok {
		return nil
	}

	modelDate := builder.tx.Model().Date()

	cancelledStopVisit := &siri.SIRICancelledStopVisit{
		RecordedAtTime:         stopVisit.RecordedAt,
		ItemRef:                itemIdentifier,
		MonitoringRef:          builder.MonitoringRef,
		LineRef:                lineObjectId.Value(),
		DatedVehicleJourneyRef: dataVehicleJourneyRef,
		DataFrameRef:           builder.dataFrameGenerator.NewIdentifier(IdentifierAttributes{Id: modelDate.String()}),
		PublishedLineName:      line.Name,
	}

	return cancelledStopVisit
}

func (builder *BroadcastStopMonitoringBuilder) BuildMonitoredStopVisit(stopVisit model.StopVisit) *siri.SIRIMonitoredStopVisit {
	stopPointRef, ok := builder.tx.Model().StopAreas().Find(stopVisit.StopAreaId)
	if !ok {
		logger.Log.Printf("Ignore StopVisit %s without StopArea", stopVisit.Id())
		return nil
	}
	stopPointRefObjectId, ok := stopPointRef.ObjectID(builder.remoteObjectidKind)
	if !ok {
		logger.Log.Printf("Ignore StopVisit %s with StopArea without correct ObjectID", stopVisit.Id())
		return nil
	}
	vehicleJourney, ok := builder.tx.Model().VehicleJourneys().Find(stopVisit.VehicleJourneyId)
	if !ok {
		logger.Log.Printf("Ignore StopVisit %s without Vehiclejourney", stopVisit.Id())
		return nil
	}
	line, ok := builder.tx.Model().Lines().Find(vehicleJourney.LineId)
	if !ok {
		logger.Log.Printf("Ignore StopVisit %s without Line", stopVisit.Id())
		return nil
	}
	lineObjectId, ok := line.ObjectID(builder.remoteObjectidKind)
	if !ok {
		logger.Log.Printf("Ignore StopVisit %s with Line without correct ObjectID", stopVisit.Id())
		return nil
	}

	itemIdentifier, ok := builder.getItemIdentifier(stopVisit)
	if !ok {
		return nil
	}

	schedules := stopVisit.Schedules

	dataVehicleJourneyRef, ok := builder.dataVehicleJourneyRef(vehicleJourney)
	if !ok {
		return nil
	}

	modelDate := builder.tx.Model().Date()

	monitoredStopVisit := &siri.SIRIMonitoredStopVisit{
		ItemIdentifier: itemIdentifier,
		MonitoringRef:  builder.MonitoringRef,
		StopPointRef:   stopPointRefObjectId.Value(),
		StopPointName:  stopPointRef.Name,

		VehicleJourneyName:     vehicleJourney.Name,
		Monitored:              vehicleJourney.Monitored,
		LineRef:                lineObjectId.Value(),
		DatedVehicleJourneyRef: dataVehicleJourneyRef,
		DataFrameRef:           builder.dataFrameGenerator.NewIdentifier(IdentifierAttributes{Id: modelDate.String()}),
		RecordedAt:             stopVisit.RecordedAt,
		PublishedLineName:      line.Name,
		DepartureStatus:        string(stopVisit.DepartureStatus),
		ArrivalStatus:          string(stopVisit.ArrivalStatus),
		Order:                  stopVisit.PassageOrder,
		VehicleAtStop:          stopVisit.VehicleAtStop,
		Attributes:             make(map[string]map[string]string),
		References:             make(map[string]map[string]model.Reference),
	}
	if !stopPointRef.Monitored {
		monitoredStopVisit.Monitored = false
	}

	if stopVisit.ArrivalStatus != model.STOP_VISIT_ARRIVAL_CANCELLED && builder.StopVisitTypes != "departures" {
		monitoredStopVisit.AimedArrivalTime = schedules.Schedule(model.STOP_VISIT_SCHEDULE_AIMED).ArrivalTime()
		if monitoredStopVisit.Monitored {
			monitoredStopVisit.ExpectedArrivalTime = schedules.Schedule(model.STOP_VISIT_SCHEDULE_EXPECTED).ArrivalTime()
			monitoredStopVisit.ActualArrivalTime = schedules.Schedule(model.STOP_VISIT_SCHEDULE_ACTUAL).ArrivalTime()
		}
	}

	if stopVisit.DepartureStatus != model.STOP_VISIT_DEPARTURE_CANCELLED && builder.StopVisitTypes != "arrivals" {
		monitoredStopVisit.AimedDepartureTime = schedules.Schedule(model.STOP_VISIT_SCHEDULE_AIMED).DepartureTime()
		if monitoredStopVisit.Monitored {
			monitoredStopVisit.ExpectedDepartureTime = schedules.Schedule(model.STOP_VISIT_SCHEDULE_EXPECTED).DepartureTime()
			monitoredStopVisit.ActualDepartureTime = schedules.Schedule(model.STOP_VISIT_SCHEDULE_ACTUAL).DepartureTime()
		}
	}

	vehicleJourneyRefCopy := vehicleJourney.References.Copy()
	stopVisitRefCopy := stopVisit.References.Copy()

	builder.resolveVJReferences(vehicleJourneyRefCopy)

	builder.resolveOperator(stopVisitRefCopy)

	monitoredStopVisit.Attributes["StopVisitAttributes"] = stopVisit.Attributes
	monitoredStopVisit.References["StopVisitReferences"] = stopVisitRefCopy.GetReferences()

	monitoredStopVisit.Attributes["VehicleJourneyAttributes"] = vehicleJourney.Attributes
	monitoredStopVisit.References["VehicleJourney"] = vehicleJourneyRefCopy.GetReferences()

	return monitoredStopVisit
}

func (builder *BroadcastStopMonitoringBuilder) getItemIdentifier(stopVisit model.StopVisit) (string, bool) {
	var itemIdentifier string

	stopVisitId, ok := stopVisit.ObjectID(builder.remoteObjectidKind)
	if ok {
		itemIdentifier = stopVisitId.Value()
	} else {
		defaultObjectID, ok := stopVisit.ObjectID("_default")
		if !ok {
			logger.Log.Printf("Ignore StopVisit %s without default ObjectID", stopVisit.Id())
			return "", false
		}
		itemIdentifier = builder.referenceGenerator.NewIdentifier(IdentifierAttributes{Type: "Item", Default: defaultObjectID.Value()})
	}
	return itemIdentifier, true
}

func (builder *BroadcastStopMonitoringBuilder) dataVehicleJourneyRef(vehicleJourney model.VehicleJourney) (string, bool) {
	vehicleJourneyId, ok := vehicleJourney.ObjectID(builder.remoteObjectidKind)

	var dataVehicleJourneyRef string
	if ok {
		dataVehicleJourneyRef = vehicleJourneyId.Value()
	} else {
		defaultObjectID, ok := vehicleJourney.ObjectID("_default")
		if !ok {
			return "", false
		}
		dataVehicleJourneyRef = builder.referenceGenerator.NewIdentifier(IdentifierAttributes{Type: "VehicleJourney", Default: defaultObjectID.Value()})
	}
	return dataVehicleJourneyRef, true
}

func (builder *BroadcastStopMonitoringBuilder) resolveOperator(references model.References) {
	operatorRef, ok := references.Get("OperatorRef")
	if !ok {
		return
	}
	operator, ok := builder.tx.Model().Operators().FindByObjectId(*operatorRef.ObjectId)
	if !ok {
		return
	}
	obj, ok := operator.ObjectID(builder.remoteObjectidKind)
	if !ok {
		return
	}
	ref, _ := references.Get("OperatorRef")
	ref.ObjectId.SetValue(obj.Value())
}

func (builder *BroadcastStopMonitoringBuilder) resolveVJReferences(references model.References) {
	for _, refType := range []string{"RouteRef", "JourneyPatternRef", "DatedVehicleJourneyRef"} {
		reference, ok := references.Get(refType)
		if !ok {
			continue
		}
		reference.ObjectId.SetValue(builder.referenceGenerator.NewIdentifier(IdentifierAttributes{Type: refType[:len(refType)-3], Default: reference.GetSha1()}))
	}
	for _, refType := range []string{"PlaceRef", "OriginRef", "DestinationRef"} {
		reference, ok := references.Get(refType)
		if !ok || reference.ObjectId == nil {
			continue
		}
		builder.resolveStopAreaRef(&reference)
	}
}

func (builder *BroadcastStopMonitoringBuilder) resolveStopAreaRef(reference *model.Reference) {
	stopArea, ok := builder.tx.Model().StopAreas().FindByObjectId(*reference.ObjectId)
	if ok {
		obj, ok := stopArea.ObjectID(builder.remoteObjectidKind)
		if ok {
			reference.ObjectId.SetValue(obj.Value())
			return
		}
	}
	reference.ObjectId.SetValue(builder.stopAreareferenceGenerator.NewIdentifier(IdentifierAttributes{Default: reference.GetSha1()}))
}
<commit_msg>Refs #5013 RATPDEV requested this change, it seems like SAE can extrapolate the expectedTime using previously collected data of the day<commit_after>package core

import (
	"github.com/af83/edwig/logger"
	"github.com/af83/edwig/model"
	"github.com/af83/edwig/siri"
)

type BroadcastStopMonitoringBuilder struct {
	model.ClockConsumer
	model.UUIDConsumer

	StopVisitTypes string
	MonitoringRef  string

	tx                         *model.Transaction
	siriPartner                *SIRIPartner
	referenceGenerator         *IdentifierGenerator
	stopAreareferenceGenerator *IdentifierGenerator
	dataFrameGenerator         *IdentifierGenerator
	remoteObjectidKind         string
}

func NewBroadcastStopMonitoringBuilder(tx *model.Transaction, siriPartner *SIRIPartner, connector string) *BroadcastStopMonitoringBuilder {
	return &BroadcastStopMonitoringBuilder{
		tx:                         tx,
		siriPartner:                siriPartner,
		referenceGenerator:         siriPartner.IdentifierGenerator("reference_identifier"),
		stopAreareferenceGenerator: siriPartner.IdentifierGenerator("reference_stop_area_identifier"),
		dataFrameGenerator:         siriPartner.IdentifierGenerator("data_frame_identifier"),
		remoteObjectidKind:         siriPartner.Partner().RemoteObjectIDKind(connector),
	}
}

func (builder *BroadcastStopMonitoringBuilder) BuildCancelledStopVisit(stopVisit model.StopVisit) *siri.SIRICancelledStopVisit {
	vehicleJourney, ok := builder.tx.Model().VehicleJourneys().Find(stopVisit.VehicleJourneyId)
	if !ok {
		logger.Log.Printf("Ignore CancelledStopVisit %s without Vehiclejourney", stopVisit.Id())
		return nil
	}
	line, ok := builder.tx.Model().Lines().Find(vehicleJourney.LineId)
	if !ok {
		logger.Log.Printf("Ignore CancelledStopVisit %s without Line", stopVisit.Id())
		return nil
	}
	lineObjectId, ok := line.ObjectID(builder.remoteObjectidKind)
	if !ok {
		logger.Log.Printf("Ignore CancelledStopVisit %s with Line without correct ObjectID", stopVisit.Id())
		return nil
	}

	itemIdentifier, ok := builder.getItemIdentifier(stopVisit)
	if !ok {
		return nil
	}

	dataVehicleJourneyRef, ok := builder.dataVehicleJourneyRef(vehicleJourney)
	if !ok {
		return nil
	}

	modelDate := builder.tx.Model().Date()

	cancelledStopVisit := &siri.SIRICancelledStopVisit{
		RecordedAtTime:         stopVisit.RecordedAt,
		ItemRef:                itemIdentifier,
		MonitoringRef:          builder.MonitoringRef,
		LineRef:                lineObjectId.Value(),
		DatedVehicleJourneyRef: dataVehicleJourneyRef,
		DataFrameRef:           builder.dataFrameGenerator.NewIdentifier(IdentifierAttributes{Id: modelDate.String()}),
		PublishedLineName:      line.Name,
	}

	return cancelledStopVisit
}

func (builder *BroadcastStopMonitoringBuilder) BuildMonitoredStopVisit(stopVisit model.StopVisit) *siri.SIRIMonitoredStopVisit {
	stopPointRef, ok := builder.tx.Model().StopAreas().Find(stopVisit.StopAreaId)
	if !ok {
		logger.Log.Printf("Ignore StopVisit %s without StopArea", stopVisit.Id())
		return nil
	}
	stopPointRefObjectId, ok := stopPointRef.ObjectID(builder.remoteObjectidKind)
	if !ok {
		logger.Log.Printf("Ignore StopVisit %s with StopArea without correct ObjectID", stopVisit.Id())
		return nil
	}
	vehicleJourney, ok := builder.tx.Model().VehicleJourneys().Find(stopVisit.VehicleJourneyId)
	if !ok {
		logger.Log.Printf("Ignore StopVisit %s without Vehiclejourney", stopVisit.Id())
		return nil
	}
	line, ok := builder.tx.Model().Lines().Find(vehicleJourney.LineId)
	if !ok {
		logger.Log.Printf("Ignore StopVisit %s without Line", stopVisit.Id())
		return nil
	}
	lineObjectId, ok := line.ObjectID(builder.remoteObjectidKind)
	if !ok {
		logger.Log.Printf("Ignore StopVisit %s with Line without correct ObjectID", stopVisit.Id())
		return nil
	}

	itemIdentifier, ok := builder.getItemIdentifier(stopVisit)
	if !ok {
		return nil
	}

	schedules := stopVisit.Schedules

	dataVehicleJourneyRef, ok := builder.dataVehicleJourneyRef(vehicleJourney)
	if !ok {
		return nil
	}

	modelDate := builder.tx.Model().Date()

	monitoredStopVisit := &siri.SIRIMonitoredStopVisit{
		ItemIdentifier: itemIdentifier,
		MonitoringRef:  builder.MonitoringRef,
		StopPointRef:   stopPointRefObjectId.Value(),
		StopPointName:  stopPointRef.Name,

		VehicleJourneyName:     vehicleJourney.Name,
		Monitored:              vehicleJourney.Monitored,
		LineRef:                lineObjectId.Value(),
		DatedVehicleJourneyRef: dataVehicleJourneyRef,
		DataFrameRef:           builder.dataFrameGenerator.NewIdentifier(IdentifierAttributes{Id: modelDate.String()}),
		RecordedAt:             stopVisit.RecordedAt,
		PublishedLineName:      line.Name,
		DepartureStatus:        string(stopVisit.DepartureStatus),
		ArrivalStatus:          string(stopVisit.ArrivalStatus),
		Order:                  stopVisit.PassageOrder,
		VehicleAtStop:          stopVisit.VehicleAtStop,
		Attributes:             make(map[string]map[string]string),
		References:             make(map[string]map[string]model.Reference),
	}
	if !stopPointRef.Monitored {
		monitoredStopVisit.Monitored = false
	}

	if stopVisit.ArrivalStatus != model.STOP_VISIT_ARRIVAL_CANCELLED && builder.StopVisitTypes != "departures" {
		monitoredStopVisit.AimedArrivalTime = schedules.Schedule(model.STOP_VISIT_SCHEDULE_AIMED).ArrivalTime()
		monitoredStopVisit.ExpectedArrivalTime = schedules.Schedule(model.STOP_VISIT_SCHEDULE_EXPECTED).ArrivalTime()
		if monitoredStopVisit.Monitored {
			monitoredStopVisit.ActualArrivalTime = schedules.Schedule(model.STOP_VISIT_SCHEDULE_ACTUAL).ArrivalTime()
		}
	}

	if stopVisit.DepartureStatus != model.STOP_VISIT_DEPARTURE_CANCELLED && builder.StopVisitTypes != "arrivals" {
		monitoredStopVisit.AimedDepartureTime = schedules.Schedule(model.STOP_VISIT_SCHEDULE_AIMED).DepartureTime()
		monitoredStopVisit.ExpectedDepartureTime = schedules.Schedule(model.STOP_VISIT_SCHEDULE_EXPECTED).DepartureTime()
		if monitoredStopVisit.Monitored {
			monitoredStopVisit.ActualDepartureTime = schedules.Schedule(model.STOP_VISIT_SCHEDULE_ACTUAL).DepartureTime()
		}
	}

	vehicleJourneyRefCopy := vehicleJourney.References.Copy()
	stopVisitRefCopy := stopVisit.References.Copy()

	builder.resolveVJReferences(vehicleJourneyRefCopy)

	builder.resolveOperator(stopVisitRefCopy)

	monitoredStopVisit.Attributes["StopVisitAttributes"] = stopVisit.Attributes
	monitoredStopVisit.References["StopVisitReferences"] = stopVisitRefCopy.GetReferences()

	monitoredStopVisit.Attributes["VehicleJourneyAttributes"] = vehicleJourney.Attributes
	monitoredStopVisit.References["VehicleJourney"] = vehicleJourneyRefCopy.GetReferences()

	return monitoredStopVisit
}

func (builder *BroadcastStopMonitoringBuilder) getItemIdentifier(stopVisit model.StopVisit) (string, bool) {
	var itemIdentifier string

	stopVisitId, ok := stopVisit.ObjectID(builder.remoteObjectidKind)
	if ok {
		itemIdentifier = stopVisitId.Value()
	} else {
		defaultObjectID, ok := stopVisit.ObjectID("_default")
		if !ok {
			logger.Log.Printf("Ignore StopVisit %s without default ObjectID", stopVisit.Id())
			return "", false
		}
		itemIdentifier = builder.referenceGenerator.NewIdentifier(IdentifierAttributes{Type: "Item", Default: defaultObjectID.Value()})
	}
	return itemIdentifier, true
}

func (builder *BroadcastStopMonitoringBuilder) dataVehicleJourneyRef(vehicleJourney model.VehicleJourney) (string, bool) {
	vehicleJourneyId, ok := vehicleJourney.ObjectID(builder.remoteObjectidKind)

	var dataVehicleJourneyRef string
	if ok {
		dataVehicleJourneyRef = vehicleJourneyId.Value()
	} else {
		defaultObjectID, ok := vehicleJourney.ObjectID("_default")
		if !ok {
			return "", false
		}
		dataVehicleJourneyRef = builder.referenceGenerator.NewIdentifier(IdentifierAttributes{Type: "VehicleJourney", Default: defaultObjectID.Value()})
	}
	return dataVehicleJourneyRef, true
}

func (builder *BroadcastStopMonitoringBuilder) resolveOperator(references model.References) {
	operatorRef, ok := references.Get("OperatorRef")
	if !ok {
		return
	}
	operator, ok := builder.tx.Model().Operators().FindByObjectId(*operatorRef.ObjectId)
	if !ok {
		return
	}
	obj, ok := operator.ObjectID(builder.remoteObjectidKind)
	if !ok {
		return
	}
	ref, _ := references.Get("OperatorRef")
	ref.ObjectId.SetValue(obj.Value())
}

func (builder *BroadcastStopMonitoringBuilder) resolveVJReferences(references model.References) {
	for _, refType := range []string{"RouteRef", "JourneyPatternRef", "DatedVehicleJourneyRef"} {
		reference, ok := references.Get(refType)
		if !ok {
			continue
		}
		reference.ObjectId.SetValue(builder.referenceGenerator.NewIdentifier(IdentifierAttributes{Type: refType[:len(refType)-3], Default: reference.GetSha1()}))
	}
	for _, refType := range []string{"PlaceRef", "OriginRef", "DestinationRef"} {
		reference, ok := references.Get(refType)
		if !ok || reference.ObjectId == nil {
			continue
		}
		builder.resolveStopAreaRef(&reference)
	}
}

func (builder *BroadcastStopMonitoringBuilder) resolveStopAreaRef(reference *model.Reference) {
	stopArea, ok := builder.tx.Model().StopAreas().FindByObjectId(*reference.ObjectId)
	if ok {
		obj, ok := stopArea.ObjectID(builder.remoteObjectidKind)
		if ok {
			reference.ObjectId.SetValue(obj.Value())
			return
		}
	}
	reference.ObjectId.SetValue(builder.stopAreareferenceGenerator.NewIdentifier(IdentifierAttributes{Default: reference.GetSha1()}))
}
<|endoftext|>