<commit_before>package websocket

import (
	"sync"

	"github.com/kataras/iris/context"

	"github.com/gorilla/websocket"
)

type connectionKV struct {
	key   string // the connection ID
	value *connection
}

type connections []connectionKV

func (cs *connections) add(key string, value *connection) {
	args := *cs
	n := len(args)
	// check if already id/key exist, if yes replace the conn
	for i := 0; i < n; i++ {
		kv := &args[i]
		if kv.key == key {
			kv.value = value
			return
		}
	}

	c := cap(args)
	// make the connections slice bigger and put the conn
	if c > n {
		args = args[:n+1]
		kv := &args[n]
		kv.key = key
		kv.value = value
		*cs = args
		return
	}
	// append to the connections slice and put the conn
	kv := connectionKV{}
	kv.key = key
	kv.value = value
	*cs = append(args, kv)
}

func (cs *connections) get(key string) *connection {
	args := *cs
	n := len(args)
	for i := 0; i < n; i++ {
		kv := &args[i]
		if kv.key == key {
			return kv.value
		}
	}
	return nil
}

// returns the connection which removed and a bool value of found or not
// the connection is useful to fire the disconnect events, we use that form in order to
// make work things faster without the need of get-remove, just -remove should do the job.
func (cs *connections) remove(key string) (*connection, bool) {
	args := *cs
	n := len(args)
	for i := 0; i < n; i++ {
		kv := &args[i]
		if kv.key == key {
			conn := kv.value
			// we found the index,
			// let's remove the item by appending to the temp and
			// after set the pointer of the slice to this temp args
			args = append(args[:i], args[i+1:]...)
			*cs = args
			return conn, true
		}
	}
	return nil, false
}

type (
	// ConnectionFunc is the callback which fires when a client/connection is connected to the Server.
	// Receives one parameter which is the Connection
	ConnectionFunc func(Connection)

	// websocketRoomPayload is used as payload from the connection to the Server
	websocketRoomPayload struct {
		roomName     string
		connectionID string
	}

	// payloads, connection -> Server
	websocketMessagePayload struct {
		from string
		to   string
		data []byte
	}

	// Server is the websocket Server's implementation.
	//
	// It listens for websocket clients (either from the javascript client-side or from any websocket implementation).
	// See `OnConnection` , to register a single event which will handle all incoming connections and
	// the `Handler` which builds the upgrader handler that you can register to a route based on an Endpoint.
	//
	// To serve the built'n javascript client-side library look the `websocket.ClientHandler`.
	Server struct {
		config                Config
		connections           connections
		rooms                 map[string][]string // by default a connection is joined to a room which has the connection id as its name
		mu                    sync.RWMutex          // for rooms
		onConnectionListeners []ConnectionFunc
		//connectionPool        sync.Pool // sadly we can't make this because the websocket connection is live until is closed.
		handler context.Handler
	}
)

// New returns a new websocket Server based on a configuration.
// See `OnConnection` , to register a single event which will handle all incoming connections and
// the `Handler` which builds the upgrader handler that you can register to a route based on an Endpoint.
//
// To serve the built'n javascript client-side library look the `websocket.ClientHandler`.
func New(cfg Config) *Server {
	return &Server{
		config: cfg.Validate(),
		rooms:  make(map[string][]string, 0),
		onConnectionListeners: make([]ConnectionFunc, 0),
	}
}

// Handler builds the handler based on the configuration and returns it.
// It should be called once per Server, its result should be passed
// as a middleware to an iris route which will be responsible
// to register the websocket's endpoint.
//
// Endpoint is the path which the websocket Server will listen for clients/connections.
//
// To serve the built'n javascript client-side library look the `websocket.ClientHandler`.
func (s *Server) Handler() context.Handler {
	// build the upgrader once
	c := s.config

	upgrader := websocket.Upgrader{
		HandshakeTimeout:  c.HandshakeTimeout,
		ReadBufferSize:    c.ReadBufferSize,
		WriteBufferSize:   c.WriteBufferSize,
		Error:             c.Error,
		CheckOrigin:       c.CheckOrigin,
		Subprotocols:      c.Subprotocols,
		EnableCompression: c.EnableCompression,
	}

	return func(ctx context.Context) {
		// Upgrade upgrades the HTTP Server connection to the WebSocket protocol.
		//
		// The responseHeader is included in the response to the client's upgrade
		// request. Use the responseHeader to specify cookies (Set-Cookie) and the
		// application negotiated subprotocol (Sec--Protocol).
		//
		// If the upgrade fails, then Upgrade replies to the client with an HTTP error
		// response.
		conn, err := upgrader.Upgrade(ctx.ResponseWriter(), ctx.Request(), ctx.ResponseWriter().Header())
		if err != nil {
			ctx.Application().Logger().Warnf("websocket error: %v\n", err)
			ctx.StatusCode(503) // Status Service Unavailable
			return
		}
		s.handleConnection(ctx, conn)
	}
}

// handleConnection creates & starts to listening to a new connection
func (s *Server) handleConnection(ctx context.Context, websocketConn UnderlineConnection) {
	// use the config's id generator (or the default) to create a websocket client/connection id
	cid := s.config.IDGenerator(ctx)
	// create the new connection
	c := newConnection(ctx, s, websocketConn, cid)
	// add the connection to the Server's list
	s.connections.add(cid, c)

	// join to itself
	s.Join(c.ID(), c.ID())

	// NOTE TO ME: fire these first BEFORE startReader and startPinger
	// in order to set the events and any messages to send
	// the startPinger will send the OK to the client and only
	// then the client is able to send and receive from Server
	// when all things are ready and only then. DO NOT change this order.

	// fire the on connection event callbacks, if any
	for i := range s.onConnectionListeners {
		s.onConnectionListeners[i](c)
	}

	// start the ping
	c.startPinger()

	// start the messages reader
	c.startReader()
}

/* Notes:
   We use the id as the signature of the connection because with the custom IDGenerator
	 the developer can share this ID with a database field, so we want to give the oportunnity to handle
	 his/her websocket connections without even use the connection itself.

	 Another question may be:
	 Q: Why you use Server as the main actioner for all of the connection actions?
	 	  For example the Server.Disconnect(connID) manages the connection internal fields, is this code-style correct?
	 A: It's the correct code-style for these type of applications and libraries, Server manages all, the connnection's functions
	 should just do some internal checks (if needed) and push the action to its parent, which is the Server, the Server is able to
	 remove a connection, the rooms of its connected and all these things, so in order to not split the logic, we have the main logic
	 here, in the Server, and let the connection with some exported functions whose exists for the per-connection action user's code-style.

	 Ok my english are s** I can feel it, but these comments are mostly for me.
*/

/*
   connection actions, same as the connection's method,
    but these methods accept the connection ID,
    which is useful when the developer maps
    this id with a database field (using config.IDGenerator).
*/

// OnConnection is the main event you, as developer, will work with each of the websocket connections.
func (s *Server) OnConnection(cb ConnectionFunc) {
	s.onConnectionListeners = append(s.onConnectionListeners, cb)
}

// IsConnected returns true if the connection with that ID is connected to the Server
// useful when you have defined a custom connection id generator (based on a database)
// and you want to check if that connection is already connected (on multiple tabs)
func (s *Server) IsConnected(connID string) bool {
	c := s.connections.get(connID)
	return c != nil
}

// Join joins a websocket client to a room,
// first parameter is the room name and the second the connection.ID()
//
// You can use connection.Join("room name") instead.
func (s *Server) Join(roomName string, connID string) {
	s.mu.Lock()
	s.join(roomName, connID)
	s.mu.Unlock()
}

// join used internally, no locks used.
func (s *Server) join(roomName string, connID string) {
	if s.rooms[roomName] == nil {
		s.rooms[roomName] = make([]string, 0)
	}
	s.rooms[roomName] = append(s.rooms[roomName], connID)
}

// LeaveAll kicks out a connection from ALL of its joined rooms
func (s *Server) LeaveAll(connID string) {
	s.mu.Lock()
	for name, connectionIDs := range s.rooms {
		for i := range connectionIDs {
			if connectionIDs[i] == connID {
				// fire the on room leave connection's listeners
				s.connections.get(connID).fireOnLeave(name)
				// the connection is inside this room, lets remove it
				if i < len(s.rooms[name]) {
					s.rooms[name] = append(s.rooms[name][:i], s.rooms[name][i+1:]...)
				}
			}
		}
	}
	s.mu.Unlock()
}

// Leave leaves a websocket client from a room,
// first parameter is the room name and the second the connection.ID()
//
// You can use connection.Leave("room name") instead.
// Returns true if the connection has actually left from the particular room.
func (s *Server) Leave(roomName string, connID string) bool {
	s.mu.Lock()
	left := s.leave(roomName, connID)
	s.mu.Unlock()
	return left
}

// leave used internally, no locks used.
func (s *Server) leave(roomName string, connID string) (left bool) {
	///THINK: we could add locks to its room but we still use the lock for the whole rooms or we can just do what we do with connections
	// I will think about it on the next revision, so far we use the locks only for rooms so we are ok...
	if s.rooms[roomName] != nil {
		for i := range s.rooms[roomName] {
			if s.rooms[roomName][i] == connID {
				s.rooms[roomName] = append(s.rooms[roomName][:i], s.rooms[roomName][i+1:]...)
				left = true
				break
			}
		}
		if len(s.rooms[roomName]) == 0 { // if room is empty then delete it
			delete(s.rooms, roomName)
		}
	}

	if left {
		// fire the on room leave connection's listeners
		s.connections.get(connID).fireOnLeave(roomName)
	}
	return
}

// GetTotalConnections returns the number of total connections
func (s *Server) GetTotalConnections() int {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return len(s.connections)
}

// GetConnections returns all connections
func (s *Server) GetConnections() []Connection {
	s.mu.Lock()
	var conns []Connection
	for _, conn := range s.connections {
		conns = append(conns, conn.value)
	}
	s.mu.Unlock()
	return conns
}

// GetConnection returns single connection
func (s *Server) GetConnection(key string) Connection {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.connections.get(key)
}

// GetConnectionsByRoom returns a list of Connection
// which are joined to this room.
func (s *Server) GetConnectionsByRoom(roomName string) []Connection {
	s.mu.Lock()
	var conns []Connection
	if connIDs, found := s.rooms[roomName]; found {
		for _, connID := range connIDs {
			conns = append(conns, s.connections.get(connID))
		}

	}
	s.mu.Unlock()
	return conns
}

// emitMessage is the main 'router' of the messages coming from the connection
// this is the main function which writes the RAW websocket messages to the client.
// It sends them(messages) to the correct room (self, broadcast or to specific client)
//
// You don't have to use this generic method, exists only for extreme
// apps which you have an external goroutine with a list of custom connection list.
//
// You SHOULD use connection.EmitMessage/Emit/To().Emit/EmitMessage instead.
// let's keep it unexported for the best.
func (s *Server) emitMessage(from, to string, data []byte) {
	if to != All && to != Broadcast && s.rooms[to] != nil {
		// it suppose to send the message to a specific room/or a user inside its own room
		for _, connectionIDInsideRoom := range s.rooms[to] {
			if c := s.connections.get(connectionIDInsideRoom); c != nil {
				c.writeDefault(data) //send the message to the client(s)
			} else {
				// the connection is not connected but it's inside the room, we remove it on disconnect but for ANY CASE:
				cid := connectionIDInsideRoom
				if c != nil {
					cid = c.id
				}
				s.Leave(cid, to)
			}
		}
	} else {
		// it suppose to send the message to all opened connections or to all except the sender
		for _, cKV := range s.connections {
			connID := cKV.key
			if to != All && to != connID { // if it's not suppose to send to all connections (including itself)
				if to == Broadcast && from == connID { // if broadcast to other connections except this
					continue //here we do the opossite of previous block,
					// just skip this connection when it's suppose to send the message to all connections except the sender
				}

			}
			// send to the client(s) when the top validators passed
			cKV.value.writeDefault(data)
		}
	}
}

// Disconnect force-disconnects a websocket connection based on its connection.ID()
// What it does?
// 1. remove the connection from the list
// 2. leave from all joined rooms
// 3. fire the disconnect callbacks, if any
// 4. close the underline connection and return its error, if any.
//
// You can use the connection.Disconnect() instead.
func (s *Server) Disconnect(connID string) (err error) {
	// leave from all joined rooms before remove the actual connection from the list.
	// note: we cannot use that to send data if the client is actually closed.
	s.LeaveAll(connID)

	// remove the connection from the list
	if c, ok := s.connections.remove(connID); ok {
		if !c.disconnected {
			c.disconnected = true

			// fire the disconnect callbacks, if any
			c.fireDisconnect()
			// close the underline connection and return its error, if any.
			err = c.underline.Close()
		}
	}

	return
}
<commit_msg>Some changes for the benefit of performance for https://github.com/kataras/iris/pull/795<commit_after>package websocket

import (
	"sync"

	"github.com/kataras/iris/context"

	"github.com/gorilla/websocket"
)

type connectionKV struct {
	key   string // the connection ID
	value *connection
}

type connections []connectionKV

func (cs *connections) add(key string, value *connection) {
	args := *cs
	n := len(args)
	// check if already id/key exist, if yes replace the conn
	for i := 0; i < n; i++ {
		kv := &args[i]
		if kv.key == key {
			kv.value = value
			return
		}
	}

	c := cap(args)
	// make the connections slice bigger and put the conn
	if c > n {
		args = args[:n+1]
		kv := &args[n]
		kv.key = key
		kv.value = value
		*cs = args
		return
	}
	// append to the connections slice and put the conn
	kv := connectionKV{}
	kv.key = key
	kv.value = value
	*cs = append(args, kv)
}

func (cs *connections) get(key string) *connection {
	args := *cs
	n := len(args)
	for i := 0; i < n; i++ {
		kv := &args[i]
		if kv.key == key {
			return kv.value
		}
	}
	return nil
}

// returns the connection which removed and a bool value of found or not
// the connection is useful to fire the disconnect events, we use that form in order to
// make work things faster without the need of get-remove, just -remove should do the job.
func (cs *connections) remove(key string) (*connection, bool) {
	args := *cs
	n := len(args)
	for i := 0; i < n; i++ {
		kv := &args[i]
		if kv.key == key {
			conn := kv.value
			// we found the index,
			// let's remove the item by appending to the temp and
			// after set the pointer of the slice to this temp args
			args = append(args[:i], args[i+1:]...)
			*cs = args
			return conn, true
		}
	}
	return nil, false
}

type (
	// ConnectionFunc is the callback which fires when a client/connection is connected to the Server.
	// Receives one parameter which is the Connection
	ConnectionFunc func(Connection)

	// websocketRoomPayload is used as payload from the connection to the Server
	websocketRoomPayload struct {
		roomName     string
		connectionID string
	}

	// payloads, connection -> Server
	websocketMessagePayload struct {
		from string
		to   string
		data []byte
	}

	// Server is the websocket Server's implementation.
	//
	// It listens for websocket clients (either from the javascript client-side or from any websocket implementation).
	// See `OnConnection` , to register a single event which will handle all incoming connections and
	// the `Handler` which builds the upgrader handler that you can register to a route based on an Endpoint.
	//
	// To serve the built'n javascript client-side library look the `websocket.ClientHandler`.
	Server struct {
		config                Config
		connections           connections
		rooms                 map[string][]string // by default a connection is joined to a room which has the connection id as its name
		mu                    sync.RWMutex          // for rooms
		onConnectionListeners []ConnectionFunc
		//connectionPool        sync.Pool // sadly we can't make this because the websocket connection is live until is closed.
		handler context.Handler
	}
)

// New returns a new websocket Server based on a configuration.
// See `OnConnection` , to register a single event which will handle all incoming connections and
// the `Handler` which builds the upgrader handler that you can register to a route based on an Endpoint.
//
// To serve the built'n javascript client-side library look the `websocket.ClientHandler`.
func New(cfg Config) *Server {
	return &Server{
		config: cfg.Validate(),
		rooms:  make(map[string][]string, 0),
		onConnectionListeners: make([]ConnectionFunc, 0),
	}
}

// Handler builds the handler based on the configuration and returns it.
// It should be called once per Server, its result should be passed
// as a middleware to an iris route which will be responsible
// to register the websocket's endpoint.
//
// Endpoint is the path which the websocket Server will listen for clients/connections.
//
// To serve the built'n javascript client-side library look the `websocket.ClientHandler`.
func (s *Server) Handler() context.Handler {
	// build the upgrader once
	c := s.config

	upgrader := websocket.Upgrader{
		HandshakeTimeout:  c.HandshakeTimeout,
		ReadBufferSize:    c.ReadBufferSize,
		WriteBufferSize:   c.WriteBufferSize,
		Error:             c.Error,
		CheckOrigin:       c.CheckOrigin,
		Subprotocols:      c.Subprotocols,
		EnableCompression: c.EnableCompression,
	}

	return func(ctx context.Context) {
		// Upgrade upgrades the HTTP Server connection to the WebSocket protocol.
		//
		// The responseHeader is included in the response to the client's upgrade
		// request. Use the responseHeader to specify cookies (Set-Cookie) and the
		// application negotiated subprotocol (Sec--Protocol).
		//
		// If the upgrade fails, then Upgrade replies to the client with an HTTP error
		// response.
		conn, err := upgrader.Upgrade(ctx.ResponseWriter(), ctx.Request(), ctx.ResponseWriter().Header())
		if err != nil {
			ctx.Application().Logger().Warnf("websocket error: %v\n", err)
			ctx.StatusCode(503) // Status Service Unavailable
			return
		}
		s.handleConnection(ctx, conn)
	}
}

// handleConnection creates & starts to listening to a new connection
func (s *Server) handleConnection(ctx context.Context, websocketConn UnderlineConnection) {
	// use the config's id generator (or the default) to create a websocket client/connection id
	cid := s.config.IDGenerator(ctx)
	// create the new connection
	c := newConnection(ctx, s, websocketConn, cid)
	// add the connection to the Server's list
	s.connections.add(cid, c)

	// join to itself
	s.Join(c.ID(), c.ID())

	// NOTE TO ME: fire these first BEFORE startReader and startPinger
	// in order to set the events and any messages to send
	// the startPinger will send the OK to the client and only
	// then the client is able to send and receive from Server
	// when all things are ready and only then. DO NOT change this order.

	// fire the on connection event callbacks, if any
	for i := range s.onConnectionListeners {
		s.onConnectionListeners[i](c)
	}

	// start the ping
	c.startPinger()

	// start the messages reader
	c.startReader()
}

/* Notes:
   We use the id as the signature of the connection because with the custom IDGenerator
	 the developer can share this ID with a database field, so we want to give the oportunnity to handle
	 his/her websocket connections without even use the connection itself.

	 Another question may be:
	 Q: Why you use Server as the main actioner for all of the connection actions?
	 	  For example the Server.Disconnect(connID) manages the connection internal fields, is this code-style correct?
	 A: It's the correct code-style for these type of applications and libraries, Server manages all, the connnection's functions
	 should just do some internal checks (if needed) and push the action to its parent, which is the Server, the Server is able to
	 remove a connection, the rooms of its connected and all these things, so in order to not split the logic, we have the main logic
	 here, in the Server, and let the connection with some exported functions whose exists for the per-connection action user's code-style.

	 Ok my english are s** I can feel it, but these comments are mostly for me.
*/

/*
   connection actions, same as the connection's method,
    but these methods accept the connection ID,
    which is useful when the developer maps
    this id with a database field (using config.IDGenerator).
*/

// OnConnection is the main event you, as developer, will work with each of the websocket connections.
func (s *Server) OnConnection(cb ConnectionFunc) {
	s.onConnectionListeners = append(s.onConnectionListeners, cb)
}

// IsConnected returns true if the connection with that ID is connected to the Server
// useful when you have defined a custom connection id generator (based on a database)
// and you want to check if that connection is already connected (on multiple tabs)
func (s *Server) IsConnected(connID string) bool {
	c := s.connections.get(connID)
	return c != nil
}

// Join joins a websocket client to a room,
// first parameter is the room name and the second the connection.ID()
//
// You can use connection.Join("room name") instead.
func (s *Server) Join(roomName string, connID string) {
	s.mu.Lock()
	s.join(roomName, connID)
	s.mu.Unlock()
}

// join used internally, no locks used.
func (s *Server) join(roomName string, connID string) {
	if s.rooms[roomName] == nil {
		s.rooms[roomName] = make([]string, 0)
	}
	s.rooms[roomName] = append(s.rooms[roomName], connID)
}

// LeaveAll kicks out a connection from ALL of its joined rooms
func (s *Server) LeaveAll(connID string) {
	s.mu.Lock()
	for name, connectionIDs := range s.rooms {
		for i := range connectionIDs {
			if connectionIDs[i] == connID {
				// fire the on room leave connection's listeners
				s.connections.get(connID).fireOnLeave(name)
				// the connection is inside this room, lets remove it
				if i < len(s.rooms[name]) {
					s.rooms[name] = append(s.rooms[name][:i], s.rooms[name][i+1:]...)
				}
			}
		}
	}
	s.mu.Unlock()
}

// Leave leaves a websocket client from a room,
// first parameter is the room name and the second the connection.ID()
//
// You can use connection.Leave("room name") instead.
// Returns true if the connection has actually left from the particular room.
func (s *Server) Leave(roomName string, connID string) bool {
	s.mu.Lock()
	left := s.leave(roomName, connID)
	s.mu.Unlock()
	return left
}

// leave used internally, no locks used.
func (s *Server) leave(roomName string, connID string) (left bool) {
	///THINK: we could add locks to its room but we still use the lock for the whole rooms or we can just do what we do with connections
	// I will think about it on the next revision, so far we use the locks only for rooms so we are ok...
	if s.rooms[roomName] != nil {
		for i := range s.rooms[roomName] {
			if s.rooms[roomName][i] == connID {
				s.rooms[roomName] = append(s.rooms[roomName][:i], s.rooms[roomName][i+1:]...)
				left = true
				break
			}
		}
		if len(s.rooms[roomName]) == 0 { // if room is empty then delete it
			delete(s.rooms, roomName)
		}
	}

	if left {
		// fire the on room leave connection's listeners
		s.connections.get(connID).fireOnLeave(roomName)
	}
	return
}

// GetTotalConnections returns the number of total connections
func (s *Server) GetTotalConnections() int {
	s.mu.RLock()
	l:= len(s.connections)
	s.mu.RUnlock()
	return l
}

// GetConnections returns all connections
func (s *Server) GetConnections() []Connection {
	s.mu.RLock()
	conns:= make([]Connection, len(s.connections))
	copy(conns, s.connections)
	s.mu.RUnlock()
	return conns
}

// GetConnection returns single connection
func (s *Server) GetConnection(key string) Connection {
	return s.connections.get(key)
}

// GetConnectionsByRoom returns a list of Connection
// which are joined to this room.
func (s *Server) GetConnectionsByRoom(roomName string) []Connection {
	s.mu.Lock()
	var conns []Connection
	if connIDs, found := s.rooms[roomName]; found {
		for _, connID := range connIDs {
			conns = append(conns, s.connections.get(connID))
		}

	}
	s.mu.Unlock()
	return conns
}

// emitMessage is the main 'router' of the messages coming from the connection
// this is the main function which writes the RAW websocket messages to the client.
// It sends them(messages) to the correct room (self, broadcast or to specific client)
//
// You don't have to use this generic method, exists only for extreme
// apps which you have an external goroutine with a list of custom connection list.
//
// You SHOULD use connection.EmitMessage/Emit/To().Emit/EmitMessage instead.
// let's keep it unexported for the best.
func (s *Server) emitMessage(from, to string, data []byte) {
	if to != All && to != Broadcast && s.rooms[to] != nil {
		// it suppose to send the message to a specific room/or a user inside its own room
		for _, connectionIDInsideRoom := range s.rooms[to] {
			if c := s.connections.get(connectionIDInsideRoom); c != nil {
				c.writeDefault(data) //send the message to the client(s)
			} else {
				// the connection is not connected but it's inside the room, we remove it on disconnect but for ANY CASE:
				cid := connectionIDInsideRoom
				if c != nil {
					cid = c.id
				}
				s.Leave(cid, to)
			}
		}
	} else {
		// it suppose to send the message to all opened connections or to all except the sender
		for _, cKV := range s.connections {
			connID := cKV.key
			if to != All && to != connID { // if it's not suppose to send to all connections (including itself)
				if to == Broadcast && from == connID { // if broadcast to other connections except this
					continue //here we do the opossite of previous block,
					// just skip this connection when it's suppose to send the message to all connections except the sender
				}

			}
			// send to the client(s) when the top validators passed
			cKV.value.writeDefault(data)
		}
	}
}

// Disconnect force-disconnects a websocket connection based on its connection.ID()
// What it does?
// 1. remove the connection from the list
// 2. leave from all joined rooms
// 3. fire the disconnect callbacks, if any
// 4. close the underline connection and return its error, if any.
//
// You can use the connection.Disconnect() instead.
func (s *Server) Disconnect(connID string) (err error) {
	// leave from all joined rooms before remove the actual connection from the list.
	// note: we cannot use that to send data if the client is actually closed.
	s.LeaveAll(connID)

	// remove the connection from the list
	if c, ok := s.connections.remove(connID); ok {
		if !c.disconnected {
			c.disconnected = true

			// fire the disconnect callbacks, if any
			c.fireDisconnect()
			// close the underline connection and return its error, if any.
			err = c.underline.Close()
		}
	}

	return
}
<|endoftext|>


<commit_before>// Copyright © 2015 Clement 'cmc' Rey <cr.rey.clement@gmail.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package mmm

import (
	"fmt"
	"reflect"
)

// -----------------------------------------------------------------------------

// Type represents the underlying type of an interface.
type Type int

const (
	// TypeInvalid is an illegal type.
	TypeInvalid Type = iota
	// TypeNumeric is any of bool/int/uint/float/complex and their variants.
	TypeNumeric Type = iota
	// TypeArray is an array of any underlying type.
	TypeArray Type = iota
	// TypeStruct is any struct.
	TypeStruct Type = iota
	// TypeUnsafePointer is any pointer from the unsafe package.
	TypeUnsafePointer Type = iota
)

// TypeOf returns the underlying type of an interface.
func TypeOf(v interface{}) (Type, error) {
	return typeOf(reflect.ValueOf(v))
}

func typeOf(v reflect.Value) (Type, error) {
	if !v.IsValid() {
		return TypeInvalid, Error(fmt.Sprintf("unsuppported type: %#v", v))
	}

	k := v.Type().Kind()

	switch k {
	case reflect.Bool:
		return TypeNumeric, nil
	case reflect.Int:
		return TypeNumeric, nil
	case reflect.Int8:
		return TypeNumeric, nil
	case reflect.Int16:
		return TypeNumeric, nil
	case reflect.Int32:
		return TypeNumeric, nil
	case reflect.Int64:
		return TypeNumeric, nil
	case reflect.Uint:
		return TypeNumeric, nil
	case reflect.Uint8:
		return TypeNumeric, nil
	case reflect.Uint16:
		return TypeNumeric, nil
	case reflect.Uint32:
		return TypeNumeric, nil
	case reflect.Uint64:
		return TypeNumeric, nil
	case reflect.Uintptr:
		return TypeNumeric, nil
	case reflect.Float32:
		return TypeNumeric, nil
	case reflect.Float64:
		return TypeNumeric, nil
	case reflect.Complex64:
		return TypeNumeric, nil
	case reflect.Complex128:
		return TypeNumeric, nil
	case reflect.Array:
		return TypeArray, nil
	case reflect.Struct:
		return TypeStruct, nil
	case reflect.UnsafePointer:
		return TypeUnsafePointer, nil
	default:
		return TypeInvalid, Error(fmt.Sprintf("unsuppported type: %#v", k.String()))
	}
}

// TypeCheck recursively checks the underlying types of `v` and returns an error
// if one or more of those types are illegal.
func TypeCheck(v interface{}) error {
	return typeCheck(reflect.ValueOf(v))
}

func typeCheck(v reflect.Value) error {
	t, err := typeOf(v)
	if err != nil {
		return nil
	}

	if t == TypeArray && v.Len() > 0 {
		return typeCheck(v.Index(0))
	}

	if t == TypeStruct {
		fields := v.NumField()
		for i := 0; i < fields; i++ {
			if err := typeCheck(v.Field(i)); err != nil {
				return err
			}
		}
	}

	return nil
}
<commit_msg>more concise type switch<commit_after>// Copyright © 2015 Clement 'cmc' Rey <cr.rey.clement@gmail.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package mmm

import (
	"fmt"
	"reflect"
)

// -----------------------------------------------------------------------------

// Type represents the underlying type of an interface.
type Type int

const (
	// TypeInvalid is an illegal type.
	TypeInvalid Type = iota
	// TypeNumeric is any of bool/int/uint/float/complex and their variants.
	TypeNumeric Type = iota
	// TypeArray is an array of any underlying type.
	TypeArray Type = iota
	// TypeStruct is any struct.
	TypeStruct Type = iota
	// TypeUnsafePointer is any pointer from the unsafe package.
	TypeUnsafePointer Type = iota
)

// TypeOf returns the underlying type of an interface.
func TypeOf(v interface{}) (Type, error) {
	return typeOf(reflect.ValueOf(v))
}

func typeOf(v reflect.Value) (Type, error) {
	if !v.IsValid() {
		return TypeInvalid, Error(fmt.Sprintf("unsuppported type: %#v", v))
	}

	k := v.Type().Kind()

	switch k {
	case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16,
		reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16,
		reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32,
		reflect.Float64, reflect.Complex64, reflect.Complex128:
		return TypeNumeric, nil
	case reflect.Array:
		return TypeArray, nil
	case reflect.Struct:
		return TypeStruct, nil
	case reflect.UnsafePointer:
		return TypeUnsafePointer, nil
	default:
		return TypeInvalid, Error(fmt.Sprintf("unsuppported type: %#v", k.String()))
	}
}

// TypeCheck recursively checks the underlying types of `v` and returns an error
// if one or more of those types are illegal.
func TypeCheck(v interface{}) error {
	return typeCheck(reflect.ValueOf(v))
}

func typeCheck(v reflect.Value) error {
	t, err := typeOf(v)
	if err != nil {
		return nil
	}

	if t == TypeArray && v.Len() > 0 {
		return typeCheck(v.Index(0))
	}

	if t == TypeStruct {
		fields := v.NumField()
		for i := 0; i < fields; i++ {
			if err := typeCheck(v.Field(i)); err != nil {
				return err
			}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package tw

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"gopkg.in/mgo.v2/bson"
)

var (
	ErrNoMorePages = errors.New("No more remaining pages")
)

type FollowersPage struct {
	IDs            []int64 `json:"ids"`
	NextCursor     int64   `json:"next_cursor"`
	PreviousCursor int64   `json:"previous_cursor"`
}

type FriendsPage struct {
	IDs            []int64 `json:"ids"`
	NextCursor     int64   `json:"next_cursor"`
	PreviousCursor int64   `json:"previous_cursor"`
}

type RubyDate struct {
	value time.Time
}

func (t RubyDate) MarshalJSON() ([]byte, error) {
	return []byte(`"` + t.value.Format(time.RubyDate) + `"`), nil
}

func (t RubyDate) UnmarshalJSON(data []byte) error {
	ts := strings.Trim(string(data), `"`)
	var err error
	t.value, err = time.Parse(time.RubyDate, ts)
	return err
}

func (t RubyDate) GetBSON() (interface{}, error) {
	return t.value, nil
}

func (t RubyDate) SetBSON(raw bson.Raw) error {
	return raw.Unmarshal(&t.value)
}

type User struct {
	ID              int64    `json:"id"                bson:"_id"`
	Name            string   `json:"name"              bson:"name"`
	ScreenName      string   `json:"screen_name"       bson:"screen_name"`
	Description     string   `json:"description"       bson:"description"`
	ProfileImageURL string   `json:"profile_image_url" bson:"profile_image_url"`
	Location        string   `json:"location"          bson:"location"`
	Lang            string   `json:"lang"              bson:"lang"`
	TimeZone        string   `json:"time_zone"         bson:"time_zone"`
	URL             string   `json:"url"               bson:"url"`
	Protected       bool     `json:"protected"         bson:"protected"`
	Verified        bool     `json:"verified"          bson:"verified"`
	FriendsCount    int      `json:"friends_count"     bson:"friends_count"`
	ListedCount     int      `json:"listed_count"      bson:"listed_count"`
	FavouritesCount int      `json:"favourites_count"  bson:"favourites_count"`
	FollowersCount  int      `json:"followers_count"   bson:"followers_count"`
	StatusesCount   int      `json:"statuses_count"    bson:"statuses_count"`
	CreatedAt       RubyDate `json:"created_at"        bson:"created_at"`
}

type UserMention struct {
	ID int64 `json:"id" bson:"id"`
}
type Entities struct {
	UserMentions []UserMention `json:"user_mentions"   bson:"user_mentions"`
}
type Tweet struct {
	ID            int64    `json:"id"                 bson:"_id"`
	UserID        int64    `json:"user_id"            bson:"user_id"`
	Text          string   `json:"text"               bson:"text"`
	Retweeted     bool     `json:"retweeted"          bson:"retweeted"`
	IsRetweet     bool     `json:"is_retweet"         bson:"is_retweet"`
	RetweetCount  uint     `json:"retweet_count"      bson:"retweet_count"`
	FavoriteCount uint     `json:"favorite_count"     bson:"favorite_count"`
	Sensitive     bool     `json:"possibly_sensitive" bson:"possibly_sensitive"`
	Entities      Entities `json:"entities"           bson:"entities"`
	CreatedAt     RubyDate `json:"created_at"         bson:"created_at"`
}

type Client struct {
	consumerKey       string
	consumerSecret    string
	bearerAccessToken string
}

type FollowersIterator struct {
	client     *Client
	userID     int64
	screenName string
	count      int
	cursor     int64
}

type FriendsIterator struct {
	client     *Client
	userID     int64
	screenName string
	count      int
	cursor     int64
}

func (t *FollowersIterator) Next(data *[]int64) error {
	if t.cursor == 0 {
		return ErrNoMorePages
	}
	url := fmt.Sprintf("%s/followers/ids.json?count=%d&cursor=%d",
		baseURL, t.count, t.cursor)
	if t.userID != 0 {
		url += fmt.Sprintf("&user_id=%d", t.userID)
	} else {
		url += "&screen_name=" + t.screenName
	}
	req, err := t.client.prepareRequest("GET", url)
	if err != nil {
		return err
	}
	var resp FollowersPage
	if err = exec(req, &resp); err != nil {
		return err
	}
	t.cursor = resp.NextCursor
	*data = resp.IDs
	return nil
}

func (t *FriendsIterator) Next(data *[]int64) error {
	if t.cursor == 0 {
		return ErrNoMorePages
	}
	url := fmt.Sprintf("%s/friends/ids.json?count=%d&cursor=%d",
		baseURL, t.count, t.cursor)
	if t.userID != 0 {
		url += fmt.Sprintf("&user_id=%d", t.userID)
	} else {
		url += "&screen_name=" + t.screenName
	}
	req, err := t.client.prepareRequest("GET", url)
	if err != nil {
		return err
	}
	var resp FriendsPage
	if err = exec(req, &resp); err != nil {
		return err
	}
	t.cursor = resp.NextCursor
	*data = resp.IDs
	return nil
}
<commit_msg>Fix RubyDate marshalling problems<commit_after>package tw

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"gopkg.in/mgo.v2/bson"
)

var (
	ErrNoMorePages = errors.New("No more remaining pages")
)

type FollowersPage struct {
	IDs            []int64 `json:"ids"`
	NextCursor     int64   `json:"next_cursor"`
	PreviousCursor int64   `json:"previous_cursor"`
}

type FriendsPage struct {
	IDs            []int64 `json:"ids"`
	NextCursor     int64   `json:"next_cursor"`
	PreviousCursor int64   `json:"previous_cursor"`
}

type RubyDate struct {
	value time.Time
}

func (t *RubyDate) MarshalJSON() ([]byte, error) {
	return []byte(`"` + t.value.Format(time.RubyDate) + `"`), nil
}

func (t *RubyDate) UnmarshalJSON(data []byte) error {
	ts := strings.Trim(string(data), `"`)
	var err error
	t.value, err = time.Parse(time.RubyDate, ts)
	return err
}

func (t *RubyDate) GetBSON() (interface{}, error) {
	return t.value, nil
}

func (t *RubyDate) SetBSON(raw bson.Raw) error {
	return raw.Unmarshal(&t.value)
}

type User struct {
	ID              int64     `json:"id"                bson:"_id"`
	Name            string    `json:"name"              bson:"name"`
	ScreenName      string    `json:"screen_name"       bson:"screen_name"`
	Description     string    `json:"description"       bson:"description"`
	ProfileImageURL string    `json:"profile_image_url" bson:"profile_image_url"`
	Location        string    `json:"location"          bson:"location"`
	Lang            string    `json:"lang"              bson:"lang"`
	TimeZone        string    `json:"time_zone"         bson:"time_zone"`
	URL             string    `json:"url"               bson:"url"`
	Protected       bool      `json:"protected"         bson:"protected"`
	Verified        bool      `json:"verified"          bson:"verified"`
	FriendsCount    int       `json:"friends_count"     bson:"friends_count"`
	ListedCount     int       `json:"listed_count"      bson:"listed_count"`
	FavouritesCount int       `json:"favourites_count"  bson:"favourites_count"`
	FollowersCount  int       `json:"followers_count"   bson:"followers_count"`
	StatusesCount   int       `json:"statuses_count"    bson:"statuses_count"`
	CreatedAt       *RubyDate `json:"created_at"        bson:"created_at"`
	RetrievedAt     time.Time `json:"retrieved_at"      bson:"retrieved_at"`
}

type UserMention struct {
	ID int64 `json:"id" bson:"id"`
}
type Entities struct {
	UserMentions []UserMention `json:"user_mentions"   bson:"user_mentions"`
}
type Tweet struct {
	ID            int64    `json:"id"                 bson:"_id"`
	UserID        int64    `json:"user_id"            bson:"user_id"`
	Text          string   `json:"text"               bson:"text"`
	Retweeted     bool     `json:"retweeted"          bson:"retweeted"`
	IsRetweet     bool     `json:"is_retweet"         bson:"is_retweet"`
	RetweetCount  uint     `json:"retweet_count"      bson:"retweet_count"`
	FavoriteCount uint     `json:"favorite_count"     bson:"favorite_count"`
	Sensitive     bool     `json:"possibly_sensitive" bson:"possibly_sensitive"`
	Entities      Entities `json:"entities"           bson:"entities"`
	CreatedAt     RubyDate `json:"created_at"         bson:"created_at"`
}

type Client struct {
	consumerKey       string
	consumerSecret    string
	bearerAccessToken string
}

type FollowersIterator struct {
	client     *Client
	userID     int64
	screenName string
	count      int
	cursor     int64
}

type FriendsIterator struct {
	client     *Client
	userID     int64
	screenName string
	count      int
	cursor     int64
}

func (t *FollowersIterator) Next(data *[]int64) error {
	if t.cursor == 0 {
		return ErrNoMorePages
	}
	url := fmt.Sprintf("%s/followers/ids.json?count=%d&cursor=%d",
		baseURL, t.count, t.cursor)
	if t.userID != 0 {
		url += fmt.Sprintf("&user_id=%d", t.userID)
	} else {
		url += "&screen_name=" + t.screenName
	}
	req, err := t.client.prepareRequest("GET", url)
	if err != nil {
		return err
	}
	var resp FollowersPage
	if err = exec(req, &resp); err != nil {
		return err
	}
	t.cursor = resp.NextCursor
	*data = resp.IDs
	return nil
}

func (t *FriendsIterator) Next(data *[]int64) error {
	if t.cursor == 0 {
		return ErrNoMorePages
	}
	url := fmt.Sprintf("%s/friends/ids.json?count=%d&cursor=%d",
		baseURL, t.count, t.cursor)
	if t.userID != 0 {
		url += fmt.Sprintf("&user_id=%d", t.userID)
	} else {
		url += "&screen_name=" + t.screenName
	}
	req, err := t.client.prepareRequest("GET", url)
	if err != nil {
		return err
	}
	var resp FriendsPage
	if err = exec(req, &resp); err != nil {
		return err
	}
	t.cursor = resp.NextCursor
	*data = resp.IDs
	return nil
}
<|endoftext|>


<commit_before>// types.go

// This file contains the various types used by the API

package atlas

// APIError is for errors returned by the RIPE API.
type APIError struct {
	Error struct {
		Status int    `json:"status"`
		Code   int    `json:"code"`
		Detail string `json:"detail"`
		Title  string `json:"title"`
	} `json:"error"`
}

// Key is holding the API key parameters
type Key struct {
	UUID      string `json:"uuid"`
	ValidFrom string `json:"valid_from"`
	ValidTo   string `json:"valid_to"`
	Enabled   bool
	IsActive  bool    `json:"is_active"`
	CreatedAt string  `json:"created_at"`
	Label     string  `json:"label"`
	Grants    []Grant `json:"grants"`
	Type      string  `json:"type"`
}

// Grant is the permission(s) associated with a key
type Grant struct {
	Permission string `json:"permission"`
	Target     struct {
		Type string `json:"type"`
		ID   string `json:"id"`
	} `json:"target"`
}

// Probe is holding probe's data
type Probe struct {
	AddressV4      string `json:"address_v4"`
	AddressV6      string `json:"address_v6"`
	AsnV4          int    `json:"asn_v4"`
	AsnV6          int    `json:"asn_v6"`
	CountryCode    string `json:"country_code"`
	Description    string `json:"description"`
	FirstConnected int    `json:"first_connected"`
	Geometry       struct {
		Type        string    `json:"type"`
		Coordinates []float64 `json:"coordinates"`
	} `json:"geometry"`
	ID            int    `json:"id"`
	IsAnchor      bool   `json:"is_anchor"`
	IsPublic      bool   `json:"is_public"`
	LastConnected int    `json:"last_connected"`
	PrefixV4      string `json:"prefix_v4"`
	PrefixV6      string `json:"prefix_v6"`
	Status        struct {
		Since string `json:"since"`
		ID    int    `json:"id"`
		Name  string `json:"name"`
	} `json:"status"`
	StatusSince int `json:"status_since"`
	Tags        []struct {
		Name string `json:"name"`
		Slug string `json:"slug"`
	} `json:"tags"`
	Type string `json:"type"`
}

// Measurement is what we are working with
type Measurement struct {
	Af                    int                    `json:"af"`
	CreationTime          int                    `json:"creation_time"`
	Description           string                 `json:"description"`
	DestinationOptionSize interface{}            `json:"destination_option_size"`
	DontFragment          interface{}            `json:"dont_fragment"`
	DuplicateTimeout      interface{}            `json:"duplicate_timeout"`
	FirstHop              int                    `json:"first_hop"`
	Group                 string                 `json:"group"`
	GroupID               int                    `json:"group_id"`
	HopByHopOptionSize    interface{}            `json:"hop_by_hop_option_size"`
	ID                    int                    `json:"id"`
	InWifiGroup           bool                   `json:"in_wifi_group"`
	Interval              int                    `json:"interval"`
	IsAllScheduled        bool                   `json:"is_all_scheduled"`
	IsOneoff              bool                   `json:"is_oneoff"`
	IsPublic              bool                   `json:"is_public"`
	MaxHops               int                    `json:"max_hops"`
	PacketInterval        interface{}            `json:"packet_interval"`
	Packets               int                    `json:"packets"`
	Paris                 int                    `json:"paris"`
	ParticipantCount      int                    `json:"participant_count"`
	ParticipationRequests []ParticipationRequest `json:"participation_requests"`
	Port                  interface{}            `json:"port"`
	ProbesRequested       int                    `json:"probes_requested"`
	ProbesScheduled       int                    `json:"probes_scheduled"`
	Protocol              string                 `json:"protocol"`
	ResolveOnProbe        bool                   `json:"resolve_on_probe"`
	ResolvedIPs           []string               `json:"resolved_ips"`
	ResponseTimeout       int                    `json:"response_timeout"`
	Result                string                 `json:"result"`
	Size                  int                    `json:"size"`
	Spread                interface{}            `json:"spread"`
	StartTime             int                    `json:"start_time"`
	Status                struct {
		ID   int    `json:"id"`
		Name string `json:"name"`
	} `json:"status"`
	StopTime  int    `json:"stop_time"`
	Target    string `json:"target"`
	TargetASN int    `json:"target_asn"`
	TargetIP  string `json:"target_ip"`
	Type      string `json:"type"`
}

// ParticipationRequest allow you to add or remove probes from a measurement that
// was already created
type ParticipationRequest struct {
	Action        string `json:"action"`
	CreatedAt     int    `json:"created_at"`
	ID            int    `json:"id"`
	Self          string `json:"self"`
	Measurement   string `json:"measurement"`
	MeasurementID int    `json:"measurement_id"`
	Requested     int    `json:"requested"`
	Type          string `json:"type"`
	Value         string `json:"value"`
	Logs          string `json:"logs"`
}

var (
	// ProbeTypes should be obvious
	ProbeTypes = []string{"area", "country", "prefix", "asn", "probes", "msm"}
	// AreaTypes should also be obvious
	AreaTypes = []string{"WW", "West", "North-Central", "South-Central", "North-East", "South-East"}
)

// MeasurementRequest contains the different measurement to create/view
type MeasurementRequest struct {
	// see below for definition
	Definitions []Definition `json:"definitions"`

	// requested set of probes
	Probes ProbeSet `json:"probes"`
	//
	BillTo       int  `json:"bill_to,omitempty"`
	IsOneoff     bool `json:"is_oneoff,omitempty"`
	SkipDNSCheck bool `json:"skip_dns_check,omitempty"`
	Times        int  `json:"times,omitempty"`
	StartTime    int  `json:"start_time,omitempty"`
	StopTime     int  `json:"stop_time,omitempty"`
}

// ProbeSet is a set of probes obviously
type ProbeSet []struct {
	Requested int               `json:"requested"` // number of probes
	Type      string            `json:"type"`      // area, country, prefix, asn, probes, msm
	Value     string            `json:"value"`     // can be numeric or string
	Tags      map[string]string `json:"tags,omitempty"`
}

// Definition is used to create measurements
type Definition struct {
	// Required fields
	Description string `json:"description"`
	Type        string `json:"type"`
	AF          int    `json:"af"`

	// Required for all but "dns"
	Target string `json:"target,omitempty"`

	GroupID        int    `json:"group_id,omitempty"`
	Group          string `json:"group,omitempty"`
	InWifiGroup    bool   `json:"in_wifi_group,omitempty"`
	Spread         int    `json:"spread,omitempty"`
	Packets        int    `json:"packets,omitempty"`
	PacketInterval int    `json:"packet_interval,omitempty"`

	// Common parameters
	ExtraWait      int  `json:"extra_wait,omitempty"`
	IsOneoff       bool `json:"is_oneoff,omitempty"`
	IsPublic       bool `json:"is_public,omitempty"`
	ResolveOnProbe bool `json:"resolve_on_probe,omitempty"`

	// Default depends on type
	Interval int `json:"interval,omitempty"`

	// dns & traceroute parameters
	Protocol string `json:"protocol"`

	// dns parameters
	QueryClass       string `json:"query_class,omitempty"`
	QueryType        string `json:"query_type,omitempty"`
	QueryArgument    string `json:"query_argument,omitempty"`
	Retry            int    `json:"retry"`
	SetCDBit         bool   `json:"set_cd_bit"`
	SetDOBit         bool   `json:"set_do_bit"`
	SetNSIDBit       bool   `json:"set_nsid_bit"`
	SetRDBit         bool   `json:"set_rd_bit"`
	UDPPayloadSize   int    `json:"udp_payload_size"`
	UseProbeResolver bool   `json:"use_probe_resolver"`

	// ping parameters
	//   none (see target)

	// traceroute parameters
	DestinationOptionSize int  `json:"destination_option_size"`
	DontFragment          bool `json:"dont_fragment"`
	DuplicateTimeout      int  `json:"duplicate_timeout"`
	FirstHop              int  `json:"first_hop"`
	HopByHopOptionSize    int  `json:"hop_by_hop_option_size"`
	MaxHops               int  `json:"max_hops"`
	Paris                 int  `json:"paris"`

	// ntp parameters
	//   none (see target)

	// http parameters
	//   none (see target)

	// sslcert parameters
	//   none (see target)

	// sslcert & traceroute parameters
	Port int `json:"port"`

	// ping & traceroute parameters
	Size int `json:"size"`

	// wifi parameters
	AnonymousIdentity string `json:"anonymous_identity,omitempty"`
	Cert              string `json:"cert,omitempty"`
	EAP               string `json:"eap,omitempty"`
}
<commit_msg>Some parameters are optional.<commit_after>// types.go

// This file contains the various types used by the API

package atlas

// APIError is for errors returned by the RIPE API.
type APIError struct {
	Error struct {
		Status int    `json:"status"`
		Code   int    `json:"code"`
		Detail string `json:"detail"`
		Title  string `json:"title"`
	} `json:"error"`
}

// Key is holding the API key parameters
type Key struct {
	UUID      string `json:"uuid"`
	ValidFrom string `json:"valid_from"`
	ValidTo   string `json:"valid_to"`
	Enabled   bool
	IsActive  bool    `json:"is_active"`
	CreatedAt string  `json:"created_at"`
	Label     string  `json:"label"`
	Grants    []Grant `json:"grants"`
	Type      string  `json:"type"`
}

// Grant is the permission(s) associated with a key
type Grant struct {
	Permission string `json:"permission"`
	Target     struct {
		Type string `json:"type"`
		ID   string `json:"id"`
	} `json:"target"`
}

// Probe is holding probe's data
type Probe struct {
	AddressV4      string `json:"address_v4"`
	AddressV6      string `json:"address_v6"`
	AsnV4          int    `json:"asn_v4"`
	AsnV6          int    `json:"asn_v6"`
	CountryCode    string `json:"country_code"`
	Description    string `json:"description"`
	FirstConnected int    `json:"first_connected"`
	Geometry       struct {
		Type        string    `json:"type"`
		Coordinates []float64 `json:"coordinates"`
	} `json:"geometry"`
	ID            int    `json:"id"`
	IsAnchor      bool   `json:"is_anchor"`
	IsPublic      bool   `json:"is_public"`
	LastConnected int    `json:"last_connected"`
	PrefixV4      string `json:"prefix_v4"`
	PrefixV6      string `json:"prefix_v6"`
	Status        struct {
		Since string `json:"since"`
		ID    int    `json:"id"`
		Name  string `json:"name"`
	} `json:"status"`
	StatusSince int `json:"status_since"`
	Tags        []struct {
		Name string `json:"name"`
		Slug string `json:"slug"`
	} `json:"tags"`
	Type string `json:"type"`
}

// Measurement is what we are working with
type Measurement struct {
	Af                    int                    `json:"af"`
	CreationTime          int                    `json:"creation_time"`
	Description           string                 `json:"description"`
	DestinationOptionSize interface{}            `json:"destination_option_size"`
	DontFragment          interface{}            `json:"dont_fragment"`
	DuplicateTimeout      interface{}            `json:"duplicate_timeout"`
	FirstHop              int                    `json:"first_hop"`
	Group                 string                 `json:"group"`
	GroupID               int                    `json:"group_id"`
	HopByHopOptionSize    interface{}            `json:"hop_by_hop_option_size"`
	ID                    int                    `json:"id"`
	InWifiGroup           bool                   `json:"in_wifi_group"`
	Interval              int                    `json:"interval"`
	IsAllScheduled        bool                   `json:"is_all_scheduled"`
	IsOneoff              bool                   `json:"is_oneoff"`
	IsPublic              bool                   `json:"is_public"`
	MaxHops               int                    `json:"max_hops"`
	PacketInterval        interface{}            `json:"packet_interval"`
	Packets               int                    `json:"packets"`
	Paris                 int                    `json:"paris"`
	ParticipantCount      int                    `json:"participant_count"`
	ParticipationRequests []ParticipationRequest `json:"participation_requests"`
	Port                  interface{}            `json:"port"`
	ProbesRequested       int                    `json:"probes_requested"`
	ProbesScheduled       int                    `json:"probes_scheduled"`
	Protocol              string                 `json:"protocol"`
	ResolveOnProbe        bool                   `json:"resolve_on_probe"`
	ResolvedIPs           []string               `json:"resolved_ips"`
	ResponseTimeout       int                    `json:"response_timeout"`
	Result                string                 `json:"result"`
	Size                  int                    `json:"size"`
	Spread                interface{}            `json:"spread"`
	StartTime             int                    `json:"start_time"`
	Status                struct {
		ID   int    `json:"id"`
		Name string `json:"name"`
	} `json:"status"`
	StopTime  int    `json:"stop_time"`
	Target    string `json:"target"`
	TargetASN int    `json:"target_asn"`
	TargetIP  string `json:"target_ip"`
	Type      string `json:"type"`
}

// ParticipationRequest allow you to add or remove probes from a measurement that
// was already created
type ParticipationRequest struct {
	Action        string `json:"action"`
	CreatedAt     int    `json:"created_at"`
	ID            int    `json:"id"`
	Self          string `json:"self"`
	Measurement   string `json:"measurement"`
	MeasurementID int    `json:"measurement_id"`
	Requested     int    `json:"requested"`
	Type          string `json:"type"`
	Value         string `json:"value"`
	Logs          string `json:"logs"`
}

var (
	// ProbeTypes should be obvious
	ProbeTypes = []string{"area", "country", "prefix", "asn", "probes", "msm"}
	// AreaTypes should also be obvious
	AreaTypes = []string{"WW", "West", "North-Central", "South-Central", "North-East", "South-East"}
)

// MeasurementRequest contains the different measurement to create/view
type MeasurementRequest struct {
	// see below for definition
	Definitions []Definition `json:"definitions"`

	// requested set of probes
	Probes ProbeSet `json:"probes"`
	//
	BillTo       int  `json:"bill_to,omitempty"`
	IsOneoff     bool `json:"is_oneoff,omitempty"`
	SkipDNSCheck bool `json:"skip_dns_check,omitempty"`
	Times        int  `json:"times,omitempty"`
	StartTime    int  `json:"start_time,omitempty"`
	StopTime     int  `json:"stop_time,omitempty"`
}

// ProbeSet is a set of probes obviously
type ProbeSet []struct {
	Requested int               `json:"requested"` // number of probes
	Type      string            `json:"type"`      // area, country, prefix, asn, probes, msm
	Value     string            `json:"value"`     // can be numeric or string
	Tags      map[string]string `json:"tags,omitempty"`
}

// Definition is used to create measurements
type Definition struct {
	// Required fields
	Description string `json:"description"`
	Type        string `json:"type"`
	AF          int    `json:"af"`

	// Required for all but "dns"
	Target string `json:"target,omitempty"`

	GroupID        int    `json:"group_id,omitempty"`
	Group          string `json:"group,omitempty"`
	InWifiGroup    bool   `json:"in_wifi_group,omitempty"`
	Spread         int    `json:"spread,omitempty"`
	Packets        int    `json:"packets,omitempty"`
	PacketInterval int    `json:"packet_interval,omitempty"`

	// Common parameters
	ExtraWait      int  `json:"extra_wait,omitempty"`
	IsOneoff       bool `json:"is_oneoff,omitempty"`
	IsPublic       bool `json:"is_public,omitempty"`
	ResolveOnProbe bool `json:"resolve_on_probe,omitempty"`

	// Default depends on type
	Interval int `json:"interval,omitempty"`

	// dns & traceroute parameters
	Protocol string `json:"protocol"`

	// dns parameters
	QueryClass       string `json:"query_class,omitempty"`
	QueryType        string `json:"query_type,omitempty"`
	QueryArgument    string `json:"query_argument,omitempty"`
	Retry            int    `json:"retry"`
	SetCDBit         bool   `json:"set_cd_bit"`
	SetDOBit         bool   `json:"set_do_bit"`
	SetNSIDBit       bool   `json:"set_nsid_bit"`
	SetRDBit         bool   `json:"set_rd_bit"`
	UDPPayloadSize   int    `json:"udp_payload_size"`
	UseProbeResolver bool   `json:"use_probe_resolver"`

	// ping parameters
	//   none (see target)

	// traceroute parameters
	DestinationOptionSize int  `json:"destination_option_size,omitempty"`
	DontFragment          bool `json:"dont_fragment,omitempty"`
	DuplicateTimeout      int  `json:"duplicate_timeout,omitempty"`
	FirstHop              int  `json:"first_hop,omitempty"`
	HopByHopOptionSize    int  `json:"hop_by_hop_option_size,omitempty"`
	MaxHops               int  `json:"max_hops,omitempty"`
	Paris                 int  `json:"paris,omitempty"`

	// ntp parameters
	//   none (see target)

	// http parameters
	//   none (see target)

	// sslcert parameters
	//   none (see target)

	// sslcert & traceroute parameters
	Port int `json:"port,omitempty"`

	// ping & traceroute parameters
	Size int `json:"size,omitempty"`

	// wifi parameters
	AnonymousIdentity string `json:"anonymous_identity,omitempty"`
	Cert              string `json:"cert,omitempty"`
	EAP               string `json:"eap,omitempty"`
}
<|endoftext|>


<commit_before>package gomarathon

// RequestOptions passed for query api
type RequestOptions struct {
	Method string
	Path   string
	Datas  interface{}
	Params *Parameters
}

// Parameters to build url query
type Parameters struct {
	Cmd         string
	Host        string
	Scale       bool
	CallbackURL string
}

// Response representation of a full marathon response
type Response struct {
	Code     int
	Apps     []*Application `json:"apps,omitempty"`
	App      *Application   `json:"app,omitempty"`
	Versions []string       `json:",omitempty"`
	Tasks    []*Task        `json:"tasks,omitempty"`
}

// Application marathon application see :
// https://github.com/mesosphere/marathon/blob/master/REST.md#apps
type Application struct {
	ID              string            `json:"id"`
	Cmd             string            `json:"cmd,omitempty"`
	Constraints     [][]string        `json:"constraints,omitempty"`
	Container       *Container        `json:"container,omitempty"`
	CPUs            float32           `json:"cpus,omitempty"`
	Env             map[string]string `json:"env,omitempty"`
	Executor        string            `json:"executor,omitempty"`
	HealthChecks    []*HealthCheck    `json:"healthChecks,omitempty"`
	Instances       int               `json:"instances,omitemptys"`
	Mem             float32           `json:"mem,omitempty"`
	Tasks           []*Task           `json:"tasks,omitempty"`
	Ports           []int             `json:"ports,omitempty"`
	RequirePorts    bool              `json:"requirePorts,omitempty"`
	BackoffFactor   float32           `json:"backoffFactor,omitempty"`
	TasksRunning    int               `json:"tasksRunning,omitempty"`
	TasksStaged     int               `json:"tasksStaged,omitempty"`
	UpgradeStrategy *UpgradeStrategy  `json:"upgradeStrategy,omitempty"`
	Uris            []string          `json:"uris,omitempty"`
	Version         string            `json:"version,omitempty"`
}

// Container is docker parameters
type Container struct {
	Type    string    `json:"type,omitempty"`
	Docker  *Docker   `json:"docker,omitempty"`
	Volumes []*Volume `json:"volumes,omitempty"`
}

// Docker options
type Docker struct {
	Image        string         `json:"image,omitempty"`
	Network      string         `json:"network,omitempty"`
	PortMappings []*PortMapping `json:"portMappings,omitempty"`
}

// Volume is used for mounting a host directory as a container volume
type Volume struct {
	ContainerPath string `json:"containerPath,omitempty"`
	HostPath      string `json:"hostPath,omitempty"`
	Mode          string `json:"mode,omitempty"`
}

// Container PortMappings
type PortMapping struct {
	ContainerPort int    `json:"containerPort,omitempty"`
	HostPort      int    `json:"hostPort,omitempty"`
	ServicePort   int    `json:"servicePort,omitempty"`
	Protocol      string `json:"protocol,omitempty"`
}

// UpgradeStrategy has a minimumHealthCapacity which defines the minimum number of healty nodes
type UpgradeStrategy struct {
	MinimumHealthCapacity float32 `json:"minimumHealthCapacity,omitempty"`
}

// HealthCheck is described here:
// https://github.com/mesosphere/marathon/blob/master/REST.md#healthchecks
type HealthCheck struct {
	Protocol           string `json:"protocol,omitempty"`
	Path               string `json:"path,omitempty"`
	GracePeriodSeconds int    `json:"gracePeriodSeconds,omitempty"`
	IntervalSeconds    int    `json:"intervalSeconds,omitempty"`
	PortIndex          int    `json:"portIndex,omitempty"`
	TimeoutSeconds     int    `json:"timeoutSeconds,omitempty"`
}

// Task is described here:
// https://github.com/mesosphere/marathon/blob/master/REST.md#tasks
type Task struct {
	AppID     string `json:"appId"`
	Host      string `json:"host"`
	ID        string `json:"id"`
	Ports     []int  `json:"ports"`
	StagedAt  string `json:"stagedAt"`
	StartedAt string `json:"startedAt"`
	Version   string `json:"version"`
}

// EventSubscription is described here:
// https://github.com/mesosphere/marathon/blob/master/REST.md#event-subscriptions
type EventSubscription struct {
	CallbackURL  string   `json:"CallbackUrl"`
	ClientIP     string   `json:"ClientIp"`
	EventType    string   `json:"eventType"`
	CallbackURLs []string `json:"CallbackUrls"`
}
<commit_msg>add type Deployment for Application<commit_after>package gomarathon

// RequestOptions passed for query api
type RequestOptions struct {
	Method string
	Path   string
	Datas  interface{}
	Params *Parameters
}

// Parameters to build url query
type Parameters struct {
	Cmd         string
	Host        string
	Scale       bool
	CallbackURL string
}

// Response representation of a full marathon response
type Response struct {
	Code     int
	Apps     []*Application `json:"apps,omitempty"`
	App      *Application   `json:"app,omitempty"`
	Versions []string       `json:",omitempty"`
	Tasks    []*Task        `json:"tasks,omitempty"`
}

// Application marathon application see :
// https://github.com/mesosphere/marathon/blob/master/REST.md#apps
type Application struct {
	ID              string            `json:"id"`
	Cmd             string            `json:"cmd,omitempty"`
	Constraints     [][]string        `json:"constraints,omitempty"`
	Container       *Container        `json:"container,omitempty"`
	CPUs            float32           `json:"cpus,omitempty"`
	Deployments     []*Deployment     `json:"deployments,omitempty"`
	Env             map[string]string `json:"env,omitempty"`
	Executor        string            `json:"executor,omitempty"`
	HealthChecks    []*HealthCheck    `json:"healthChecks,omitempty"`
	Instances       int               `json:"instances,omitemptys"`
	Mem             float32           `json:"mem,omitempty"`
	Tasks           []*Task           `json:"tasks,omitempty"`
	Ports           []int             `json:"ports,omitempty"`
	RequirePorts    bool              `json:"requirePorts,omitempty"`
	BackoffFactor   float32           `json:"backoffFactor,omitempty"`
	TasksRunning    int               `json:"tasksRunning,omitempty"`
	TasksStaged     int               `json:"tasksStaged,omitempty"`
	UpgradeStrategy *UpgradeStrategy  `json:"upgradeStrategy,omitempty"`
	Uris            []string          `json:"uris,omitempty"`
	Version         string            `json:"version,omitempty"`
}

// Container is docker parameters
type Container struct {
	Type    string    `json:"type,omitempty"`
	Docker  *Docker   `json:"docker,omitempty"`
	Volumes []*Volume `json:"volumes,omitempty"`
}

// Docker options
type Docker struct {
	Image        string         `json:"image,omitempty"`
	Network      string         `json:"network,omitempty"`
	PortMappings []*PortMapping `json:"portMappings,omitempty"`
}

// Volume is used for mounting a host directory as a container volume
type Volume struct {
	ContainerPath string `json:"containerPath,omitempty"`
	HostPath      string `json:"hostPath,omitempty"`
	Mode          string `json:"mode,omitempty"`
}

// Container PortMappings
type PortMapping struct {
	ContainerPort int    `json:"containerPort,omitempty"`
	HostPort      int    `json:"hostPort,omitempty"`
	ServicePort   int    `json:"servicePort,omitempty"`
	Protocol      string `json:"protocol,omitempty"`
}

// UpgradeStrategy has a minimumHealthCapacity which defines the minimum number of healty nodes
type UpgradeStrategy struct {
	MinimumHealthCapacity float32 `json:"minimumHealthCapacity,omitempty"`
}

// HealthCheck is described here:
// https://github.com/mesosphere/marathon/blob/master/REST.md#healthchecks
type HealthCheck struct {
	Protocol           string `json:"protocol,omitempty"`
	Path               string `json:"path,omitempty"`
	GracePeriodSeconds int    `json:"gracePeriodSeconds,omitempty"`
	IntervalSeconds    int    `json:"intervalSeconds,omitempty"`
	PortIndex          int    `json:"portIndex,omitempty"`
	TimeoutSeconds     int    `json:"timeoutSeconds,omitempty"`
}

// Task is described here:
// https://github.com/mesosphere/marathon/blob/master/REST.md#tasks
type Task struct {
	AppID     string `json:"appId"`
	Host      string `json:"host"`
	ID        string `json:"id"`
	Ports     []int  `json:"ports"`
	StagedAt  string `json:"stagedAt"`
	StartedAt string `json:"startedAt"`
	Version   string `json:"version"`
}

// Deployment is described here:
// https://mesosphere.github.io/marathon/docs/rest-api.html#get-/v2/deployments
type Deployment struct {
	AffectedApps   []string          `json:"affectedApps"`
	ID             string            `json:"id"`
	Steps          []*DeploymentStep `json:"steps"`
	CurrentActions []*DeploymentStep `json:"currentActions"`
	CurrentStep    int               `json:"currentStep"`
	TotalSteps     int               `json:"totalSteps"`
	Version        string            `json:"version"`
}

// Deployment steps
type DeploymentStep struct {
	Action string `json:"action"`
	App    string `json:"app"`
}

// EventSubscription is described here:
// https://github.com/mesosphere/marathon/blob/master/REST.md#event-subscriptions
type EventSubscription struct {
	CallbackURL  string   `json:"CallbackUrl"`
	ClientIP     string   `json:"ClientIp"`
	EventType    string   `json:"eventType"`
	CallbackURLs []string `json:"CallbackUrls"`
}
<|endoftext|>


<commit_before>//go:generate generateEmojiCodeMap -pkg discordgo
// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains custom types, currently only a timestamp wrapper.

package discordgo

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"
)

// Timestamp stores a timestamp, as sent by the Discord API.
type Timestamp string

// Parse parses a timestamp string into a time.Time object.
// The only time this can fail is if Discord changes their timestamp format.
func (t Timestamp) Parse() (time.Time, error) {
	return time.Parse(time.RFC3339, string(t))
}

// RESTError stores error information about a request with a bad response code.
// Message is not always present, there are cases where api calls can fail
// without returning a json message.
type RESTError struct {
	Request      *http.Request
	Response     *http.Response
	ResponseBody []byte

	Message *APIErrorMessage // Message may be nil.
}

func newRestError(req *http.Request, resp *http.Response, body []byte) *RESTError {
	restErr := &RESTError{
		Request:      req,
		Response:     resp,
		ResponseBody: body,
	}

	// Attempt to decode the error and assume no message was provided if it fails
	var msg *APIErrorMessage
	err := json.Unmarshal(body, &msg)
	if err == nil {
		restErr.Message = msg
	}

	return restErr
}

func (r RESTError) Error() string {
	return fmt.Sprintf("HTTP %s, %s", r.Response.Status, r.ResponseBody)
}

// IDSlice Is a slice of snowflake id's that properly marshals and unmarshals the way discord expects them to
// They unmarshal from string arrays and marshals back to string arrays
type IDSlice []int64

func (ids *IDSlice) UnmarshalJSON(data []byte) error {
	if len(data) < 2 {
		return nil
	}

	// Split and strip away "[" "]"
	split := strings.Split(string(data[1:len(data)-1]), ",")
	*ids = make([]int64, len(split))
	for i, s := range split {
		s = strings.TrimSpace(s)
		if len(s) < 3 {
			// Empty or invalid
			continue
		}

		// Strip away quotes and parse
		parsed, err := strconv.ParseInt(s[1:len(s)-1], 10, 64)
		if err != nil {
			return err
		}
		(*ids)[i] = parsed
	}

	return nil
}

func (ids IDSlice) MarshalJSON() ([]byte, error) {
	// Capacity:
	// 2 brackets
	// each id is:
	//    18 characters currently, but 1 extra added for the future,
	//    1 comma
	//    2 quotes
	if len(ids) < 1 {
		return []byte("[]"), nil
	}

	outPut := make([]byte, 1, 2+(len(ids)*22))
	outPut[0] = '['

	for i, id := range ids {
		if i != 0 {
			outPut = append(outPut, '"', ',', '"')
		} else {
			outPut = append(outPut, '"')
		}
		outPut = append(outPut, []byte(strconv.FormatInt(id, 10))...)
	}

	outPut = append(outPut, '"', ']')
	return outPut, nil
}

type EmojiName struct {
	string
}

func (emoji EmojiName) String() string {
	if codepoint, ok := emojiCodeMap[emoji.string]; ok {
		emoji.string = codepoint
	}
	// Discord does not accept the emoji qualifier character.
	return strings.Replace(emoji.string, "\uFE0F", "", 1)
}
<commit_msg>Fix parsing int slices incorrectly<commit_after>//go:generate generateEmojiCodeMap -pkg discordgo
// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains custom types, currently only a timestamp wrapper.

package discordgo

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"
)

// Timestamp stores a timestamp, as sent by the Discord API.
type Timestamp string

// Parse parses a timestamp string into a time.Time object.
// The only time this can fail is if Discord changes their timestamp format.
func (t Timestamp) Parse() (time.Time, error) {
	return time.Parse(time.RFC3339, string(t))
}

// RESTError stores error information about a request with a bad response code.
// Message is not always present, there are cases where api calls can fail
// without returning a json message.
type RESTError struct {
	Request      *http.Request
	Response     *http.Response
	ResponseBody []byte

	Message *APIErrorMessage // Message may be nil.
}

func newRestError(req *http.Request, resp *http.Response, body []byte) *RESTError {
	restErr := &RESTError{
		Request:      req,
		Response:     resp,
		ResponseBody: body,
	}

	// Attempt to decode the error and assume no message was provided if it fails
	var msg *APIErrorMessage
	err := json.Unmarshal(body, &msg)
	if err == nil {
		restErr.Message = msg
	}

	return restErr
}

func (r RESTError) Error() string {
	return fmt.Sprintf("HTTP %s, %s", r.Response.Status, r.ResponseBody)
}

// IDSlice Is a slice of snowflake id's that properly marshals and unmarshals the way discord expects them to
// They unmarshal from string arrays and marshals back to string arrays
type IDSlice []int64

func (ids *IDSlice) UnmarshalJSON(data []byte) error {
	if len(data) < 3 {
		return nil
	}

	// Split and strip away "[" "]"
	split := strings.Split(string(data[1:len(data)-1]), ",")
	*ids = make([]int64, 0, len(split))
	for _, s := range split {
		s = strings.TrimSpace(s)
		if len(s) < 3 {
			// Empty or invalid
			continue
		}

		// Strip away quotes and parse
		parsed, err := strconv.ParseInt(s[1:len(s)-1], 10, 64)
		if err != nil {
			return err
		}

		*ids = append(*ids, parsed)
	}

	return nil
}

func (ids IDSlice) MarshalJSON() ([]byte, error) {
	// Capacity:
	// 2 brackets
	// each id is:
	//    18 characters currently, but 1 extra added for the future,
	//    1 comma
	//    2 quotes
	if len(ids) < 1 {
		return []byte("[]"), nil
	}

	outPut := make([]byte, 1, 2+(len(ids)*22))
	outPut[0] = '['

	for i, id := range ids {
		if i != 0 {
			outPut = append(outPut, '"', ',', '"')
		} else {
			outPut = append(outPut, '"')
		}
		outPut = append(outPut, []byte(strconv.FormatInt(id, 10))...)
	}

	outPut = append(outPut, '"', ']')
	return outPut, nil
}

type EmojiName struct {
	string
}

func (emoji EmojiName) String() string {
	if codepoint, ok := emojiCodeMap[emoji.string]; ok {
		emoji.string = codepoint
	}
	// Discord does not accept the emoji qualifier character.
	return strings.Replace(emoji.string, "\uFE0F", "", 1)
}
<|endoftext|>


<commit_before>package vsolver

type ProjectName string

type ProjectAtom struct {
	Name    ProjectName
	Version Version
}

var emptyProjectAtom ProjectAtom

type ProjectDep struct {
	Name       ProjectName
	Constraint Constraint
}

type Dependency struct {
	Depender ProjectAtom
	Dep      ProjectDep
}

// ProjectInfo holds the spec and lock information for a given ProjectAtom
type ProjectInfo struct {
	pa ProjectAtom
	Manifest
	Lock
}

// LockedProject is a single project entry from a lock file. It expresses the
// project's name, one or both of version and underlying revision, the URI for
// accessing it, and the path at which it should be placed within a vendor
// directory.
//
// TODO note that sometime soon, we also plan to allow pkgs. this'll change
type LockedProject struct {
	n         ProjectName
	v         UnpairedVersion
	r         Revision
	path, uri string
}

// NewLockedProject creates a new LockedProject struct with a given name,
// version, upstream repository URI, and on-disk path at which the project is to
// be checked out under a vendor directory.
//
// Note that passing a nil version will cause a panic. This is a correctness
// measure to ensure that the solver is never exposed to a version-less lock
// entry. Such a case would be meaningless - the solver would have no choice but
// to simply dismiss that project. By creating a hard failure case via panic
// instead, we are trying to avoid inflicting the resulting pain on the user by
// instead forcing a decision on the Analyzer implementation.
func NewLockedProject(n ProjectName, v Version, uri, path string) LockedProject {
	if v == nil {
		panic("must provide a non-nil version to create a LockedProject")
	}

	lp := LockedProject{
		n:    n,
		uri:  uri,
		path: path,
	}

	switch tv := v.(type) {
	case Revision:
		lp.r = tv
	case branchVersion:
		lp.v = tv
	case semVersion:
		lp.v = tv
	case plainVersion:
		lp.v = tv
	case versionPair:
		lp.r = tv.r
		lp.v = tv.v
	}

	return lp
}

// Name returns the name of the locked project.
func (lp LockedProject) Name() ProjectName {
	return lp.n
}

// Version assembles together whatever version and/or revision data is
// available into a single Version.
func (lp LockedProject) Version() Version {
	if lp.r == "" {
		return lp.v
	}

	if lp.v == nil {
		return lp.r
	}

	return lp.v.Is(lp.r)
}

// URI returns the upstream URI of the locked project.
func (lp LockedProject) URI() string {
	return lp.uri
}

// Path returns the path relative to the vendor directory to which the locked
// project should be checked out.
func (lp LockedProject) Path() string {
	return lp.path
}

func (lp LockedProject) toAtom() ProjectAtom {
	pa := ProjectAtom{
		Name: lp.n,
	}

	if lp.v == nil {
		pa.Version = lp.r
	} else if lp.r != "" {
		pa.Version = lp.v.Is(lp.r)
	} else {
		pa.Version = lp.v
	}

	return pa
}

// Manifest represents the data from a manifest file (or however the
// implementing tool chooses to store it) at a particular version that is
// relevant to the satisfiability solving process:
//
// - A list of dependencies: project name, and a constraint
// - A list of development-time dependencies (e.g. for testing - only
// the root project's are incorporated)
//
// Finding a solution that satisfies the constraints expressed by all of these
// dependencies (and those from all other projects, transitively), is what the
// solver does.
//
// Note that vsolver does perform static analysis on all projects' codebases;
// if dependencies it finds through that analysis are missing from what the
// Manifest lists, it is considered an error that will eliminate that version
// from consideration in the solving algorithm.
type Manifest interface {
	Name() ProjectName
	GetDependencies() []ProjectDep
	GetDevDependencies() []ProjectDep
}

// Lock represents data from a lock file (or however the implementing tool
// chooses to store it) at a particular version that is relevant to the
// satisfiability solving process.
//
// In general, the information produced by vsolver on finding a successful
// solution is all that would be necessary to constitute a lock file, though
// tools can include whatever other information they want in their storage.
type Lock interface {
	// Indicates the version of the solver used to generate this lock data
	//SolverVersion() string

	// The hash of inputs to vsolver that resulted in this lock data
	InputHash() []byte

	// Projects returns the list of LockedProjects contained in the lock data.
	Projects() []LockedProject
}

// SimpleLock is a helper for tools to simply enumerate lock data when they know
// that no hash, or other complex information, is available.
type SimpleLock []LockedProject

var _ Lock = SimpleLock{}

// InputHash always returns an empty string for SimpleLock. This makes it useless
// as a stable lock to be written to disk, but still useful for some ephemeral
// purposes.
func (SimpleLock) InputHash() []byte {
	return nil
}

// Projects returns the entire contents of the SimpleLock.
func (l SimpleLock) Projects() []LockedProject {
	return l
}

// SimpleManifest is a helper for tools to enumerate manifest data. It's
// intended for ephemeral manifests, such as those created by Analyzers on the
// fly.
type SimpleManifest struct {
	N  ProjectName
	P  []ProjectDep
	DP []ProjectDep
}

var _ Manifest = SimpleManifest{}

// Name returns the name of the project described by the manifest.
func (m SimpleManifest) Name() ProjectName {
	return m.N
}

// GetDependencies returns the project's dependencies.
func (m SimpleManifest) GetDependencies() []ProjectDep {
	return m.P
}

// GetDependencies returns the project's test dependencies.
func (m SimpleManifest) GetDevDependencies() []ProjectDep {
	return m.DP
}
<commit_msg>Maybe the new struct<commit_after>package vsolver

type ProjectIdentifier struct {
	Name       ProjectName
	NetworkURI string
}

type ProjectName string

type ProjectAtom struct {
	Name    ProjectName // TODO to ProjectIdentifier
	Version Version
}

var emptyProjectAtom ProjectAtom

type ProjectDep struct {
	Name       ProjectName // TODO to ProjectIdentifier
	Constraint Constraint
}

type Dependency struct {
	Depender ProjectAtom
	Dep      ProjectDep
}

// ProjectInfo holds the spec and lock information for a given ProjectAtom
type ProjectInfo struct {
	pa ProjectAtom
	Manifest
	Lock
}

// LockedProject is a single project entry from a lock file. It expresses the
// project's name, one or both of version and underlying revision, the URI for
// accessing it, and the path at which it should be placed within a vendor
// directory.
//
// TODO note that sometime soon, we also plan to allow pkgs. this'll change
type LockedProject struct {
	n         ProjectName
	v         UnpairedVersion
	r         Revision
	path, uri string
}

// NewLockedProject creates a new LockedProject struct with a given name,
// version, upstream repository URI, and on-disk path at which the project is to
// be checked out under a vendor directory.
//
// Note that passing a nil version will cause a panic. This is a correctness
// measure to ensure that the solver is never exposed to a version-less lock
// entry. Such a case would be meaningless - the solver would have no choice but
// to simply dismiss that project. By creating a hard failure case via panic
// instead, we are trying to avoid inflicting the resulting pain on the user by
// instead forcing a decision on the Analyzer implementation.
func NewLockedProject(n ProjectName, v Version, uri, path string) LockedProject {
	if v == nil {
		panic("must provide a non-nil version to create a LockedProject")
	}

	lp := LockedProject{
		n:    n,
		uri:  uri,
		path: path,
	}

	switch tv := v.(type) {
	case Revision:
		lp.r = tv
	case branchVersion:
		lp.v = tv
	case semVersion:
		lp.v = tv
	case plainVersion:
		lp.v = tv
	case versionPair:
		lp.r = tv.r
		lp.v = tv.v
	}

	return lp
}

// Name returns the name of the locked project.
func (lp LockedProject) Name() ProjectName {
	return lp.n
}

// Version assembles together whatever version and/or revision data is
// available into a single Version.
func (lp LockedProject) Version() Version {
	if lp.r == "" {
		return lp.v
	}

	if lp.v == nil {
		return lp.r
	}

	return lp.v.Is(lp.r)
}

// URI returns the upstream URI of the locked project.
func (lp LockedProject) URI() string {
	return lp.uri
}

// Path returns the path relative to the vendor directory to which the locked
// project should be checked out.
func (lp LockedProject) Path() string {
	return lp.path
}

func (lp LockedProject) toAtom() ProjectAtom {
	pa := ProjectAtom{
		Name: lp.n,
	}

	if lp.v == nil {
		pa.Version = lp.r
	} else if lp.r != "" {
		pa.Version = lp.v.Is(lp.r)
	} else {
		pa.Version = lp.v
	}

	return pa
}

// Manifest represents the data from a manifest file (or however the
// implementing tool chooses to store it) at a particular version that is
// relevant to the satisfiability solving process:
//
// - A list of dependencies: project name, and a constraint
// - A list of development-time dependencies (e.g. for testing - only
// the root project's are incorporated)
//
// Finding a solution that satisfies the constraints expressed by all of these
// dependencies (and those from all other projects, transitively), is what the
// solver does.
//
// Note that vsolver does perform static analysis on all projects' codebases;
// if dependencies it finds through that analysis are missing from what the
// Manifest lists, it is considered an error that will eliminate that version
// from consideration in the solving algorithm.
type Manifest interface {
	Name() ProjectName
	GetDependencies() []ProjectDep
	GetDevDependencies() []ProjectDep
}

// Lock represents data from a lock file (or however the implementing tool
// chooses to store it) at a particular version that is relevant to the
// satisfiability solving process.
//
// In general, the information produced by vsolver on finding a successful
// solution is all that would be necessary to constitute a lock file, though
// tools can include whatever other information they want in their storage.
type Lock interface {
	// Indicates the version of the solver used to generate this lock data
	//SolverVersion() string

	// The hash of inputs to vsolver that resulted in this lock data
	InputHash() []byte

	// Projects returns the list of LockedProjects contained in the lock data.
	Projects() []LockedProject
}

// SimpleLock is a helper for tools to simply enumerate lock data when they know
// that no hash, or other complex information, is available.
type SimpleLock []LockedProject

var _ Lock = SimpleLock{}

// InputHash always returns an empty string for SimpleLock. This makes it useless
// as a stable lock to be written to disk, but still useful for some ephemeral
// purposes.
func (SimpleLock) InputHash() []byte {
	return nil
}

// Projects returns the entire contents of the SimpleLock.
func (l SimpleLock) Projects() []LockedProject {
	return l
}

// SimpleManifest is a helper for tools to enumerate manifest data. It's
// intended for ephemeral manifests, such as those created by Analyzers on the
// fly.
type SimpleManifest struct {
	N  ProjectName
	P  []ProjectDep
	DP []ProjectDep
}

var _ Manifest = SimpleManifest{}

// Name returns the name of the project described by the manifest.
func (m SimpleManifest) Name() ProjectName {
	return m.N
}

// GetDependencies returns the project's dependencies.
func (m SimpleManifest) GetDependencies() []ProjectDep {
	return m.P
}

// GetDependencies returns the project's test dependencies.
func (m SimpleManifest) GetDevDependencies() []ProjectDep {
	return m.DP
}
<|endoftext|>


<commit_before>package ebpf

//go:generate stringer -output types_string.go -type=MapType,ProgType

// MapType indicates the type map structure
// that will be initialized in the kernel.
type MapType uint32

// All the various map types that can be created
const (
	UnspecifiedMap MapType = iota
	// Hash is a hash map
	Hash
	// Array is an array map
	Array
	// ProgramArray - A program array map is a special kind of array map whose map
	// values contain only file descriptors referring to other eBPF
	// programs.  Thus, both the key_size and value_size must be
	// exactly four bytes.  This map is used in conjunction with the
	// TailCall helper.
	ProgramArray
	// PerfEventArray - A perf event array is used in conjunction with PerfEventRead
	// and PerfEventOutput calls, to read the raw bpf_perf_data from the registers.
	PerfEventArray
	// PerCPUHash - This data structure is useful for people who have high performance
	// network needs and can reconcile adds at the end of some cycle, so that
	// hashes can be lock free without the use of XAdd, which can be costly.
	PerCPUHash
	// PerCPUArray - This data structure is useful for people who have high performance
	// network needs and can reconcile adds at the end of some cycle, so that
	// hashes can be lock free without the use of XAdd, which can be costly.
	// Each CPU gets a copy of this hash, the contents of all of which can be reconciled
	// later.
	PerCPUArray
	// StackTrace - This holds whole user and kernel stack traces, it can be retrieved with
	// GetStackID
	StackTrace
	// CGroupArray - This is a very niche structure used to help SKBInCGroup determine
	// if an skb is from a socket belonging to a specific cgroup
	CGroupArray
	// LRUHash - This allows you to create a small hash structure that will purge the
	// least recently used items rather than thow an error when you run out of memory
	LRUHash
	// LRUCPUHash - This is NOT like PerCPUHash, this structure is shared among the CPUs,
	// it has more to do with including the CPU id with the LRU calculation so that if a
	// particular CPU is using a value over-and-over again, then it will be saved, but if
	// a value is being retrieved a lot but sparsely across CPUs it is not as important, basically
	// giving weight to CPU locality over overall usage.
	LRUCPUHash
	// LPMTrie - This is an implementation of Longest-Prefix-Match Trie structure. It is useful,
	// for storing things like IP addresses which can be bit masked allowing for keys of differing
	// values to refer to the same reference based on their masks. See wikipedia for more details.
	LPMTrie
	// ArrayOfMaps - Each item in the array is another map. The inner map mustn't be a map of maps
	// itself.
	ArrayOfMaps
	// HashOfMaps - Each item in the hash map is another map. The inner map mustn't be a map of maps
	// itself.
	HashOfMaps
)

// hasPerCPUValue returns true if the Map stores a value per CPU.
func (mt MapType) hasPerCPUValue() bool {
	if mt == PerCPUHash || mt == PerCPUArray {
		return true
	}
	return false
}

const (
	_MapCreate = iota
	_MapLookupElem
	_MapUpdateElem
	_MapDeleteElem
	_MapGetNextKey
	_ProgLoad
	_ObjPin
	_ObjGet
	_ProgAttach
	_ProgDetach
	_ProgTestRun
	_ProgGetNextID
	_MapGetNextID
	_ProgGetFDByID
	_MapGetFDByID
	_ObjGetInfoByFD
)

const (
	_Any = iota
	_NoExist
	_Exist
)

// All flags used by eBPF helper functions
const (
	// RecomputeCSUM SKBStoreBytes flags
	RecomputeCSUM = uint64(1)
	// FInvalidateHash SKBStoreBytes flags
	FInvalidateHash = uint64(1 << 1)

	// FHdrFieldMask CSUMReplaceL4 and CSUMReplaceL3 flags.
	// First 4 bits are for passing the header field size.
	FHdrFieldMask = uint64(0xF)

	// FPseudoHdr CSUMReplaceL4 flags
	FPseudoHdr = uint64(1 << 4)
	// FMarkMangled0 CSUMReplaceL4 flags
	FMarkMangled0 = uint64(1 << 5)
	// FMakrEnforce CSUMReplaceL4 flags
	FMakrEnforce = uint64(1 << 6)

	// FIngress CloneRedirect and Redirect flags
	FIngress = uint64(1)

	// FTunInfoIPV6 SKBSetTunnelKey and SKBGetTunnelKey flags
	FTunInfoIPV6 = uint(1)

	// FSkipFieldMask GetStackID flags
	FSkipFieldMask = uint64(0xff)
	// FUserStack GetStackID flags
	FUserStack = uint64(1 << 8)
	// FFastStackCMP GetStackID flags
	FFastStackCMP = uint64(1 << 9)
	// FReuseStackID GetStackID flags
	FReuseStackID = uint64(1 << 10)

	// FZeroCSUMTx SKBSetTunnelKey flag
	FZeroCSUMTX = uint64(1 << 1)
	// FZeroCSUMTx SKBSetTunnelKey flag
	FDontFragment = uint64(1 << 2)

	// FindIndexMask PerfEventOutput and PerfEventRead flags.
	FIndexMask = uint64(0xffffffff)
	// FCurrentCPU PerfEventOutput and PerfEventRead flags.
	FCurrentCPU = FIndexMask

	// FCtxLenMask PerfEventOutput for SKBuff input context.
	FCtxLenMask = uint64(0xfffff << 32)

	// AdjRoomNet Mode for SKBAdjustRoom helper.
	AdjRoomNet = 0
)

// ProgType of the eBPF program
type ProgType uint32

// eBPF program types
const (
	// Unrecognized program type
	Unrecognized ProgType = iota
	// SocketFilter socket or seccomp filter
	SocketFilter
	// Kprobe program
	Kprobe
	// SchedCLS traffic control shaper
	SchedCLS
	// SchedACT routing control shaper
	SchedACT
	// TracePoint program
	TracePoint
	// XDP program
	XDP
	// PerfEvent program
	PerfEvent
	// CGroupSKB program
	CGroupSKB
	// CGroupSock program
	CGroupSock
	// LWTIn program
	LWTIn
	// LWTOut program
	LWTOut
	// LWTXmit program
	LWTXmit
	// SockOps program
	SockOps
	// SkSKB program
	SkSKB
	// CGroupDevice program
	CGroupDevice
	// SkMsg program
	SkMsg
	// RawTracepoint program
	RawTracepoint
	// CGroupSockAddr program
	CGroupSockAddr
	// LWTSeg6Local program
	LWTSeg6Local
	// LircMode2 program
	LircMode2
	// SkReuseport program
	SkReuseport
	// FlowDissector program
	FlowDissector
	// CGroupSysctl program
	CGroupSysctl
	// RawTracepointWritable program
	RawTracepointWritable
	// CGroupSockopt program
	CGroupSockopt
)

// AttachType of the eBPF program, needed to differentiate allowed context accesses in
// some newer program types like CGroupSockAddr. Should be set to AttachNone if not required.
// Will cause invalid argument (EINVAL) at program load time if set incorrectly.
type AttachType uint32

// AttachNone is an alias for AttachCGroupInetIngress for readability reasons
const AttachNone AttachType = 0

const (
	AttachCGroupInetIngress AttachType = iota
	AttachCGroupInetEgress
	AttachCGroupInetSockCreate
	AttachCGroupSockOps
	AttachSkSKBStreamParser
	AttachSkSKBStreamVerdict
	AttachCGroupDevice
	AttachSkMsgVerdict
	AttachCGroupInet4Bind
	AttachCGroupInet6Bind
	AttachCGroupInet4Connect
	AttachCGroupInet6Connect
	AttachCGroupInet4PostBind
	AttachCGroupInet6PostBind
	AttachCGroupUDP4Sendmsg
	AttachCGroupUDP6Sendmsg
	AttachLircMode2
	AttachFlowDissector
	AttachCGroupSysctl
	AttachCGroupUDP4Recvmsg
	AttachCGroupUDP6Recvmsg
	AttachCGroupGetsockopt
	AttachCGroupSetsockopt
)
<commit_msg>remove helper flags<commit_after>package ebpf

//go:generate stringer -output types_string.go -type=MapType,ProgType

// MapType indicates the type map structure
// that will be initialized in the kernel.
type MapType uint32

// All the various map types that can be created
const (
	UnspecifiedMap MapType = iota
	// Hash is a hash map
	Hash
	// Array is an array map
	Array
	// ProgramArray - A program array map is a special kind of array map whose map
	// values contain only file descriptors referring to other eBPF
	// programs.  Thus, both the key_size and value_size must be
	// exactly four bytes.  This map is used in conjunction with the
	// TailCall helper.
	ProgramArray
	// PerfEventArray - A perf event array is used in conjunction with PerfEventRead
	// and PerfEventOutput calls, to read the raw bpf_perf_data from the registers.
	PerfEventArray
	// PerCPUHash - This data structure is useful for people who have high performance
	// network needs and can reconcile adds at the end of some cycle, so that
	// hashes can be lock free without the use of XAdd, which can be costly.
	PerCPUHash
	// PerCPUArray - This data structure is useful for people who have high performance
	// network needs and can reconcile adds at the end of some cycle, so that
	// hashes can be lock free without the use of XAdd, which can be costly.
	// Each CPU gets a copy of this hash, the contents of all of which can be reconciled
	// later.
	PerCPUArray
	// StackTrace - This holds whole user and kernel stack traces, it can be retrieved with
	// GetStackID
	StackTrace
	// CGroupArray - This is a very niche structure used to help SKBInCGroup determine
	// if an skb is from a socket belonging to a specific cgroup
	CGroupArray
	// LRUHash - This allows you to create a small hash structure that will purge the
	// least recently used items rather than thow an error when you run out of memory
	LRUHash
	// LRUCPUHash - This is NOT like PerCPUHash, this structure is shared among the CPUs,
	// it has more to do with including the CPU id with the LRU calculation so that if a
	// particular CPU is using a value over-and-over again, then it will be saved, but if
	// a value is being retrieved a lot but sparsely across CPUs it is not as important, basically
	// giving weight to CPU locality over overall usage.
	LRUCPUHash
	// LPMTrie - This is an implementation of Longest-Prefix-Match Trie structure. It is useful,
	// for storing things like IP addresses which can be bit masked allowing for keys of differing
	// values to refer to the same reference based on their masks. See wikipedia for more details.
	LPMTrie
	// ArrayOfMaps - Each item in the array is another map. The inner map mustn't be a map of maps
	// itself.
	ArrayOfMaps
	// HashOfMaps - Each item in the hash map is another map. The inner map mustn't be a map of maps
	// itself.
	HashOfMaps
)

// hasPerCPUValue returns true if the Map stores a value per CPU.
func (mt MapType) hasPerCPUValue() bool {
	if mt == PerCPUHash || mt == PerCPUArray {
		return true
	}
	return false
}

const (
	_MapCreate = iota
	_MapLookupElem
	_MapUpdateElem
	_MapDeleteElem
	_MapGetNextKey
	_ProgLoad
	_ObjPin
	_ObjGet
	_ProgAttach
	_ProgDetach
	_ProgTestRun
	_ProgGetNextID
	_MapGetNextID
	_ProgGetFDByID
	_MapGetFDByID
	_ObjGetInfoByFD
)

const (
	_Any = iota
	_NoExist
	_Exist
)

// ProgType of the eBPF program
type ProgType uint32

// eBPF program types
const (
	// Unrecognized program type
	Unrecognized ProgType = iota
	// SocketFilter socket or seccomp filter
	SocketFilter
	// Kprobe program
	Kprobe
	// SchedCLS traffic control shaper
	SchedCLS
	// SchedACT routing control shaper
	SchedACT
	// TracePoint program
	TracePoint
	// XDP program
	XDP
	// PerfEvent program
	PerfEvent
	// CGroupSKB program
	CGroupSKB
	// CGroupSock program
	CGroupSock
	// LWTIn program
	LWTIn
	// LWTOut program
	LWTOut
	// LWTXmit program
	LWTXmit
	// SockOps program
	SockOps
	// SkSKB program
	SkSKB
	// CGroupDevice program
	CGroupDevice
	// SkMsg program
	SkMsg
	// RawTracepoint program
	RawTracepoint
	// CGroupSockAddr program
	CGroupSockAddr
	// LWTSeg6Local program
	LWTSeg6Local
	// LircMode2 program
	LircMode2
	// SkReuseport program
	SkReuseport
	// FlowDissector program
	FlowDissector
	// CGroupSysctl program
	CGroupSysctl
	// RawTracepointWritable program
	RawTracepointWritable
	// CGroupSockopt program
	CGroupSockopt
)

// AttachType of the eBPF program, needed to differentiate allowed context accesses in
// some newer program types like CGroupSockAddr. Should be set to AttachNone if not required.
// Will cause invalid argument (EINVAL) at program load time if set incorrectly.
type AttachType uint32

// AttachNone is an alias for AttachCGroupInetIngress for readability reasons
const AttachNone AttachType = 0

const (
	AttachCGroupInetIngress AttachType = iota
	AttachCGroupInetEgress
	AttachCGroupInetSockCreate
	AttachCGroupSockOps
	AttachSkSKBStreamParser
	AttachSkSKBStreamVerdict
	AttachCGroupDevice
	AttachSkMsgVerdict
	AttachCGroupInet4Bind
	AttachCGroupInet6Bind
	AttachCGroupInet4Connect
	AttachCGroupInet6Connect
	AttachCGroupInet4PostBind
	AttachCGroupInet6PostBind
	AttachCGroupUDP4Sendmsg
	AttachCGroupUDP6Sendmsg
	AttachLircMode2
	AttachFlowDissector
	AttachCGroupSysctl
	AttachCGroupUDP4Recvmsg
	AttachCGroupUDP6Recvmsg
	AttachCGroupGetsockopt
	AttachCGroupSetsockopt
)
<|endoftext|>