<commit_before>package flux

import (
	"bytes"
	"errors"
	"io"
	"io/ioutil"
	"time"
)

type (

	//StreamMod is a function that modifies a data []byte
	StreamMod func(bu []byte) []byte

	//StreamInterface define the interface method rules for streams
	StreamInterface interface {
		Subscribe(func(interface{}, *Sub)) *Sub
		StreamMod(fx func(interface{}) interface{}) (StreamInterface, error)
		Stream() (StreamInterface, error)
		StreamWriter(io.Writer) *Sub
		StreamReader(io.Reader) *Sub
		Write([]byte) (int, error)
		Read([]byte) (int, error)
		Emit(interface{}) (int, error)
		Close() error
		OnClosed() ActionInterface
	}

	//BaseStream defines a basic stream structure
	BaseStream struct {
		push   Pipe
		closed ActionInterface
		sub    *Sub
	}

	//ByteStream provides a buffer back streamer
	ByteStream struct {
		StreamInterface
		buf *bytes.Buffer
	}

	//WrapByteStream provides a ByteStream wrapped around a reader
	WrapByteStream struct {
		StreamInterface
		reader io.ReadCloser
		base   StreamInterface
	}

	//TimedStream provides a bytestream that checks for inactivity
	//on reads or writes and if its passed its duration time,it closes
	//the stream
	TimedStream struct {
		StreamInterface
		Idle *TimeWait
	}
)

var (
	//ErrWrongType denotes a wrong-type assertion
	ErrWrongType = errors.New("Wrong Type!")
	//ErrReadMisMatch denotes when the total bytes length is not read
	ErrReadMisMatch = errors.New("Length MisMatch,Data not fully Read")
)

//TimedStreamFrom returns a new TimedByteStream instance
func TimedStreamFrom(ns StreamInterface, max int, ms time.Duration) *TimedStream {
	ts := &TimedStream{ns, NewTimeWait(max, ms)}

	ts.Idle.Then().WhenOnly(func(_ interface{}) {
		ts.StreamInterface.Close()
	})

	return ts
}

//Emit push data into the stream
func (b *TimedStream) Emit(data interface{}) (int, error) {
	n, err := b.StreamInterface.Emit(data)
	b.Idle.Add()
	return n, err
}

//Write push a byte slice into the stream
func (b *TimedStream) Write(data []byte) (int, error) {
	n, err := b.StreamInterface.Write(data)
	b.Idle.Add()
	return n, err
}

//Read is supposed to read data into the supplied byte slice,
//but for a BaseStream this is a no-op and a 0 is returned as read length
func (b *TimedStream) Read(data []byte) (int, error) {
	n, err := b.StreamInterface.Read(data)
	b.Idle.Add()
	return n, err
}

//ReaderModByteStreamBy returns a bytestream that its input will be modded
func ReaderModByteStreamBy(base StreamInterface, fx StreamMod, rd io.ReadCloser) (*WrapByteStream, error) {
	bs, err := DoByteStream(base, fx)
	return &WrapByteStream{bs, rd, base}, err
}

//ReaderModByteStream returns a bytestream that its input will be modded
func ReaderModByteStream(fx StreamMod, rd io.ReadCloser) (*WrapByteStream, error) {
	ns := NewByteStream()
	bs, err := DoByteStream(ns, fx)
	return &WrapByteStream{bs, rd, ns}, err
}

//ReaderByteStream returns a bytestream that wraps a reader closer
func ReaderByteStream(rd io.Reader) *WrapByteStream {
	return &WrapByteStream{NewByteStream(), ioutil.NopCloser(rd), nil}
}

//ReadCloserByteStream returns a bytestream that wraps a reader closer
func ReadCloserByteStream(rd io.ReadCloser) *WrapByteStream {
	return &WrapByteStream{NewByteStream(), rd, nil}
}

//Close closes the stream and its associated reader
func (b *WrapByteStream) Close() error {
	err := b.reader.Close()
	_ = b.StreamInterface.Close()
	return err
}

//Read reads the data from internal wrap ReadCloser if it fails,
//it attempts to read from the ByteStream buffer and returns
func (b *WrapByteStream) Read(data []byte) (int, error) {
	nx, err := b.reader.Read(data)

	if err == nil {
		var cd []byte
		copy(cd, data)
		b.Write(cd)
	}

	return nx, err
}

//NewBaseStream returns a basestream instance
func NewBaseStream() *BaseStream {
	return &BaseStream{PushSocket(0), NewAction(), nil}
}

//OnClosed returns an action that gets fullfilled when the stream is closed
func (b *BaseStream) OnClosed() ActionInterface {
	return b.closed.Wrap()
}

//Close closes the stream
func (b *BaseStream) Close() error {
	if b.sub != nil {
		b.sub.Close()
	}
	b.push.Close()
	b.closed.Fullfill(true)
	return nil
}

//Stream creates a new StreamInterface and pipes all current data into that stream
func (b *BaseStream) Stream() (StreamInterface, error) {
	return &BaseStream{PushSocketWith(b.push), NewAction(), nil}, nil
}

//StreamMod provides a new stream that mods the internal details of these streams byte
func (b *BaseStream) StreamMod(fn func(v interface{}) interface{}) (StreamInterface, error) {
	return &BaseStream{DoPushSocket(b.push, func(v interface{}, sock SocketInterface) {
		sock.Emit(fn(v))
	}), NewAction(), nil}, nil
}

//StreamReader provides a subscription into the stream to stream to a reader
func (b *BaseStream) StreamReader(w io.Reader) *Sub {
	return b.Subscribe(func(data interface{}, sub *Sub) {
		buff, ok := data.([]byte)

		if !ok {

			str, ok := data.(string)

			if !ok {
				return
			}

			w.Read([]byte(str))
		}

		_, _ = w.Read(buff)
	})
}

//StreamWriter provides a subscription into the stream into a writer
func (b *BaseStream) StreamWriter(w io.Writer) *Sub {
	return b.Subscribe(func(data interface{}, sub *Sub) {
		buff, ok := data.([]byte)

		if !ok {

			str, ok := data.(string)

			if !ok {
				return
			}

			w.Write([]byte(str))
		}

		_, _ = w.Write(buff)
	})
}

//Subscribe provides a subscription into the stream
func (b *BaseStream) Subscribe(fn func(interface{}, *Sub)) *Sub {
	return b.push.Subscribe(fn)
}

//Emit push data into the stream
func (b *BaseStream) Emit(data interface{}) (int, error) {
	b.push.Emit(data)
	return 1, nil
}

//Write push a byte slice into the stream
func (b *BaseStream) Write(data []byte) (int, error) {
	b.Emit(data)
	return len(data), nil
}

//Read is supposed to read data into the supplied byte slice,
//but for a BaseStream this is a no-op and a 0 is returned as read length
func (b *BaseStream) Read(data []byte) (int, error) {
	return 0, nil
}

//DoByteStream returns a new Stream instance whos data is modified by a function
func DoByteStream(b StreamInterface, fn StreamMod) (*ByteStream, error) {
	modd, err := b.StreamMod(func(v interface{}) interface{} {
		buff, ok := v.([]byte)

		if !ok {

			str, ok := v.(string)

			if !ok {
				return nil
			}

			return fn([]byte(str))
		}

		return fn(buff)

	})

	if err != nil {
		return nil, err
	}

	nb := &ByteStream{modd, new(bytes.Buffer)}

	sub := nb.Subscribe(func(data interface{}, _ *Sub) {
		buff, ok := data.([]byte)

		if !ok {
			return
		}

		_, _ = nb.buf.Write(buff)
	})

	nb.OnClosed().WhenOnly(func(_ interface{}) {
		sub.Close()
	})

	return nb, err
}

//ByteStreamFrom returns a new Stream instance
func ByteStreamFrom(b StreamInterface) *ByteStream {
	nb, _ := DoByteStream(b, func(data []byte) []byte {
		return data
	})
	return nb
}

//NewByteStream returns a new Stream instance
func NewByteStream() *ByteStream {
	return &ByteStream{
		NewBaseStream(),
		new(bytes.Buffer),
	}
}

//Write reads the data in the byte slice into the buffer while notifying
//listeners
func (b *ByteStream) Write(data []byte) (int, error) {
	n, err := b.buf.Write(data)
	_, _ = b.StreamInterface.Emit(data)
	return n, err
}

//Read reads the data from the internal buf into the provided slice
func (b *ByteStream) Read(data []byte) (int, error) {
	n, err := b.buf.Read(data)
	return n, err
}

//Stream provides a subscription into the stream and returns a streamer that
//reads the buffer and connects for future data
func (b *ByteStream) Stream() (StreamInterface, error) {
	data := make([]byte, b.buf.Len())
	_, err := b.buf.Read(data)

	if err != nil {
		return nil, err
	}

	nb := NewByteStream()
	nb.Write(data)

	sub := nb.Subscribe(func(data interface{}, _ *Sub) {
		buff, ok := data.([]byte)

		if !ok {

			str, ok := data.(string)

			if !ok {
				return
			}

			_, _ = nb.Write([]byte(str))
		}

		_, _ = nb.Write(buff)
	})

	nb.OnClosed().WhenOnly(func(_ interface{}) {
		sub.Close()
	})

	return nb, nil
}

//Emit push data into the stream
func (b *ByteStream) Emit(data interface{}) (int, error) {
	buff, ok := data.([]byte)

	if !ok {

		str, ok := data.(string)

		if !ok {
			return 0, ErrWrongType
		}

		return b.StreamInterface.Emit([]byte(str))
	}

	return b.StreamInterface.Emit(buff)
}

//Close closes the stream
func (b *ByteStream) Close() error {
	b.buf.Reset()
	b.StreamInterface.Close()
	return nil
}
<commit_msg>added timestream<commit_after>package flux

import (
	"bytes"
	"errors"
	"io"
	"io/ioutil"
	"time"
)

type (

	//StreamMod is a function that modifies a data []byte
	StreamMod func(bu []byte) []byte

	//StreamInterface define the interface method rules for streams
	StreamInterface interface {
		Subscribe(func(interface{}, *Sub)) *Sub
		StreamMod(fx func(interface{}) interface{}) (StreamInterface, error)
		Stream() (StreamInterface, error)
		StreamWriter(io.Writer) *Sub
		StreamReader(io.Reader) *Sub
		Write([]byte) (int, error)
		Read([]byte) (int, error)
		Emit(interface{}) (int, error)
		Close() error
		OnClosed() ActionInterface
	}

	//BaseStream defines a basic stream structure
	BaseStream struct {
		push   Pipe
		closed ActionInterface
		sub    *Sub
	}

	//ByteStream provides a buffer back streamer
	ByteStream struct {
		StreamInterface
		buf *bytes.Buffer
	}

	//WrapByteStream provides a ByteStream wrapped around a reader
	WrapByteStream struct {
		StreamInterface
		reader io.ReadCloser
		base   StreamInterface
	}

	//TimedStream provides a bytestream that checks for inactivity
	//on reads or writes and if its passed its duration time,it closes
	//the stream
	TimedStream struct {
		StreamInterface
		Idle *TimeWait
	}
)

var (
	//ErrWrongType denotes a wrong-type assertion
	ErrWrongType = errors.New("Wrong Type!")
	//ErrReadMisMatch denotes when the total bytes length is not read
	ErrReadMisMatch = errors.New("Length MisMatch,Data not fully Read")
)

//TimedStreamFrom returns a new TimedByteStream instance
func TimedStreamFrom(ns StreamInterface, max int, ms time.Duration) *TimedStream {
	ts := &TimedStream{ns, NewTimeWait(max, ms)}

	ts.Idle.Then().WhenOnly(func(_ interface{}) {
		_ = ts.StreamInterface.Close()
	})

	return ts
}

//Close closes the timestream idletimer which closes the inner stream
func (b *TimedStream) Close(data interface{}) error {
	b.Idle.Flush()
	return nil
}

//Emit push data into the stream
func (b *TimedStream) Emit(data interface{}) (int, error) {
	n, err := b.StreamInterface.Emit(data)
	b.Idle.Add()
	return n, err
}

//Write push a byte slice into the stream
func (b *TimedStream) Write(data []byte) (int, error) {
	n, err := b.StreamInterface.Write(data)
	b.Idle.Add()
	return n, err
}

//Read is supposed to read data into the supplied byte slice,
//but for a BaseStream this is a no-op and a 0 is returned as read length
func (b *TimedStream) Read(data []byte) (int, error) {
	n, err := b.StreamInterface.Read(data)
	b.Idle.Add()
	return n, err
}

//ReaderModByteStreamBy returns a bytestream that its input will be modded
func ReaderModByteStreamBy(base StreamInterface, fx StreamMod, rd io.ReadCloser) (*WrapByteStream, error) {
	bs, err := DoByteStream(base, fx)
	return &WrapByteStream{bs, rd, base}, err
}

//ReaderModByteStream returns a bytestream that its input will be modded
func ReaderModByteStream(fx StreamMod, rd io.ReadCloser) (*WrapByteStream, error) {
	ns := NewByteStream()
	bs, err := DoByteStream(ns, fx)
	return &WrapByteStream{bs, rd, ns}, err
}

//ReaderByteStream returns a bytestream that wraps a reader closer
func ReaderByteStream(rd io.Reader) *WrapByteStream {
	return &WrapByteStream{NewByteStream(), ioutil.NopCloser(rd), nil}
}

//ReadCloserByteStream returns a bytestream that wraps a reader closer
func ReadCloserByteStream(rd io.ReadCloser) *WrapByteStream {
	return &WrapByteStream{NewByteStream(), rd, nil}
}

//Close closes the stream and its associated reader
func (b *WrapByteStream) Close() error {
	err := b.reader.Close()
	_ = b.StreamInterface.Close()
	return err
}

//Read reads the data from internal wrap ReadCloser if it fails,
//it attempts to read from the ByteStream buffer and returns
func (b *WrapByteStream) Read(data []byte) (int, error) {
	nx, err := b.reader.Read(data)

	if err == nil {
		var cd []byte
		copy(cd, data)
		b.Write(cd)
	}

	return nx, err
}

//NewBaseStream returns a basestream instance
func NewBaseStream() *BaseStream {
	return &BaseStream{PushSocket(0), NewAction(), nil}
}

//OnClosed returns an action that gets fullfilled when the stream is closed
func (b *BaseStream) OnClosed() ActionInterface {
	return b.closed.Wrap()
}

//Close closes the stream
func (b *BaseStream) Close() error {
	if b.sub != nil {
		b.sub.Close()
	}
	b.push.Close()
	b.closed.Fullfill(true)
	return nil
}

//Stream creates a new StreamInterface and pipes all current data into that stream
func (b *BaseStream) Stream() (StreamInterface, error) {
	return &BaseStream{PushSocketWith(b.push), NewAction(), nil}, nil
}

//StreamMod provides a new stream that mods the internal details of these streams byte
func (b *BaseStream) StreamMod(fn func(v interface{}) interface{}) (StreamInterface, error) {
	return &BaseStream{DoPushSocket(b.push, func(v interface{}, sock SocketInterface) {
		sock.Emit(fn(v))
	}), NewAction(), nil}, nil
}

//StreamReader provides a subscription into the stream to stream to a reader
func (b *BaseStream) StreamReader(w io.Reader) *Sub {
	return b.Subscribe(func(data interface{}, sub *Sub) {
		buff, ok := data.([]byte)

		if !ok {

			str, ok := data.(string)

			if !ok {
				return
			}

			w.Read([]byte(str))
		}

		_, _ = w.Read(buff)
	})
}

//StreamWriter provides a subscription into the stream into a writer
func (b *BaseStream) StreamWriter(w io.Writer) *Sub {
	return b.Subscribe(func(data interface{}, sub *Sub) {
		buff, ok := data.([]byte)

		if !ok {

			str, ok := data.(string)

			if !ok {
				return
			}

			w.Write([]byte(str))
		}

		_, _ = w.Write(buff)
	})
}

//Subscribe provides a subscription into the stream
func (b *BaseStream) Subscribe(fn func(interface{}, *Sub)) *Sub {
	return b.push.Subscribe(fn)
}

//Emit push data into the stream
func (b *BaseStream) Emit(data interface{}) (int, error) {
	b.push.Emit(data)
	return 1, nil
}

//Write push a byte slice into the stream
func (b *BaseStream) Write(data []byte) (int, error) {
	b.Emit(data)
	return len(data), nil
}

//Read is supposed to read data into the supplied byte slice,
//but for a BaseStream this is a no-op and a 0 is returned as read length
func (b *BaseStream) Read(data []byte) (int, error) {
	return 0, nil
}

//DoByteStream returns a new Stream instance whos data is modified by a function
func DoByteStream(b StreamInterface, fn StreamMod) (*ByteStream, error) {
	modd, err := b.StreamMod(func(v interface{}) interface{} {
		buff, ok := v.([]byte)

		if !ok {

			str, ok := v.(string)

			if !ok {
				return nil
			}

			return fn([]byte(str))
		}

		return fn(buff)

	})

	if err != nil {
		return nil, err
	}

	nb := &ByteStream{modd, new(bytes.Buffer)}

	sub := nb.Subscribe(func(data interface{}, _ *Sub) {
		buff, ok := data.([]byte)

		if !ok {
			return
		}

		_, _ = nb.buf.Write(buff)
	})

	nb.OnClosed().WhenOnly(func(_ interface{}) {
		sub.Close()
	})

	return nb, err
}

//ByteStreamFrom returns a new Stream instance
func ByteStreamFrom(b StreamInterface) *ByteStream {
	nb, _ := DoByteStream(b, func(data []byte) []byte {
		return data
	})
	return nb
}

//NewByteStream returns a new Stream instance
func NewByteStream() *ByteStream {
	return &ByteStream{
		NewBaseStream(),
		new(bytes.Buffer),
	}
}

//Write reads the data in the byte slice into the buffer while notifying
//listeners
func (b *ByteStream) Write(data []byte) (int, error) {
	n, err := b.buf.Write(data)
	_, _ = b.StreamInterface.Emit(data)
	return n, err
}

//Read reads the data from the internal buf into the provided slice
func (b *ByteStream) Read(data []byte) (int, error) {
	n, err := b.buf.Read(data)
	return n, err
}

//Stream provides a subscription into the stream and returns a streamer that
//reads the buffer and connects for future data
func (b *ByteStream) Stream() (StreamInterface, error) {
	data := make([]byte, b.buf.Len())
	_, err := b.buf.Read(data)

	if err != nil {
		return nil, err
	}

	nb := NewByteStream()
	nb.Write(data)

	sub := nb.Subscribe(func(data interface{}, _ *Sub) {
		buff, ok := data.([]byte)

		if !ok {

			str, ok := data.(string)

			if !ok {
				return
			}

			_, _ = nb.Write([]byte(str))
		}

		_, _ = nb.Write(buff)
	})

	nb.OnClosed().WhenOnly(func(_ interface{}) {
		sub.Close()
	})

	return nb, nil
}

//Emit push data into the stream
func (b *ByteStream) Emit(data interface{}) (int, error) {
	buff, ok := data.([]byte)

	if !ok {

		str, ok := data.(string)

		if !ok {
			return 0, ErrWrongType
		}

		return b.StreamInterface.Emit([]byte(str))
	}

	return b.StreamInterface.Emit(buff)
}

//Close closes the stream
func (b *ByteStream) Close() error {
	b.buf.Reset()
	b.StreamInterface.Close()
	return nil
}
<|endoftext|>


<commit_before>package gorpc

import "fmt"

func (device *Device) String() string {
	return fmt.Sprintf("%s-%s-%s", device.ID, device.OS, device.OSVersion)
}
<commit_msg>implement NamedService fmt.Stringer interface<commit_after>package gorpc

import "fmt"

func (device *Device) String() string {
	return fmt.Sprintf("%s-%s-%s", device.ID, device.OS, device.OSVersion)
}

func (named *NamedService) String() string {
	return fmt.Sprintf("%s:%s-%s:%d", named.NodeName, named.Name, named.DispatchID, named.VNodes)
}
<|endoftext|>


<commit_before>package replay

import (
    "bytes"
    "encoding/gob"
    "flag"
    "fmt"
    "log"
    "net"
    "net/http"
    "os"
    "strconv"
)

const (
    bufSize = 1024 * 10
)

type ReplaySettings struct {
    port int
    host string

    limit int

    address string
}

var settings ReplaySettings = ReplaySettings{}

type HttpRequest struct {
    Tag     string
    Method  string
    Url     string
    Headers map[string]string
}

type Response struct {
    req  *HttpRequest
    resp *http.Response
    err  error
}

func sendRequest(request *HttpRequest, responses chan *Response) {
    var req *http.Request

    client := &http.Client{}

    req, err := http.NewRequest("GET", settings.address+request.Url, nil)

    if err != nil {
        responses <- &Response{err: err}
        return
    }

    for key, value := range request.Headers {
        req.Header.Add(key, value)
    }

    resp, err := client.Do(req)

    responses <- &Response{request, resp, err}
}

func handleRequests(requests chan *HttpRequest) {
    stat := &RequestStat{}
    stat.reset()

    responses := make(chan *Response)

    for {
        select {
        case req := <-requests:
            go sendRequest(req, responses)

        case resp := <-responses:
            stat.inc(resp)

            if resp.err != nil {
                log.Println("Request err:", resp.err)
            } else {
                log.Println("Request ok:", resp.req.Url)
            }
        }
    }
}

func decodeRequest(enc []byte) (request *HttpRequest, err error) {
    var buf bytes.Buffer
    buf.Write(enc)

    request = &HttpRequest{}

    encoder := gob.NewDecoder(&buf)
    err = encoder.Decode(request)

    return
}

func Run() {
    var buf [bufSize]byte

    serverAddress := settings.host + ":" + strconv.Itoa(settings.port)

    addr, err := net.ResolveUDPAddr("udp", serverAddress)
    if err != nil {
        log.Fatal("Can't start:", err)
    }

    conn, err := net.ListenUDP("udp", addr)
    if err != nil {
        log.Fatal("Can't start:", err)
    }

    defer conn.Close()

    if settings.address == "" {

    }

    fmt.Println("Starting replay server at:", serverAddress)
    fmt.Println("Forwarding incoming requests to:", settings.address)
    fmt.Println("Limiting concurrent request count to:", settings.limit)

    requests := make(chan *HttpRequest)

    go handleRequests(requests)

    for {
        rlen, _, err := conn.ReadFromUDP(buf[0:])

        if err != nil {
            continue
        }

        if rlen > 0 {
            if rlen > bufSize {
                log.Fatal("Too large udp packet", bufSize)
            }

            request, err := decodeRequest(buf[0:rlen])

            if err != nil {
                log.Println("Decode error:", err)
            } else {
                requests <- request
            }
        }
    }

}

func init() {
    if len(os.Args) < 2 || os.Args[1] != "replay" {
        return
    }

    const (
        defaultPort = 28020
        defaultHost = "0.0.0.0"

        defaultLimit   = 10
        defaultAddress = "http://localhost:8080"
    )

    flag.IntVar(&settings.port, "p", defaultPort, "specify port number")

    flag.StringVar(&settings.host, "ip", defaultHost, "ip addresses to listen on")

    flag.IntVar(&settings.limit, "l", defaultLimit, "limit number for requests per second. It will start dropping packets.")

    flag.StringVar(&settings.address, "h", defaultAddress, "http address to forward traffic ")
}
<commit_msg>Updated flag name<commit_after>package replay

import (
    "bytes"
    "encoding/gob"
    "flag"
    "fmt"
    "log"
    "net"
    "net/http"
    "os"
    "strconv"
)

const (
    bufSize = 1024 * 10
)

type ReplaySettings struct {
    port int
    host string

    limit int

    address string
}

var settings ReplaySettings = ReplaySettings{}

type HttpRequest struct {
    Tag     string
    Method  string
    Url     string
    Headers map[string]string
}

type Response struct {
    req  *HttpRequest
    resp *http.Response
    err  error
}

func sendRequest(request *HttpRequest, responses chan *Response) {
    var req *http.Request

    client := &http.Client{}

    req, err := http.NewRequest("GET", settings.address+request.Url, nil)

    if err != nil {
        responses <- &Response{err: err}
        return
    }

    for key, value := range request.Headers {
        req.Header.Add(key, value)
    }

    resp, err := client.Do(req)

    responses <- &Response{request, resp, err}
}

func handleRequests(requests chan *HttpRequest) {
    stat := &RequestStat{}
    stat.reset()

    responses := make(chan *Response)

    for {
        select {
        case req := <-requests:
            go sendRequest(req, responses)

        case resp := <-responses:
            stat.inc(resp)

            if resp.err != nil {
                log.Println("Request err:", resp.err)
            } else {
                log.Println("Request ok:", resp.req.Url)
            }
        }
    }
}

func decodeRequest(enc []byte) (request *HttpRequest, err error) {
    var buf bytes.Buffer
    buf.Write(enc)

    request = &HttpRequest{}

    encoder := gob.NewDecoder(&buf)
    err = encoder.Decode(request)

    return
}

func Run() {
    var buf [bufSize]byte

    serverAddress := settings.host + ":" + strconv.Itoa(settings.port)

    addr, err := net.ResolveUDPAddr("udp", serverAddress)
    if err != nil {
        log.Fatal("Can't start:", err)
    }

    conn, err := net.ListenUDP("udp", addr)
    if err != nil {
        log.Fatal("Can't start:", err)
    }

    defer conn.Close()

    if settings.address == "" {

    }

    fmt.Println("Starting replay server at:", serverAddress)
    fmt.Println("Forwarding incoming requests to:", settings.address)
    fmt.Println("Limiting concurrent request count to:", settings.limit)

    requests := make(chan *HttpRequest)

    go handleRequests(requests)

    for {
        rlen, _, err := conn.ReadFromUDP(buf[0:])

        if err != nil {
            continue
        }

        if rlen > 0 {
            if rlen > bufSize {
                log.Fatal("Too large udp packet", bufSize)
            }

            request, err := decodeRequest(buf[0:rlen])

            if err != nil {
                log.Println("Decode error:", err)
            } else {
                requests <- request
            }
        }
    }

}

func init() {
    if len(os.Args) < 2 || os.Args[1] != "replay" {
        return
    }

    const (
        defaultPort = 28020
        defaultHost = "0.0.0.0"

        defaultLimit   = 10
        defaultAddress = "http://localhost:8080"
    )

    flag.IntVar(&settings.port, "p", defaultPort, "specify port number")

    flag.StringVar(&settings.host, "ip", defaultHost, "ip addresses to listen on")

    flag.IntVar(&settings.limit, "l", defaultLimit, "limit number for requests per second. It will start dropping packets.")

    flag.StringVar(&settings.address, "f", defaultAddress, "http address to forward traffic ")
}
<|endoftext|>


<commit_before>package match

import (
	"fmt"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/consts"
	"github.com/tisp-lang/tisp/src/lib/debug"
	"github.com/tisp-lang/tisp/src/lib/gensym"
	"github.com/tisp-lang/tisp/src/lib/scalar"
)

type desugarer struct {
	letBoundNames, lets []interface{}
}

func newDesugarer() *desugarer {
	return &desugarer{nil, nil}
}

func (d *desugarer) desugar(x interface{}) interface{} {
	return ast.Convert(func(x interface{}) interface{} {
		switch x := x.(type) {
		case ast.LetFunction:
			ls := make([]interface{}, 0, len(x.Lets()))

			for _, l := range x.Lets() {
				l := d.desugar(l)
				ls = append(ls, append(d.takeLets(), l)...)
			}

			b := d.desugar(x.Body())

			return ast.NewLetFunction(
				x.Name(),
				x.Signature(),
				append(ls, d.takeLets()...),
				b,
				x.DebugInfo())
		case ast.Match:
			cs := make([]ast.MatchCase, 0, len(x.Cases()))

			for _, c := range x.Cases() {
				cs = append(cs, renameBoundNamesInCase(ast.NewMatchCase(c.Pattern(), d.desugar(c.Value()))))
			}

			return d.resultApp(d.createMatchFunction(cs), d.desugar(x.Value()))
		}

		return nil
	}, x)
}

func (d *desugarer) takeLets() []interface{} {
	ls := append(d.letBoundNames, d.lets...)
	d.letBoundNames = nil
	d.lets = nil
	return ls
}

func (d *desugarer) letTempVar(v interface{}) string {
	s := gensym.GenSym()
	d.lets = append(d.lets, ast.NewLetVar(s, v))
	return s
}

func (d *desugarer) bindName(p interface{}, v interface{}) string {
	s := generalNamePatternToName(p)
	d.letBoundNames = append(d.letBoundNames, ast.NewLetVar(s, v))
	return s
}

// matchedApp applies a function to arguments and creates a matched value of
// match expression.
func (d *desugarer) matchedApp(f interface{}, args ...interface{}) string {
	return d.bindName(gensym.GenSym(), app(f, args...))
}

// resultApp applies a function to arguments and creates a result value of match
// expression.
func (d *desugarer) resultApp(f interface{}, args ...interface{}) string {
	return d.letTempVar(app(f, args...))
}

func (d *desugarer) createMatchFunction(cs []ast.MatchCase) interface{} {
	arg := gensym.GenSym()
	body := d.desugarCases(arg, cs, "$matchError")

	f := ast.NewLetFunction(
		gensym.GenSym(),
		ast.NewSignature([]string{arg}, nil, "", nil, nil, ""),
		d.takeLets(),
		body,
		debug.NewGoInfo(0))

	d.lets = append(d.lets, f)

	return f.Name()
}

func (d *desugarer) desugarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	css := groupCases(cs)

	if cs, ok := css[namePattern]; ok {
		c := cs[0]
		d.bindName(c.Pattern().(string), v)
		dc = c.Value()
	}

	ks := []ast.SwitchCase{}

	if cs, ok := css[listPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"list\"", d.desugarListCases(v, cs, dc)))
	}

	if cs, ok := css[dictPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"dict\"", d.desugarDictCases(v, cs, dc)))
	}

	if cs, ok := css[scalarPattern]; ok {
		dc = d.desugarScalarCases(v, cs, dc)
	}

	return newSwitch(d.resultApp("$typeOf", v), ks, dc)
}

func groupCases(cs []ast.MatchCase) map[patternType][]ast.MatchCase {
	css := map[patternType][]ast.MatchCase{}

	for i, c := range cs {
		t := getPatternType(c.Pattern())

		if t == namePattern && i < len(cs)-1 {
			panic("A wildcard pattern is found while some patterns are left")
		}

		css[t] = append(css[t], c)
	}

	return css
}

func getPatternType(p interface{}) patternType {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return scalarPattern
		}

		return namePattern
	case ast.App:
		switch x.Function().(string) {
		case consts.Names.ListFunction:
			return listPattern
		case consts.Names.DictionaryFunction:
			return dictPattern
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}

func isGeneralNamePattern(p interface{}) bool {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return false
		}

		return true
	case ast.App:
		ps := x.Arguments().Positionals()
		ok := len(ps) == 1 && ps[0].Expanded()

		switch x.Function().(string) {
		case consts.Names.ListFunction:
			return ok
		case consts.Names.DictionaryFunction:
			return ok
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}

func (d *desugarer) desugarListCases(list interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		first interface{}
		cases []ast.MatchCase
	}

	gs := []group{}
	first := d.matchedApp("$first", list)
	rest := d.matchedApp("$rest", list)

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = d.resultApp("$if", app("$=", list, consts.Names.EmptyList), c.Value(), dc)
			continue
		}

		v := ps[0].Value()

		if ps[0].Expanded() {
			d.bindName(v.(string), list)
			dc = c.Value()
			break
		}

		c = ast.NewMatchCase(
			ast.NewApp(
				consts.Names.ListFunction,
				ast.NewArguments(ps[1:], nil, nil),
				debug.NewGoInfo(0)),
			c.Value())

		if isGeneralNamePattern(v) {
			d.bindName(v, first)

			if cs := cs[i+1:]; len(cs) > 0 {
				dc = d.desugarListCases(list, cs, dc)
			}

			dc = d.defaultCaseOfGeneralNamePattern(
				first,
				v,
				d.desugarCases(rest, []ast.MatchCase{c}, dc),
				dc)
			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.first) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	cs = make([]ast.MatchCase, 0, len(gs))
	dc = d.letTempVar(dc)

	for _, g := range gs {
		cs = append(cs, ast.NewMatchCase(g.first, d.desugarCases(rest, g.cases, dc)))
	}

	return d.desugarCases(first, cs, dc)
}

func (d *desugarer) ifType(v interface{}, t string, then, els interface{}) interface{} {
	return d.resultApp("$if", app("$=", app("$typeOf", v), "\""+t+"\""), then, els)
}

func (d *desugarer) desugarDictCases(dict interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		key   interface{}
		cases []ast.MatchCase
	}

	gs := []group{}

	for _, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = d.resultApp("$if", app("$=", dict, consts.Names.EmptyDictionary), c.Value(), dc)
			continue
		}

		k := ps[0].Value()

		if ps[0].Expanded() {
			d.bindName(k.(string), dict)
			dc = c.Value()
			break
		}

		g := group{k, []ast.MatchCase{c}}

		if len(gs) == 0 {
			gs = append(gs, g)
		} else if last := len(gs) - 1; equalPatterns(g.key, gs[last].key) {
			gs[last].cases = append(gs[last].cases, c)
		} else {
			gs = append(gs, g)
		}
	}

	for i := len(gs) - 1; i >= 0; i-- {
		g := gs[i]
		dc = d.resultApp("$if",
			app("$include", dict, g.key),
			d.desugarDictCasesOfSameKey(dict, g.cases, dc),
			dc)
	}

	return dc
}

func (d *desugarer) desugarDictCasesOfSameKey(dict interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		value interface{}
		cases []ast.MatchCase
	}

	key := cs[0].Pattern().(ast.App).Arguments().Positionals()[0].Value()
	value := d.matchedApp(dict, key)
	rest := d.matchedApp("delete", dict, key)
	gs := []group{}

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()
		v := ps[1].Value()

		c = ast.NewMatchCase(
			ast.NewApp(
				consts.Names.DictionaryFunction,
				ast.NewArguments(ps[2:], nil, nil),
				debug.NewGoInfo(0)),
			c.Value())

		if isGeneralNamePattern(v) {
			d.bindName(v, value)

			if cs := cs[i+1:]; len(cs) != 0 {
				dc = d.desugarDictCasesOfSameKey(dict, cs, dc)
			}

			dc = d.defaultCaseOfGeneralNamePattern(
				value,
				v,
				d.desugarCases(rest, []ast.MatchCase{c}, dc),
				dc)
			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.value) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	cs = make([]ast.MatchCase, 0, len(gs))
	dc = d.letTempVar(dc)

	for _, g := range gs {
		cs = append(cs, ast.NewMatchCase(g.value, d.desugarCases(rest, g.cases, dc)))
	}

	return d.desugarCases(value, cs, dc)
}

func (d *desugarer) defaultCaseOfGeneralNamePattern(v, p, body, dc interface{}) interface{} {
	switch getPatternType(p) {
	case namePattern:
		return body
	case listPattern:
		return d.ifType(v, "list", body, dc)
	case dictPattern:
		return d.ifType(v, "dict", body, dc)
	}

	panic("Unreachable")
}

func (d *desugarer) desugarScalarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	ks := []ast.SwitchCase{}

	for _, c := range cs {
		ks = append(ks, ast.NewSwitchCase(c.Pattern().(string), c.Value()))
	}

	return newSwitch(v, ks, dc)
}

func renameBoundNamesInCase(c ast.MatchCase) ast.MatchCase {
	p, ns := newPatternRenamer().rename(c.Pattern())
	return ast.NewMatchCase(p, newValueRenamer(ns).rename(c.Value()))
}

func equalPatterns(p, q interface{}) bool {
	switch x := p.(type) {
	case string:
		y, ok := q.(string)

		if !ok {
			return false
		}

		return x == y
	case ast.App:
		y, ok := q.(ast.App)

		if !ok ||
			x.Function().(string) != y.Function().(string) ||
			len(x.Arguments().Positionals()) != len(y.Arguments().Positionals()) {
			return false
		}

		for i := range x.Arguments().Positionals() {
			p := x.Arguments().Positionals()[i]
			q := y.Arguments().Positionals()[i]

			if p.Expanded() != q.Expanded() || !equalPatterns(p.Value(), q.Value()) {
				return false
			}
		}

		return true
	}

	panic(fmt.Errorf("Invalid pattern: %#v, %#v", p, q))
}

func generalNamePatternToName(p interface{}) string {
	switch x := p.(type) {
	case string:
		return x
	case ast.App:
		if ps := x.Arguments().Positionals(); len(ps) == 1 && ps[0].Expanded() {
			return ps[0].Value().(string)
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}
<commit_msg>Mark empty lists and dictionaries as list or dictionary<commit_after>package match

import (
	"fmt"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/consts"
	"github.com/tisp-lang/tisp/src/lib/debug"
	"github.com/tisp-lang/tisp/src/lib/gensym"
	"github.com/tisp-lang/tisp/src/lib/scalar"
)

type desugarer struct {
	letBoundNames, lets []interface{}
}

func newDesugarer() *desugarer {
	return &desugarer{nil, nil}
}

func (d *desugarer) desugar(x interface{}) interface{} {
	return ast.Convert(func(x interface{}) interface{} {
		switch x := x.(type) {
		case ast.LetFunction:
			ls := make([]interface{}, 0, len(x.Lets()))

			for _, l := range x.Lets() {
				l := d.desugar(l)
				ls = append(ls, append(d.takeLets(), l)...)
			}

			b := d.desugar(x.Body())

			return ast.NewLetFunction(
				x.Name(),
				x.Signature(),
				append(ls, d.takeLets()...),
				b,
				x.DebugInfo())
		case ast.Match:
			cs := make([]ast.MatchCase, 0, len(x.Cases()))

			for _, c := range x.Cases() {
				cs = append(cs, renameBoundNamesInCase(ast.NewMatchCase(c.Pattern(), d.desugar(c.Value()))))
			}

			return d.resultApp(d.createMatchFunction(cs), d.desugar(x.Value()))
		}

		return nil
	}, x)
}

func (d *desugarer) takeLets() []interface{} {
	ls := append(d.letBoundNames, d.lets...)
	d.letBoundNames = nil
	d.lets = nil
	return ls
}

func (d *desugarer) letTempVar(v interface{}) string {
	s := gensym.GenSym()
	d.lets = append(d.lets, ast.NewLetVar(s, v))
	return s
}

func (d *desugarer) bindName(p interface{}, v interface{}) string {
	s := generalNamePatternToName(p)
	d.letBoundNames = append(d.letBoundNames, ast.NewLetVar(s, v))
	return s
}

// matchedApp applies a function to arguments and creates a matched value of
// match expression.
func (d *desugarer) matchedApp(f interface{}, args ...interface{}) string {
	return d.bindName(gensym.GenSym(), app(f, args...))
}

// resultApp applies a function to arguments and creates a result value of match
// expression.
func (d *desugarer) resultApp(f interface{}, args ...interface{}) string {
	return d.letTempVar(app(f, args...))
}

func (d *desugarer) createMatchFunction(cs []ast.MatchCase) interface{} {
	arg := gensym.GenSym()
	body := d.desugarCases(arg, cs, "$matchError")

	f := ast.NewLetFunction(
		gensym.GenSym(),
		ast.NewSignature([]string{arg}, nil, "", nil, nil, ""),
		d.takeLets(),
		body,
		debug.NewGoInfo(0))

	d.lets = append(d.lets, f)

	return f.Name()
}

func (d *desugarer) desugarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	css := groupCases(cs)

	if cs, ok := css[namePattern]; ok {
		c := cs[0]
		d.bindName(c.Pattern().(string), v)
		dc = c.Value()
	}

	ks := []ast.SwitchCase{}

	if cs, ok := css[listPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"list\"", d.desugarListCases(v, cs, dc)))
	}

	if cs, ok := css[dictPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"dict\"", d.desugarDictCases(v, cs, dc)))
	}

	if cs, ok := css[scalarPattern]; ok {
		dc = d.desugarScalarCases(v, cs, dc)
	}

	return newSwitch(d.resultApp("$typeOf", v), ks, dc)
}

func groupCases(cs []ast.MatchCase) map[patternType][]ast.MatchCase {
	css := map[patternType][]ast.MatchCase{}

	for i, c := range cs {
		t := getPatternType(c.Pattern())

		if t == namePattern && i < len(cs)-1 {
			panic("A wildcard pattern is found while some patterns are left")
		}

		css[t] = append(css[t], c)
	}

	return css
}

func getPatternType(p interface{}) patternType {
	switch x := p.(type) {
	case string:
		switch x {
		case consts.Names.EmptyList:
			return listPattern
		case consts.Names.EmptyDictionary:
			return dictPattern
		}

		if scalar.Defined(x) {
			return scalarPattern
		}

		return namePattern
	case ast.App:
		switch x.Function().(string) {
		case consts.Names.ListFunction:
			return listPattern
		case consts.Names.DictionaryFunction:
			return dictPattern
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}

func isGeneralNamePattern(p interface{}) bool {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return false
		}

		return true
	case ast.App:
		ps := x.Arguments().Positionals()
		ok := len(ps) == 1 && ps[0].Expanded()

		switch x.Function().(string) {
		case consts.Names.ListFunction:
			return ok
		case consts.Names.DictionaryFunction:
			return ok
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}

func (d *desugarer) desugarListCases(list interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		first interface{}
		cases []ast.MatchCase
	}

	gs := []group{}
	first := d.matchedApp("$first", list)
	rest := d.matchedApp("$rest", list)

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = d.resultApp("$if", app("$=", list, consts.Names.EmptyList), c.Value(), dc)
			continue
		}

		v := ps[0].Value()

		if ps[0].Expanded() {
			d.bindName(v.(string), list)
			dc = c.Value()
			break
		}

		c = ast.NewMatchCase(
			ast.NewApp(
				consts.Names.ListFunction,
				ast.NewArguments(ps[1:], nil, nil),
				debug.NewGoInfo(0)),
			c.Value())

		if isGeneralNamePattern(v) {
			d.bindName(v, first)

			if cs := cs[i+1:]; len(cs) > 0 {
				dc = d.desugarListCases(list, cs, dc)
			}

			dc = d.defaultCaseOfGeneralNamePattern(
				first,
				v,
				d.desugarCases(rest, []ast.MatchCase{c}, dc),
				dc)
			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.first) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	cs = make([]ast.MatchCase, 0, len(gs))
	dc = d.letTempVar(dc)

	for _, g := range gs {
		cs = append(cs, ast.NewMatchCase(g.first, d.desugarCases(rest, g.cases, dc)))
	}

	return d.desugarCases(first, cs, dc)
}

func (d *desugarer) ifType(v interface{}, t string, then, els interface{}) interface{} {
	return d.resultApp("$if", app("$=", app("$typeOf", v), "\""+t+"\""), then, els)
}

func (d *desugarer) desugarDictCases(dict interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		key   interface{}
		cases []ast.MatchCase
	}

	gs := []group{}

	for _, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = d.resultApp("$if", app("$=", dict, consts.Names.EmptyDictionary), c.Value(), dc)
			continue
		}

		k := ps[0].Value()

		if ps[0].Expanded() {
			d.bindName(k.(string), dict)
			dc = c.Value()
			break
		}

		g := group{k, []ast.MatchCase{c}}

		if len(gs) == 0 {
			gs = append(gs, g)
		} else if last := len(gs) - 1; equalPatterns(g.key, gs[last].key) {
			gs[last].cases = append(gs[last].cases, c)
		} else {
			gs = append(gs, g)
		}
	}

	for i := len(gs) - 1; i >= 0; i-- {
		g := gs[i]
		dc = d.resultApp("$if",
			app("$include", dict, g.key),
			d.desugarDictCasesOfSameKey(dict, g.cases, dc),
			dc)
	}

	return dc
}

func (d *desugarer) desugarDictCasesOfSameKey(dict interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		value interface{}
		cases []ast.MatchCase
	}

	key := cs[0].Pattern().(ast.App).Arguments().Positionals()[0].Value()
	value := d.matchedApp(dict, key)
	rest := d.matchedApp("delete", dict, key)
	gs := []group{}

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()
		v := ps[1].Value()

		c = ast.NewMatchCase(
			ast.NewApp(
				consts.Names.DictionaryFunction,
				ast.NewArguments(ps[2:], nil, nil),
				debug.NewGoInfo(0)),
			c.Value())

		if isGeneralNamePattern(v) {
			d.bindName(v, value)

			if cs := cs[i+1:]; len(cs) != 0 {
				dc = d.desugarDictCasesOfSameKey(dict, cs, dc)
			}

			dc = d.defaultCaseOfGeneralNamePattern(
				value,
				v,
				d.desugarCases(rest, []ast.MatchCase{c}, dc),
				dc)
			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.value) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	cs = make([]ast.MatchCase, 0, len(gs))
	dc = d.letTempVar(dc)

	for _, g := range gs {
		cs = append(cs, ast.NewMatchCase(g.value, d.desugarCases(rest, g.cases, dc)))
	}

	return d.desugarCases(value, cs, dc)
}

func (d *desugarer) defaultCaseOfGeneralNamePattern(v, p, body, dc interface{}) interface{} {
	switch getPatternType(p) {
	case namePattern:
		return body
	case listPattern:
		return d.ifType(v, "list", body, dc)
	case dictPattern:
		return d.ifType(v, "dict", body, dc)
	}

	panic("Unreachable")
}

func (d *desugarer) desugarScalarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	ks := []ast.SwitchCase{}

	for _, c := range cs {
		ks = append(ks, ast.NewSwitchCase(c.Pattern().(string), c.Value()))
	}

	return newSwitch(v, ks, dc)
}

func renameBoundNamesInCase(c ast.MatchCase) ast.MatchCase {
	p, ns := newPatternRenamer().rename(c.Pattern())
	return ast.NewMatchCase(p, newValueRenamer(ns).rename(c.Value()))
}

func equalPatterns(p, q interface{}) bool {
	switch x := p.(type) {
	case string:
		y, ok := q.(string)

		if !ok {
			return false
		}

		return x == y
	case ast.App:
		y, ok := q.(ast.App)

		if !ok ||
			x.Function().(string) != y.Function().(string) ||
			len(x.Arguments().Positionals()) != len(y.Arguments().Positionals()) {
			return false
		}

		for i := range x.Arguments().Positionals() {
			p := x.Arguments().Positionals()[i]
			q := y.Arguments().Positionals()[i]

			if p.Expanded() != q.Expanded() || !equalPatterns(p.Value(), q.Value()) {
				return false
			}
		}

		return true
	}

	panic(fmt.Errorf("Invalid pattern: %#v, %#v", p, q))
}

func generalNamePatternToName(p interface{}) string {
	switch x := p.(type) {
	case string:
		return x
	case ast.App:
		if ps := x.Arguments().Positionals(); len(ps) == 1 && ps[0].Expanded() {
			return ps[0].Value().(string)
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}
<|endoftext|>


<commit_before>package match

import (
	"fmt"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/debug"
	"github.com/tisp-lang/tisp/src/lib/gensym"
	"github.com/tisp-lang/tisp/src/lib/scalar"
)

type desugarer struct {
	letBoundNames, lets []interface{}
}

func newDesugarer() *desugarer {
	return &desugarer{nil, nil}
}

func (d *desugarer) desugar(x interface{}) interface{} {
	switch x := x.(type) {
	case ast.App:
		return ast.NewApp(
			d.desugar(x.Function()),
			d.desugar(x.Arguments()).(ast.Arguments),
			x.DebugInfo())
	case ast.Arguments:
		ps := make([]ast.PositionalArgument, 0, len(x.Positionals()))

		for _, p := range x.Positionals() {
			ps = append(ps, d.desugar(p).(ast.PositionalArgument))
		}

		ks := make([]ast.KeywordArgument, 0, len(x.Keywords()))

		for _, k := range x.Keywords() {
			ks = append(ks, d.desugar(k).(ast.KeywordArgument))
		}

		dicts := make([]interface{}, 0, len(x.ExpandedDicts()))

		for _, dict := range x.ExpandedDicts() {
			dicts = append(dicts, d.desugar(dict))
		}

		return ast.NewArguments(ps, ks, dicts)
	case ast.KeywordArgument:
		return ast.NewKeywordArgument(x.Name(), d.desugar(x.Value()))
	case ast.LetFunction:
		ls := make([]interface{}, 0, len(x.Lets()))

		for _, l := range x.Lets() {
			l := d.desugar(l)
			ls = append(ls, append(d.takeLets(), l)...)
		}

		b := d.desugar(x.Body())
		ls = append(ls, d.takeLets()...)

		return ast.NewLetFunction(
			x.Name(),
			x.Signature(),
			ls,
			b,
			x.DebugInfo())
	case ast.LetVar:
		return ast.NewLetVar(x.Name(), d.desugar(x.Expr()))
	case ast.Match:
		cs := make([]ast.MatchCase, 0, len(x.Cases()))

		for _, c := range x.Cases() {
			cs = append(cs, renameBoundNamesInCase(ast.NewMatchCase(c.Pattern(), d.desugar(c.Value()))))
		}

		return d.resultApp(d.createMatchFunction(cs), d.desugar(x.Value()))
	case ast.Output:
		return ast.NewOutput(d.desugar(x.Expr()), x.Expanded())
	case ast.PositionalArgument:
		return ast.NewPositionalArgument(d.desugar(x.Value()), x.Expanded())
	default:
		return x
	}
}

func (d *desugarer) takeLets() []interface{} {
	ls := append(d.letBoundNames, d.lets...)
	d.letBoundNames = nil
	d.lets = nil
	return ls
}

func (d *desugarer) letVar(s string, v interface{}) string {
	d.lets = append(d.lets, ast.NewLetVar(s, v))
	return s
}

func (d *desugarer) bindName(s string, v interface{}) string {
	d.letBoundNames = append(d.letBoundNames, ast.NewLetVar(s, v))
	return s
}

// matchedApp applies a function to arguments and creates a matched value of
// match expression.
func (d *desugarer) matchedApp(f interface{}, args ...interface{}) string {
	return d.bindName(gensym.GenSym("match", "app"), app(f, args...))
}

// resultApp applies a function to arguments and creates a result value of match
// expression.
func (d *desugarer) resultApp(f interface{}, args ...interface{}) string {
	return d.letVar(gensym.GenSym("match", "app"), app(f, args...))
}

func (d *desugarer) createMatchFunction(cs []ast.MatchCase) interface{} {
	arg := gensym.GenSym("match", "argument")
	body := d.desugarCases(arg, cs, "$matchError")

	f := ast.NewLetFunction(
		gensym.GenSym("match", "function"),
		ast.NewSignature([]string{arg}, nil, "", nil, nil, ""),
		d.takeLets(),
		body,
		debug.NewGoInfo(0))

	d.lets = append(d.lets, f)

	return f.Name()
}

func (d *desugarer) desugarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	css := groupCases(cs)

	if cs, ok := css[namePattern]; ok {
		c := cs[0]
		d.bindName(c.Pattern().(string), v)
		dc = c.Value()
	}

	ks := []ast.SwitchCase{}

	if cs, ok := css[listPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"list\"", d.desugarListCases(v, cs, dc)))
	}

	if cs, ok := css[dictPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"dict\"", d.desugarDictCases(v, cs, dc)))
	}

	if cs, ok := css[scalarPattern]; ok {
		dc = d.desugarScalarCases(v, cs, dc)
	}

	return newSwitch(d.resultApp("$typeOf", v), ks, dc)
}

func groupCases(cs []ast.MatchCase) map[patternType][]ast.MatchCase {
	css := map[patternType][]ast.MatchCase{}

	for i, c := range cs {
		t := getPatternType(c.Pattern())

		if t == namePattern && i < len(cs)-1 {
			panic("A wildcard pattern is found while some patterns are left")
		}

		css[t] = append(css[t], c)
	}

	return css
}

func getPatternType(p interface{}) patternType {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return scalarPattern
		}

		return namePattern
	case ast.App:
		switch x.Function().(string) {
		case "$list":
			return listPattern
		case "$dict":
			return dictPattern
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}

func (d *desugarer) desugarListCases(list interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		first interface{}
		cases []ast.MatchCase
	}

	gs := []group{}
	first := d.matchedApp("$first", list)
	rest := d.matchedApp("$rest", list)

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = d.resultApp("$if", app("$=", list, "$emptyList"), c.Value(), dc)
			continue
		}

		if ps[0].Expanded() {
			panic("Not implemented")
		}

		v := ps[0].Value()

		c = ast.NewMatchCase(
			ast.NewApp("$list", ast.NewArguments(ps[1:], nil, nil), debug.NewGoInfo(0)),
			c.Value())

		if getPatternType(v) == namePattern {
			d.bindName(v.(string), first)
			dc = d.desugarCases(
				rest,
				[]ast.MatchCase{c},
				d.desugarListCases(list, cs[i+1:], dc))
			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.first) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	ks := make([]ast.MatchCase, 0, len(gs))

	for _, g := range gs {
		ks = append(ks, ast.NewMatchCase(g.first, d.desugarCases(rest, g.cases, dc)))
	}

	return d.desugarCases(first, ks, dc)
}

func (d *desugarer) desugarDictCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		key   interface{}
		cases []ast.MatchCase
	}

	gs := []group{}

	for _, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = d.resultApp("$if", app("$=", v, "$emptyDict"), c.Value(), dc)
			continue
		}

		if ps[0].Expanded() {
			panic("Not implemented")
		}

		new := group{ps[0].Value(), []ast.MatchCase{c}}

		if len(gs) == 0 {
			gs = append(gs, new)
		} else if last := gs[len(gs)-1]; equalPatterns(new.key, last.key) {
			last.cases = append(last.cases, c)
		} else {
			gs = append(gs, new)
		}
	}

	x := dc

	for i := len(gs) - 1; i >= 0; i-- {
		g := gs[i]
		x = d.resultApp("$if",
			app("$include", v, g.key),
			d.desugarDictCasesOfSameKey(v, g.cases, x),
			x)
	}

	return x
}

func (d *desugarer) desugarDictCasesOfSameKey(dict interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		value interface{}
		cases []ast.MatchCase
	}

	key := cs[0].Pattern().(ast.App).Arguments().Positionals()[0].Value()
	value := d.matchedApp(dict, key)
	newDict := d.matchedApp("delete", dict, key)
	gs := []group{}

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()
		v := ps[1].Value()

		c = ast.NewMatchCase(
			ast.NewApp("$dict", ast.NewArguments(ps[2:], nil, nil), debug.NewGoInfo(0)),
			c.Value())

		if getPatternType(v) == namePattern {
			d.bindName(v.(string), value)

			if rest := cs[i+1:]; len(rest) != 0 {
				dc = d.desugarDictCasesOfSameKey(dict, rest, dc)
			}

			dc = d.desugarCases(newDict, []ast.MatchCase{c}, dc)

			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.value) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	cs = make([]ast.MatchCase, 0, len(gs))

	for _, g := range gs {
		cs = append(
			cs,
			ast.NewMatchCase(g.value, d.desugarCases(newDict, g.cases, dc)))
	}

	return d.desugarCases(value, cs, dc)
}

func (d *desugarer) desugarScalarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	ks := []ast.SwitchCase{}

	for _, c := range cs {
		ks = append(ks, ast.NewSwitchCase(c.Pattern().(string), c.Value()))
	}

	return newSwitch(v, ks, dc)
}

func renameBoundNamesInCase(c ast.MatchCase) ast.MatchCase {
	p, ns := newPatternRenamer().rename(c.Pattern())
	return ast.NewMatchCase(p, newValueRenamer(ns).rename(c.Value()))
}

func equalPatterns(p, q interface{}) bool {
	switch x := p.(type) {
	case string:
		y, ok := q.(string)

		if !ok {
			return false
		}

		return x == y
	case ast.App:
		y, ok := q.(ast.App)

		if !ok ||
			x.Function().(string) != y.Function().(string) ||
			len(x.Arguments().Positionals()) != len(y.Arguments().Positionals()) {
			return false
		}

		for i := range x.Arguments().Positionals() {
			p := x.Arguments().Positionals()[i]
			q := y.Arguments().Positionals()[i]

			if p.Expanded() != q.Expanded() || !equalPatterns(p.Value(), q.Value()) {
				return false
			}
		}

		return true
	}

	panic(fmt.Errorf("Invalid pattern: %#v, %#v", p, q))
}
<commit_msg>Refactor desugarDictCases<commit_after>package match

import (
	"fmt"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/debug"
	"github.com/tisp-lang/tisp/src/lib/gensym"
	"github.com/tisp-lang/tisp/src/lib/scalar"
)

type desugarer struct {
	letBoundNames, lets []interface{}
}

func newDesugarer() *desugarer {
	return &desugarer{nil, nil}
}

func (d *desugarer) desugar(x interface{}) interface{} {
	switch x := x.(type) {
	case ast.App:
		return ast.NewApp(
			d.desugar(x.Function()),
			d.desugar(x.Arguments()).(ast.Arguments),
			x.DebugInfo())
	case ast.Arguments:
		ps := make([]ast.PositionalArgument, 0, len(x.Positionals()))

		for _, p := range x.Positionals() {
			ps = append(ps, d.desugar(p).(ast.PositionalArgument))
		}

		ks := make([]ast.KeywordArgument, 0, len(x.Keywords()))

		for _, k := range x.Keywords() {
			ks = append(ks, d.desugar(k).(ast.KeywordArgument))
		}

		dicts := make([]interface{}, 0, len(x.ExpandedDicts()))

		for _, dict := range x.ExpandedDicts() {
			dicts = append(dicts, d.desugar(dict))
		}

		return ast.NewArguments(ps, ks, dicts)
	case ast.KeywordArgument:
		return ast.NewKeywordArgument(x.Name(), d.desugar(x.Value()))
	case ast.LetFunction:
		ls := make([]interface{}, 0, len(x.Lets()))

		for _, l := range x.Lets() {
			l := d.desugar(l)
			ls = append(ls, append(d.takeLets(), l)...)
		}

		b := d.desugar(x.Body())
		ls = append(ls, d.takeLets()...)

		return ast.NewLetFunction(
			x.Name(),
			x.Signature(),
			ls,
			b,
			x.DebugInfo())
	case ast.LetVar:
		return ast.NewLetVar(x.Name(), d.desugar(x.Expr()))
	case ast.Match:
		cs := make([]ast.MatchCase, 0, len(x.Cases()))

		for _, c := range x.Cases() {
			cs = append(cs, renameBoundNamesInCase(ast.NewMatchCase(c.Pattern(), d.desugar(c.Value()))))
		}

		return d.resultApp(d.createMatchFunction(cs), d.desugar(x.Value()))
	case ast.Output:
		return ast.NewOutput(d.desugar(x.Expr()), x.Expanded())
	case ast.PositionalArgument:
		return ast.NewPositionalArgument(d.desugar(x.Value()), x.Expanded())
	default:
		return x
	}
}

func (d *desugarer) takeLets() []interface{} {
	ls := append(d.letBoundNames, d.lets...)
	d.letBoundNames = nil
	d.lets = nil
	return ls
}

func (d *desugarer) letVar(s string, v interface{}) string {
	d.lets = append(d.lets, ast.NewLetVar(s, v))
	return s
}

func (d *desugarer) bindName(s string, v interface{}) string {
	d.letBoundNames = append(d.letBoundNames, ast.NewLetVar(s, v))
	return s
}

// matchedApp applies a function to arguments and creates a matched value of
// match expression.
func (d *desugarer) matchedApp(f interface{}, args ...interface{}) string {
	return d.bindName(gensym.GenSym("match", "app"), app(f, args...))
}

// resultApp applies a function to arguments and creates a result value of match
// expression.
func (d *desugarer) resultApp(f interface{}, args ...interface{}) string {
	return d.letVar(gensym.GenSym("match", "app"), app(f, args...))
}

func (d *desugarer) createMatchFunction(cs []ast.MatchCase) interface{} {
	arg := gensym.GenSym("match", "argument")
	body := d.desugarCases(arg, cs, "$matchError")

	f := ast.NewLetFunction(
		gensym.GenSym("match", "function"),
		ast.NewSignature([]string{arg}, nil, "", nil, nil, ""),
		d.takeLets(),
		body,
		debug.NewGoInfo(0))

	d.lets = append(d.lets, f)

	return f.Name()
}

func (d *desugarer) desugarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	css := groupCases(cs)

	if cs, ok := css[namePattern]; ok {
		c := cs[0]
		d.bindName(c.Pattern().(string), v)
		dc = c.Value()
	}

	ks := []ast.SwitchCase{}

	if cs, ok := css[listPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"list\"", d.desugarListCases(v, cs, dc)))
	}

	if cs, ok := css[dictPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"dict\"", d.desugarDictCases(v, cs, dc)))
	}

	if cs, ok := css[scalarPattern]; ok {
		dc = d.desugarScalarCases(v, cs, dc)
	}

	return newSwitch(d.resultApp("$typeOf", v), ks, dc)
}

func groupCases(cs []ast.MatchCase) map[patternType][]ast.MatchCase {
	css := map[patternType][]ast.MatchCase{}

	for i, c := range cs {
		t := getPatternType(c.Pattern())

		if t == namePattern && i < len(cs)-1 {
			panic("A wildcard pattern is found while some patterns are left")
		}

		css[t] = append(css[t], c)
	}

	return css
}

func getPatternType(p interface{}) patternType {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return scalarPattern
		}

		return namePattern
	case ast.App:
		switch x.Function().(string) {
		case "$list":
			return listPattern
		case "$dict":
			return dictPattern
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}

func (d *desugarer) desugarListCases(list interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		first interface{}
		cases []ast.MatchCase
	}

	gs := []group{}
	first := d.matchedApp("$first", list)
	rest := d.matchedApp("$rest", list)

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = d.resultApp("$if", app("$=", list, "$emptyList"), c.Value(), dc)
			continue
		}

		if ps[0].Expanded() {
			panic("Not implemented")
		}

		v := ps[0].Value()

		c = ast.NewMatchCase(
			ast.NewApp("$list", ast.NewArguments(ps[1:], nil, nil), debug.NewGoInfo(0)),
			c.Value())

		if getPatternType(v) == namePattern {
			d.bindName(v.(string), first)
			dc = d.desugarCases(
				rest,
				[]ast.MatchCase{c},
				d.desugarListCases(list, cs[i+1:], dc))
			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.first) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	ks := make([]ast.MatchCase, 0, len(gs))

	for _, g := range gs {
		ks = append(ks, ast.NewMatchCase(g.first, d.desugarCases(rest, g.cases, dc)))
	}

	return d.desugarCases(first, ks, dc)
}

func (d *desugarer) desugarDictCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		key   interface{}
		cases []ast.MatchCase
	}

	gs := []group{}

	for _, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = d.resultApp("$if", app("$=", v, "$emptyDict"), c.Value(), dc)
			continue
		}

		if ps[0].Expanded() {
			panic("Not implemented")
		}

		g := group{ps[0].Value(), []ast.MatchCase{c}}

		if len(gs) == 0 {
			gs = append(gs, g)
		} else if last := gs[len(gs)-1]; equalPatterns(g.key, last.key) {
			last.cases = append(last.cases, c)
		} else {
			gs = append(gs, g)
		}
	}

	for i := len(gs) - 1; i >= 0; i-- {
		g := gs[i]
		dc = d.resultApp("$if",
			app("$include", v, g.key),
			d.desugarDictCasesOfSameKey(v, g.cases, dc),
			dc)
	}

	return dc
}

func (d *desugarer) desugarDictCasesOfSameKey(dict interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		value interface{}
		cases []ast.MatchCase
	}

	key := cs[0].Pattern().(ast.App).Arguments().Positionals()[0].Value()
	value := d.matchedApp(dict, key)
	newDict := d.matchedApp("delete", dict, key)
	gs := []group{}

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()
		v := ps[1].Value()

		c = ast.NewMatchCase(
			ast.NewApp("$dict", ast.NewArguments(ps[2:], nil, nil), debug.NewGoInfo(0)),
			c.Value())

		if getPatternType(v) == namePattern {
			d.bindName(v.(string), value)

			if rest := cs[i+1:]; len(rest) != 0 {
				dc = d.desugarDictCasesOfSameKey(dict, rest, dc)
			}

			dc = d.desugarCases(newDict, []ast.MatchCase{c}, dc)

			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.value) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	cs = make([]ast.MatchCase, 0, len(gs))

	for _, g := range gs {
		cs = append(
			cs,
			ast.NewMatchCase(g.value, d.desugarCases(newDict, g.cases, dc)))
	}

	return d.desugarCases(value, cs, dc)
}

func (d *desugarer) desugarScalarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	ks := []ast.SwitchCase{}

	for _, c := range cs {
		ks = append(ks, ast.NewSwitchCase(c.Pattern().(string), c.Value()))
	}

	return newSwitch(v, ks, dc)
}

func renameBoundNamesInCase(c ast.MatchCase) ast.MatchCase {
	p, ns := newPatternRenamer().rename(c.Pattern())
	return ast.NewMatchCase(p, newValueRenamer(ns).rename(c.Value()))
}

func equalPatterns(p, q interface{}) bool {
	switch x := p.(type) {
	case string:
		y, ok := q.(string)

		if !ok {
			return false
		}

		return x == y
	case ast.App:
		y, ok := q.(ast.App)

		if !ok ||
			x.Function().(string) != y.Function().(string) ||
			len(x.Arguments().Positionals()) != len(y.Arguments().Positionals()) {
			return false
		}

		for i := range x.Arguments().Positionals() {
			p := x.Arguments().Positionals()[i]
			q := y.Arguments().Positionals()[i]

			if p.Expanded() != q.Expanded() || !equalPatterns(p.Value(), q.Value()) {
				return false
			}
		}

		return true
	}

	panic(fmt.Errorf("Invalid pattern: %#v, %#v", p, q))
}
<|endoftext|>


<commit_before>package gom

import (
	"reflect"
)

type CreateSql func(TableModel) (string, []interface{})

type SqlGenerator struct {
	createSql   CreateSql
	tableModels []TableModel
}
type SqlFactory interface {
	Insert(TableModel) (string, []interface{})
	Update(TableModel) (string, []interface{})
	Replace(TableModel) (string, []interface{})
	Delete(TableModel) (string, []interface{})
	Query(TableModel) (string, []interface{})
}
type RowChooser interface {
	Scan(dest ...interface{}) error
}

type TableModel struct {
	ModelType  reflect.Type
	ModelValue reflect.Value
	TableName  string
	Columns    []Column
	Primary    Column
	Cnd        Condition
}
type Column struct {
	ColumnType reflect.Type
	ColumnName string
	FieldName  string
	IsPrimary  bool
	Auto       bool
}
type Condition interface {
	State() string
	Value() []interface{}
}
type Conditions struct {
	states string
	values []interface{}
}

func Cnd(sql string, values ...interface{}) Conditions {
	return Conditions{sql, values}
}
func (c Conditions) State() string {
	return c.states
}
func (c Conditions) Value() []interface{} {
	return c.values
}
func (c Conditions) And(sql string, values ...interface{}) Conditions {
	if c.states != "" {
		c.states += " and "
	}
	c.states += sql
	c.values = append(c.values, values)
	return c
}
func (c Conditions) Or(sql string, values ...interface{}) Conditions {
	if c.states != "" {
		c.states += " or "
	}
	c.states += sql
	c.values = append(c.values, values)
	return c
}
func (c Conditions) AndIn(name string, values ...interface{}) Conditions {
	if len(values) > 0 {
		if c.states != "" {
			c.states += " and "
		}
		sql := name + " in ("
		for i := 0; i < len(values); i++ {
			if i == 0 {
				sql += " ? "
			} else {
				sql += ", ? "
			}
		}
		sql += ")"
		c.states += sql
		c.values = append(c.values, values)
	}
	return c
}
func (c Conditions) OrIn(name string, values ...interface{}) Conditions {
	if len(values) > 0 {
		if c.states != "" {
			c.states += " or "
		}
		sql := name + " in ("
		for i := 0; i < len(values); i++ {
			if i == 0 {
				sql += " ? "
			} else {
				sql += ", ? "
			}
		}
		sql += ")"
		c.states += sql
		c.values = append(c.values, values)
	}
	return c
}
func (c Conditions) Sql(sql string, values ...interface{}) Conditions {
	c.states += sql
	c.values = append(c.values, values)
	return c
}

func (mo TableModel) InsertValues() []interface{} {
	var interfaces []interface{}
	results := reflect.Indirect(reflect.ValueOf(&interfaces))
	for _, column := range mo.Columns {
		vars := reflect.ValueOf(mo.ModelValue.FieldByName(column.FieldName).Interface())
		if results.Kind() == reflect.Ptr {
			results.Set(reflect.Append(results, vars.Addr()))
		} else {
			results.Set(reflect.Append(results, vars))
		}
	}
	return interfaces
}
func (m TableModel) GetPrimary() interface{} {
	return m.ModelValue.FieldByName(m.Primary.FieldName).Interface()
}
func (m TableModel) GetPrimaryCondition() Condition {
	if IsEmpty(m.GetPrimary()) || m.Primary.IsPrimary == false {
		return nil
	} else {
		return Conditions{"`" + m.Primary.ColumnName + "` = ?", []interface{}{m.GetPrimary()}}
	}
}
<commit_msg>And sql拼写错误的bug修复<commit_after>package gom

import (
	"reflect"
)

type CreateSql func(TableModel) (string, []interface{})

type SqlGenerator struct {
	createSql   CreateSql
	tableModels []TableModel
}
type SqlFactory interface {
	Insert(TableModel) (string, []interface{})
	Update(TableModel) (string, []interface{})
	Replace(TableModel) (string, []interface{})
	Delete(TableModel) (string, []interface{})
	Query(TableModel) (string, []interface{})
}
type RowChooser interface {
	Scan(dest ...interface{}) error
}

type TableModel struct {
	ModelType  reflect.Type
	ModelValue reflect.Value
	TableName  string
	Columns    []Column
	Primary    Column
	Cnd        Condition
}
type Column struct {
	ColumnType reflect.Type
	ColumnName string
	FieldName  string
	IsPrimary  bool
	Auto       bool
}
type Condition interface {
	State() string
	Value() []interface{}
}
type Conditions struct {
	states string
	values []interface{}
}

func Cnd(sql string, values ...interface{}) Conditions {
	return Conditions{sql, values}
}
func (c Conditions) State() string {
	return c.states
}
func (c Conditions) Value() []interface{} {
	return c.values
}
func (c Conditions) And(sql string, values ...interface{}) Conditions {
	if c.states != "" {
		c.states += " and "
	}
	c.states += sql
	c.values = append(c.values, values)
	return c
}
func (c Conditions) Or(sql string, values ...interface{}) Conditions {
	if c.states != "" {
		c.states += " or "
	}
	c.states += sql
	c.values = append(c.values, values)
	return c
}
func splitArrays(values interface{}) []interface{} {
	var results []interface{}
	val := reflect.ValueOf(values)
	_, ptr, sli := getType(values)
	if ptr {
		val = val.Elem()
	}
	if sli {
		lens := val.Len()
		if lens > 0 {
			for i := 0; i < lens; i++ {
				datas := splitArrays(val.Index(i).Interface())
				results = append(results, datas...)
			}
		}
	} else {
		results = append(results, val.Interface())
	}
	return results

}

func makeInSql(name string, values ...interface{}) (string, []interface{}) {
	sql := name + " in "
	var datas []interface{}
	if len(values) == 0 {
		return "", datas
	}
	datas = splitArrays(values)
	for i := 0; i < len(datas); i++ {
		if i == 0 {
			sql += " ? "
		} else {
			sql += ", ? "
		}
	}
	sql += ")"
	return sql, datas
}
func (c Conditions) AndIn(name string, values ...interface{}) Conditions {
	if len(values) > 0 {
		if c.states != "" {
			c.states += " and "
		}
		sql, datas := makeInSql(name, values...)
		c.states += sql
		c.values = append(c.values, datas)
	}
	return c
}

func (c Conditions) OrIn(name string, values ...interface{}) Conditions {
	if len(values) > 0 {
		if c.states != "" {
			c.states += " or "
		}
		sql, datas := makeInSql(name, values...)
		c.states += sql
		c.values = append(c.values, datas)
	}
	return c
}
func (c Conditions) Sql(sql string, values ...interface{}) Conditions {
	c.states += sql
	c.values = append(c.values, values)
	return c
}

func (mo TableModel) InsertValues() []interface{} {
	var interfaces []interface{}
	results := reflect.Indirect(reflect.ValueOf(&interfaces))
	for _, column := range mo.Columns {
		vars := reflect.ValueOf(mo.ModelValue.FieldByName(column.FieldName).Interface())
		if results.Kind() == reflect.Ptr {
			results.Set(reflect.Append(results, vars.Addr()))
		} else {
			results.Set(reflect.Append(results, vars))
		}
	}
	return interfaces
}
func (m TableModel) GetPrimary() interface{} {
	return m.ModelValue.FieldByName(m.Primary.FieldName).Interface()
}
func (m TableModel) GetPrimaryCondition() Condition {
	if IsEmpty(m.GetPrimary()) || m.Primary.IsPrimary == false {
		return nil
	} else {
		return Conditions{"`" + m.Primary.ColumnName + "` = ?", []interface{}{m.GetPrimary()}}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/bigroom/vision/models"
	"github.com/bigroom/zombies"
	"github.com/dgrijalva/jwt-go"
	"github.com/gorilla/websocket"
)

func messageLoop() {
	for {
		log.Println("Waiting for message...")
		m := <-messages
		log.Printf("Dispatching message '%v' to channel with key: '%v'", m.Content, m.Key())
		for _, client := range clients[m.Key()] {
			err := client.c.WriteJSON(m)
			if err != nil {
				fmt.Println("error sending message:", err)
			}
		}
	}
}

var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool { return true },
}

func dispatchHandler(w http.ResponseWriter, r *http.Request, t *jwt.Token) {
	log.Println("Dispatching")
	u, err := models.FetchUser("id", t.Claims["id"])
	if err != nil {
		log.Println("COuldnt get user")
		return
	}

	c, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println("couldn't upgrade:", err)
		return
	}

	defer c.Close()

	server := r.FormValue("server")
	if server == "" {
		server = "chat.freenode.net:6667"
	}

	log.Println("gets to here")
	resp, err := pool.Tell("exists", u.ID)
	if err != nil {
		log.Fatal("error getting exists:", err)
		return
	}

	if !resp.MustBool() {
		log.Println("Creating zombie")
		add := zombies.Add{
			ID:     u.ID,
			Nick:   u.Username,
			Server: server,
		}

		_, err := pool.Tell("add", add)
		if err != nil {
			log.Fatal("error creating:", err)
			return
		}
	}

	for {
		var a action
		err := c.ReadJSON(&a)
		if err != nil {
			log.Println("Closing connection. Error reading:", err)
			return
		}

		if a.Name == "SET" {
			log.Println("Adding user to chanel", a.Message)

			_, err := pool.Tell("join", zombies.Join{
				ID:      u.ID,
				Channel: a.Message,
			})

			if err != nil {
				log.Println("Closing connection. Error joining chanel:", err)
				return
			}

			clients[a.Message] = append(clients[a.Message], &conn{
				c:  c,
				id: u.ID,
			})
		} else if a.Name == "SEND" {
			log.Printf("Sending message '%v' to channel '%v'", a.Message, a.Channel)
			_, err := pool.Tell("send", zombies.Send{
				ID:      u.ID,
				Channel: a.Channel,
				Message: a.Message,
			})

			if err != nil {
				log.Println("Closing connection. Error sending message:", err)
				return
			}
		}
	}
}

type action struct {
	Name    string `json:"name"`
	Message string `json:"message"`
	Channel string `json:"channel"`
}

type conn struct {
	id int64
	c  *websocket.Conn
}
<commit_msg>Add response for communicating with clients. Add channels<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/bigroom/vision/models"
	"github.com/bigroom/zombies"
	"github.com/dgrijalva/jwt-go"
	"github.com/gorilla/websocket"
)

func messageLoop() {
	for {
		log.Println("Waiting for message...")
		m := <-messages
		log.Printf("Dispatching message '%v' to channel with key: '%v'", m.Content, m.Key())
		for _, client := range clients[m.Key()] {
			err := client.c.WriteJSON(response{
				Name:     "MESSAGE",
				Contents: m,
			})

			if err != nil {
				fmt.Println("error sending message:", err)
			}
		}
	}
}

var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool { return true },
}

func dispatchHandler(w http.ResponseWriter, r *http.Request, t *jwt.Token) {
	log.Println("Dispatching")
	u, err := models.FetchUser("id", t.Claims["id"])
	if err != nil {
		log.Println("COuldnt get user")
		return
	}

	c, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println("couldn't upgrade:", err)
		return
	}

	defer c.Close()

	server := r.FormValue("server")
	if server == "" {
		server = "chat.freenode.net:6667"
	}

	resp, err := pool.Tell("exists", u.ID)
	if err != nil {
		log.Fatal("error getting exists:", err)
		return
	}

	if !resp.MustBool() {
		log.Println("Creating zombie")
		add := zombies.Add{
			ID:     u.ID,
			Nick:   u.Username,
			Server: server,
		}

		_, err := pool.Tell("add", add)
		if err != nil {
			log.Fatal("error creating:", err)
			return
		}
	}

	for {
		var a action
		err := c.ReadJSON(&a)
		if err != nil {
			log.Println("Closing connection. Error reading:", err)
			return
		}

		if a.Name == "SET" {
			log.Println("Adding user to chanel", a.Message)

			_, err := pool.Tell("join", zombies.Join{
				ID:      u.ID,
				Channel: a.Message,
			})

			if err != nil {
				log.Println("Closing connection. Error joining chanel:", err)
				return
			}

			clients[a.Message] = append(clients[a.Message], &conn{
				c:  c,
				id: u.ID,
			})
		} else if a.Name == "SEND" {
			log.Printf("Sending message '%v' to channel '%v'", a.Message, a.Channel)
			_, err := pool.Tell("send", zombies.Send{
				ID:      u.ID,
				Channel: a.Channel,
				Message: a.Message,
			})

			if err != nil {
				log.Println("Closing connection. Error sending message:", err)
				return
			}
		} else if a.Name == "CHANNELS" {
			log.Println("Sending channels to user")

			resp, err := pool.Tell("channels", u.ID)
			if err != nil {
				log.Println("Closing connection. Could not connect to kite: ", err)
				return
			}

			var channels zombies.Channels
			resp.MustUnmarshal(&channels)

			err = c.WriteJSON(response{
				Contents: channels.Channels,
				Name:     "CHANNELS",
			})

			if err != nil {
				log.Println("Coudlnt wirte JSON")
			}
		}
	}
}

type action struct {
	Name    string `json:"name"`
	Message string `json:"message"`
	Channel string `json:"channel"`
}

type conn struct {
	id int64
	c  *websocket.Conn
}

type response struct {
	Name     string      `json:"name"`
	Contents interface{} `json:"contents"`
}
<|endoftext|>


<commit_before>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package cfg holds configuration shared by multiple parts
// of the go command.
package cfg

import (
	"fmt"
	"go/build"
	"os"
	"path/filepath"
	"runtime"

	"cmd/internal/objabi"
)

// These are general "build flags" used by build and other commands.
var (
	BuildA                 bool   // -a flag
	BuildBuildmode         string // -buildmode flag
	BuildContext           = build.Default
	BuildI                 bool               // -i flag
	BuildLinkshared        bool               // -linkshared flag
	BuildMSan              bool               // -msan flag
	BuildN                 bool               // -n flag
	BuildO                 string             // -o flag
	BuildP                 = runtime.NumCPU() // -p flag
	BuildPkgdir            string             // -pkgdir flag
	BuildRace              bool               // -race flag
	BuildToolexec          []string           // -toolexec flag
	BuildToolchainName     string
	BuildToolchainCompiler func() string
	BuildToolchainLinker   func() string
	BuildV                 bool // -v flag
	BuildWork              bool // -work flag
	BuildX                 bool // -x flag

	CmdName string // "build", "install", "list", etc.

	DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable)
)

func init() {
	BuildToolchainCompiler = func() string { return "missing-compiler" }
	BuildToolchainLinker = func() string { return "missing-linker" }
}

// An EnvVar is an environment variable Name=Value.
type EnvVar struct {
	Name  string
	Value string
}

// OrigEnv is the original environment of the program at startup.
var OrigEnv []string

// CmdEnv is the new environment for running go tool commands.
// User binaries (during go test or go run) are run with OrigEnv,
// not CmdEnv.
var CmdEnv []EnvVar

// Global build parameters (used during package load)
var (
	Goarch    = BuildContext.GOARCH
	Goos      = BuildContext.GOOS
	ExeSuffix string
	Gopath    = filepath.SplitList(BuildContext.GOPATH)
)

func init() {
	if Goos == "windows" {
		ExeSuffix = ".exe"
	}
}

var (
	GOROOT       = findGOROOT()
	GOBIN        = os.Getenv("GOBIN")
	GOROOTbin    = filepath.Join(GOROOT, "bin")
	GOROOTpkg    = filepath.Join(GOROOT, "pkg")
	GOROOTsrc    = filepath.Join(GOROOT, "src")
	GOROOT_FINAL = findGOROOT_FINAL()

	// Used in envcmd.MkEnv and build ID computations.
	GOARM    = fmt.Sprint(objabi.GOARM)
	GO386    = objabi.GO386
	GOMIPS   = objabi.GOMIPS
	GOMIPS64 = objabi.GOMIPS64
)

// Update build context to use our computed GOROOT.
func init() {
	BuildContext.GOROOT = GOROOT
	if runtime.Compiler != "gccgo" {
		// Note that we must use runtime.GOOS and runtime.GOARCH here,
		// as the tool directory does not move based on environment
		// variables. This matches the initialization of ToolDir in
		// go/build, except for using GOROOT rather than
		// runtime.GOROOT.
		build.ToolDir = filepath.Join(GOROOT, "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH)
	}
}

func findGOROOT() string {
	if env := os.Getenv("GOROOT"); env != "" {
		return filepath.Clean(env)
	}
	def := filepath.Clean(runtime.GOROOT())
	if runtime.Compiler == "gccgo" {
		// gccgo has no real GOROOT, and it certainly doesn't
		// depend on the executable's location.
		return def
	}
	exe, err := os.Executable()
	if err == nil {
		exe, err = filepath.Abs(exe)
		if err == nil {
			if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
				// If def (runtime.GOROOT()) and dir are the same
				// directory, prefer the spelling used in def.
				if isSameDir(def, dir) {
					return def
				}
				return dir
			}
			exe, err = filepath.EvalSymlinks(exe)
			if err == nil {
				if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
					if isSameDir(def, dir) {
						return def
					}
					return dir
				}
			}
		}
	}
	return def
}

func findGOROOT_FINAL() string {
	def := GOROOT
	if env := os.Getenv("GOROOT_FINAL"); env != "" {
		def = filepath.Clean(env)
	}
	return def
}

// isSameDir reports whether dir1 and dir2 are the same directory.
func isSameDir(dir1, dir2 string) bool {
	if dir1 == dir2 {
		return true
	}
	info1, err1 := os.Stat(dir1)
	info2, err2 := os.Stat(dir2)
	return err1 == nil && err2 == nil && os.SameFile(info1, info2)
}

// isGOROOT reports whether path looks like a GOROOT.
//
// It does this by looking for the path/pkg/tool directory,
// which is necessary for useful operation of the cmd/go tool,
// and is not typically present in a GOPATH.
func isGOROOT(path string) bool {
	stat, err := os.Stat(filepath.Join(path, "pkg", "tool"))
	if err != nil {
		return false
	}
	return stat.IsDir()
}
<commit_msg>cmd/go/internal/cfg: note the copy of this code in x/tools/cmd/godoc<commit_after>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package cfg holds configuration shared by multiple parts
// of the go command.
package cfg

import (
	"fmt"
	"go/build"
	"os"
	"path/filepath"
	"runtime"

	"cmd/internal/objabi"
)

// These are general "build flags" used by build and other commands.
var (
	BuildA                 bool   // -a flag
	BuildBuildmode         string // -buildmode flag
	BuildContext           = build.Default
	BuildI                 bool               // -i flag
	BuildLinkshared        bool               // -linkshared flag
	BuildMSan              bool               // -msan flag
	BuildN                 bool               // -n flag
	BuildO                 string             // -o flag
	BuildP                 = runtime.NumCPU() // -p flag
	BuildPkgdir            string             // -pkgdir flag
	BuildRace              bool               // -race flag
	BuildToolexec          []string           // -toolexec flag
	BuildToolchainName     string
	BuildToolchainCompiler func() string
	BuildToolchainLinker   func() string
	BuildV                 bool // -v flag
	BuildWork              bool // -work flag
	BuildX                 bool // -x flag

	CmdName string // "build", "install", "list", etc.

	DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable)
)

func init() {
	BuildToolchainCompiler = func() string { return "missing-compiler" }
	BuildToolchainLinker = func() string { return "missing-linker" }
}

// An EnvVar is an environment variable Name=Value.
type EnvVar struct {
	Name  string
	Value string
}

// OrigEnv is the original environment of the program at startup.
var OrigEnv []string

// CmdEnv is the new environment for running go tool commands.
// User binaries (during go test or go run) are run with OrigEnv,
// not CmdEnv.
var CmdEnv []EnvVar

// Global build parameters (used during package load)
var (
	Goarch    = BuildContext.GOARCH
	Goos      = BuildContext.GOOS
	ExeSuffix string
	Gopath    = filepath.SplitList(BuildContext.GOPATH)
)

func init() {
	if Goos == "windows" {
		ExeSuffix = ".exe"
	}
}

var (
	GOROOT       = findGOROOT()
	GOBIN        = os.Getenv("GOBIN")
	GOROOTbin    = filepath.Join(GOROOT, "bin")
	GOROOTpkg    = filepath.Join(GOROOT, "pkg")
	GOROOTsrc    = filepath.Join(GOROOT, "src")
	GOROOT_FINAL = findGOROOT_FINAL()

	// Used in envcmd.MkEnv and build ID computations.
	GOARM    = fmt.Sprint(objabi.GOARM)
	GO386    = objabi.GO386
	GOMIPS   = objabi.GOMIPS
	GOMIPS64 = objabi.GOMIPS64
)

// Update build context to use our computed GOROOT.
func init() {
	BuildContext.GOROOT = GOROOT
	if runtime.Compiler != "gccgo" {
		// Note that we must use runtime.GOOS and runtime.GOARCH here,
		// as the tool directory does not move based on environment
		// variables. This matches the initialization of ToolDir in
		// go/build, except for using GOROOT rather than
		// runtime.GOROOT.
		build.ToolDir = filepath.Join(GOROOT, "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH)
	}
}

// There is a copy of findGOROOT, isSameDir, and isGOROOT in
// x/tools/cmd/godoc/goroot.go.
// Try to keep them in sync for now.

// findGOROOT returns the GOROOT value, using either an explicitly
// provided environment variable, a GOROOT that contains the current
// os.Executable value, or else the GOROOT that the binary was built
// with from runtime.GOROOT().
//
// There is a copy of this code in x/tools/cmd/godoc/goroot.go.
func findGOROOT() string {
	if env := os.Getenv("GOROOT"); env != "" {
		return filepath.Clean(env)
	}
	def := filepath.Clean(runtime.GOROOT())
	if runtime.Compiler == "gccgo" {
		// gccgo has no real GOROOT, and it certainly doesn't
		// depend on the executable's location.
		return def
	}
	exe, err := os.Executable()
	if err == nil {
		exe, err = filepath.Abs(exe)
		if err == nil {
			if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
				// If def (runtime.GOROOT()) and dir are the same
				// directory, prefer the spelling used in def.
				if isSameDir(def, dir) {
					return def
				}
				return dir
			}
			exe, err = filepath.EvalSymlinks(exe)
			if err == nil {
				if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
					if isSameDir(def, dir) {
						return def
					}
					return dir
				}
			}
		}
	}
	return def
}

func findGOROOT_FINAL() string {
	def := GOROOT
	if env := os.Getenv("GOROOT_FINAL"); env != "" {
		def = filepath.Clean(env)
	}
	return def
}

// isSameDir reports whether dir1 and dir2 are the same directory.
func isSameDir(dir1, dir2 string) bool {
	if dir1 == dir2 {
		return true
	}
	info1, err1 := os.Stat(dir1)
	info2, err2 := os.Stat(dir2)
	return err1 == nil && err2 == nil && os.SameFile(info1, info2)
}

// isGOROOT reports whether path looks like a GOROOT.
//
// It does this by looking for the path/pkg/tool directory,
// which is necessary for useful operation of the cmd/go tool,
// and is not typically present in a GOPATH.
//
// There is a copy of this code in x/tools/cmd/godoc/goroot.go.
func isGOROOT(path string) bool {
	stat, err := os.Stat(filepath.Join(path, "pkg", "tool"))
	if err != nil {
		return false
	}
	return stat.IsDir()
}
<|endoftext|>


<commit_before>package publish

import (
	"strings"

	"github.com/jinzhu/gorm"
	"github.com/qor/qor/admin"
	"github.com/qor/qor/utils"

	"reflect"
)

const (
	PUBLISHED = false
	DIRTY     = true

	publishDraftMode = "publish:draft_mode"
	publishEvent     = "publish:publish_event"
)

type publishInterface interface {
	GetPublishStatus() bool
	SetPublishStatus(bool)
}

type PublishEventInterface interface {
	Publish(*gorm.DB) error
	Discard(*gorm.DB) error
}

type Status struct {
	PublishStatus bool
}

func (s Status) GetPublishStatus() bool {
	return s.PublishStatus
}

func (s *Status) SetPublishStatus(status bool) {
	s.PublishStatus = status
}

func (s Status) ConfigureQorResource(res *admin.Resource) {
	if res.GetMeta("PublishStatus") == nil {
		res.IndexAttrs(append(res.IndexAttrs(), "-PublishStatus")...)
		res.ShowAttrs(append(res.ShowAttrs(), "-PublishStatus")...)
		res.EditAttrs(append(res.EditAttrs(), "-PublishStatus")...)
		res.NewAttrs(append(res.NewAttrs(), "-PublishStatus")...)
	}
}

type Publish struct {
	DB *gorm.DB
}

func IsDraftMode(db *gorm.DB) bool {
	if draftMode, ok := db.Get(publishDraftMode); ok {
		if isDraft, ok := draftMode.(bool); ok && isDraft {
			return true
		}
	}
	return false
}

func IsPublishEvent(model interface{}) (ok bool) {
	if model != nil {
		_, ok = reflect.New(utils.ModelType(model)).Interface().(PublishEventInterface)
	}
	return
}

func IsPublishableModel(model interface{}) (ok bool) {
	if model != nil {
		_, ok = reflect.New(utils.ModelType(model)).Interface().(publishInterface)
	}
	return
}

var injectedJoinTableHandler = map[reflect.Type]bool{}

func New(db *gorm.DB) *Publish {
	tableHandler := gorm.DefaultTableNameHandler
	gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string {
		tableName := tableHandler(db, defaultTableName)

		if db != nil {
			if IsPublishableModel(db.Value) {
				// Set join table handler
				typ := utils.ModelType(db.Value)
				if !injectedJoinTableHandler[typ] {
					injectedJoinTableHandler[typ] = true
					scope := db.NewScope(db.Value)
					for _, field := range scope.GetModelStruct().StructFields {
						if many2many := utils.ParseTagOption(field.Tag.Get("gorm"))["MANY2MANY"]; many2many != "" {
							db.SetJoinTableHandler(db.Value, field.Name, &publishJoinTableHandler{})
							db.AutoMigrate(db.Value)
						}
					}
				}

				var forceDraftTable bool
				if forceDraftTable, ok := db.Get("publish:force_draft_table"); ok {
					if forceMode, ok := forceDraftTable.(bool); ok && forceMode {
						forceDraftTable = true
					}
				}

				if IsDraftMode(db) || forceDraftTable {
					return DraftTableName(tableName)
				}
			}
		}
		return tableName
	}

	db.AutoMigrate(&PublishEvent{})

	db.Callback().Create().Before("gorm:begin_transaction").Register("publish:set_table_to_draft", setTableAndPublishStatus(true))
	db.Callback().Create().Before("gorm:commit_or_rollback_transaction").
		Register("publish:sync_to_production_after_create", syncCreateFromProductionToDraft)
	db.Callback().Create().Before("gorm:commit_or_rollback_transaction").Register("gorm:create_publish_event", createPublishEvent)

	db.Callback().Delete().Before("gorm:begin_transaction").Register("publish:set_table_to_draft", setTableAndPublishStatus(true))
	db.Callback().Delete().Replace("gorm:delete", deleteScope)
	db.Callback().Delete().Before("gorm:commit_or_rollback_transaction").
		Register("publish:sync_to_production_after_delete", syncDeleteFromProductionToDraft)
	db.Callback().Delete().Before("gorm:commit_or_rollback_transaction").Register("gorm:create_publish_event", createPublishEvent)

	db.Callback().Update().Before("gorm:begin_transaction").Register("publish:set_table_to_draft", setTableAndPublishStatus(true))
	db.Callback().Update().Before("gorm:commit_or_rollback_transaction").
		Register("publish:sync_to_production", syncUpdateFromProductionToDraft)
	db.Callback().Update().Before("gorm:commit_or_rollback_transaction").Register("gorm:create_publish_event", createPublishEvent)

	db.Callback().RowQuery().Register("publish:set_table_in_draft_mode", setTableAndPublishStatus(false))
	db.Callback().Query().Before("gorm:query").Register("publish:set_table_in_draft_mode", setTableAndPublishStatus(false))
	return &Publish{DB: db}
}

func DraftTableName(table string) string {
	return OriginalTableName(table) + "_draft"
}

func OriginalTableName(table string) string {
	return strings.TrimSuffix(table, "_draft")
}

func (db *Publish) AutoMigrate(values ...interface{}) {
	for _, value := range values {
		tableName := db.DB.NewScope(value).TableName()
		db.DraftDB().Table(DraftTableName(tableName)).AutoMigrate(value)
	}
}

func (db Publish) ProductionDB() *gorm.DB {
	return db.DB.Set(publishDraftMode, false)
}

func (db Publish) DraftDB() *gorm.DB {
	return db.DB.Set(publishDraftMode, true)
}

func (db Publish) newResolver(records ...interface{}) *resolver {
	return &resolver{Records: records, DB: db.DB, Dependencies: map[string]*dependency{}}
}

func (db Publish) Publish(records ...interface{}) {
	db.newResolver(records...).Publish()
}

func (db Publish) Discard(records ...interface{}) {
	db.newResolver(records...).Discard()
}
<commit_msg>[publish] Using configure resource API<commit_after>package publish

import (
	"strings"

	"github.com/jinzhu/gorm"
	"github.com/qor/qor/admin"
	"github.com/qor/qor/resource"
	"github.com/qor/qor/utils"

	"reflect"
)

const (
	PUBLISHED = false
	DIRTY     = true

	publishDraftMode = "publish:draft_mode"
	publishEvent     = "publish:publish_event"
)

type publishInterface interface {
	GetPublishStatus() bool
	SetPublishStatus(bool)
}

type PublishEventInterface interface {
	Publish(*gorm.DB) error
	Discard(*gorm.DB) error
}

type Status struct {
	PublishStatus bool
}

func (s Status) GetPublishStatus() bool {
	return s.PublishStatus
}

func (s *Status) SetPublishStatus(status bool) {
	s.PublishStatus = status
}

func (s Status) ConfigureQorResource(res resource.Resourcer) {
	if res, ok := res.(*admin.Resource); ok {
		if res.GetMeta("PublishStatus") == nil {
			res.IndexAttrs(append(res.IndexAttrs(), "-PublishStatus")...)
			res.ShowAttrs(append(res.ShowAttrs(), "-PublishStatus")...)
			res.EditAttrs(append(res.EditAttrs(), "-PublishStatus")...)
			res.NewAttrs(append(res.NewAttrs(), "-PublishStatus")...)
		}
	}
}

type Publish struct {
	DB *gorm.DB
}

func IsDraftMode(db *gorm.DB) bool {
	if draftMode, ok := db.Get(publishDraftMode); ok {
		if isDraft, ok := draftMode.(bool); ok && isDraft {
			return true
		}
	}
	return false
}

func IsPublishEvent(model interface{}) (ok bool) {
	if model != nil {
		_, ok = reflect.New(utils.ModelType(model)).Interface().(PublishEventInterface)
	}
	return
}

func IsPublishableModel(model interface{}) (ok bool) {
	if model != nil {
		_, ok = reflect.New(utils.ModelType(model)).Interface().(publishInterface)
	}
	return
}

var injectedJoinTableHandler = map[reflect.Type]bool{}

func New(db *gorm.DB) *Publish {
	tableHandler := gorm.DefaultTableNameHandler
	gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string {
		tableName := tableHandler(db, defaultTableName)

		if db != nil {
			if IsPublishableModel(db.Value) {
				// Set join table handler
				typ := utils.ModelType(db.Value)
				if !injectedJoinTableHandler[typ] {
					injectedJoinTableHandler[typ] = true
					scope := db.NewScope(db.Value)
					for _, field := range scope.GetModelStruct().StructFields {
						if many2many := utils.ParseTagOption(field.Tag.Get("gorm"))["MANY2MANY"]; many2many != "" {
							db.SetJoinTableHandler(db.Value, field.Name, &publishJoinTableHandler{})
							db.AutoMigrate(db.Value)
						}
					}
				}

				var forceDraftTable bool
				if forceDraftTable, ok := db.Get("publish:force_draft_table"); ok {
					if forceMode, ok := forceDraftTable.(bool); ok && forceMode {
						forceDraftTable = true
					}
				}

				if IsDraftMode(db) || forceDraftTable {
					return DraftTableName(tableName)
				}
			}
		}
		return tableName
	}

	db.AutoMigrate(&PublishEvent{})

	db.Callback().Create().Before("gorm:begin_transaction").Register("publish:set_table_to_draft", setTableAndPublishStatus(true))
	db.Callback().Create().Before("gorm:commit_or_rollback_transaction").
		Register("publish:sync_to_production_after_create", syncCreateFromProductionToDraft)
	db.Callback().Create().Before("gorm:commit_or_rollback_transaction").Register("gorm:create_publish_event", createPublishEvent)

	db.Callback().Delete().Before("gorm:begin_transaction").Register("publish:set_table_to_draft", setTableAndPublishStatus(true))
	db.Callback().Delete().Replace("gorm:delete", deleteScope)
	db.Callback().Delete().Before("gorm:commit_or_rollback_transaction").
		Register("publish:sync_to_production_after_delete", syncDeleteFromProductionToDraft)
	db.Callback().Delete().Before("gorm:commit_or_rollback_transaction").Register("gorm:create_publish_event", createPublishEvent)

	db.Callback().Update().Before("gorm:begin_transaction").Register("publish:set_table_to_draft", setTableAndPublishStatus(true))
	db.Callback().Update().Before("gorm:commit_or_rollback_transaction").
		Register("publish:sync_to_production", syncUpdateFromProductionToDraft)
	db.Callback().Update().Before("gorm:commit_or_rollback_transaction").Register("gorm:create_publish_event", createPublishEvent)

	db.Callback().RowQuery().Register("publish:set_table_in_draft_mode", setTableAndPublishStatus(false))
	db.Callback().Query().Before("gorm:query").Register("publish:set_table_in_draft_mode", setTableAndPublishStatus(false))
	return &Publish{DB: db}
}

func DraftTableName(table string) string {
	return OriginalTableName(table) + "_draft"
}

func OriginalTableName(table string) string {
	return strings.TrimSuffix(table, "_draft")
}

func (db *Publish) AutoMigrate(values ...interface{}) {
	for _, value := range values {
		tableName := db.DB.NewScope(value).TableName()
		db.DraftDB().Table(DraftTableName(tableName)).AutoMigrate(value)
	}
}

func (db Publish) ProductionDB() *gorm.DB {
	return db.DB.Set(publishDraftMode, false)
}

func (db Publish) DraftDB() *gorm.DB {
	return db.DB.Set(publishDraftMode, true)
}

func (db Publish) newResolver(records ...interface{}) *resolver {
	return &resolver{Records: records, DB: db.DB, Dependencies: map[string]*dependency{}}
}

func (db Publish) Publish(records ...interface{}) {
	db.newResolver(records...).Publish()
}

func (db Publish) Discard(records ...interface{}) {
	db.newResolver(records...).Discard()
}
<|endoftext|>