<commit_before>package main

import (
  "database/sql"
  "encoding/json"
  "errors"
  "fmt"
  "net/http"
  "net/url"
  "os"
  "strconv"
  _ "github.com/lib/pq"
  "github.com/rcrowley/go-tigertonic"
  "github.com/rkbodenner/meeple_mover/record"
  "github.com/rkbodenner/parallel_universe/game"
  "github.com/rkbodenner/parallel_universe/session"
)

var players = make([]*game.Player, 0)
var playerIndex = make(map[uint64]*game.Player)

func initPlayerData(db *sql.DB) error {
  rows, err := db.Query("SELECT * FROM players")
  if nil != err {
    return err
  }
  defer rows.Close()
  for rows.Next() {
    var name string
    var id int
    if err := rows.Scan(&id, &name); err != nil {
      return err
    }
    players = append(players, &game.Player{id, name})
  }

  for _,player := range players {
    playerIndex[(uint64)(player.Id)] = player
  }

  fmt.Printf("Loaded %d players from DB\n", len(players))
  return nil
}

var games []*game.Game
var gameIndex = make(map[uint64]*game.Game)

func initGameData(db *sql.DB) error {
  gameRecords := &record.GameRecordList{}
  err := gameRecords.FindAll(db)
  if nil != err {
    return err
  }
  games = gameRecords.List()

  for _, game := range games {
    gameIndex[(uint64)(game.Id)] = game
  }

  fmt.Printf("Loaded %d games from DB\n", len(games))
  return nil
}

var sessions []*session.Session
var sessionIndex = make(map[uint64]*session.Session)

func initSessionData(db *sql.DB) error {
  records := &record.SessionRecordList{}
  err := records.FindAll(db)
  if nil != err {
    return err
  }
  sessions = records.List()

  // Update global cache of sessions
  for _, s := range sessions {
    sessionIndex[(uint64)(s.Id)] = s
  }

  fmt.Printf("Loaded %d sessions from DB\n", len(sessions))
  return nil
}

type CollectionHandler struct{}
func (h CollectionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  err := json.NewEncoder(w).Encode(games)
  if ( nil != err ) {
    fmt.Fprintln(w, err)
  }
}

type GameHandler struct{}
func (h GameHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  id_str := r.URL.Query().Get("id")
  id, err := strconv.ParseUint(id_str, 10, 64)
  if nil != err {
    http.Error(w, "Not found", http.StatusNotFound)
    return
  }

  game, ok := gameIndex[id]
  if ok {
    err := json.NewEncoder(w).Encode(game)
    if ( nil != err ) {
      http.Error(w, "Error", http.StatusInternalServerError)
    }
  } else {
    http.Error(w, "Not found", http.StatusNotFound)
  }
}

type PlayersHandler struct{}
func (h PlayersHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  err := json.NewEncoder(w).Encode(players)
  if ( nil != err ) {
    fmt.Fprintln(w, err)
  }
}

type PlayerHandler struct{}
func (h PlayerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  player_id_str := r.URL.Query().Get("player_id")
  player_id, err := strconv.ParseUint(player_id_str, 10, 64)
  if nil != err {
    http.Error(w, "Not found", http.StatusNotFound)
    return
  }

  player, ok := playerIndex[player_id]
  if ok {
    err = json.NewEncoder(w).Encode(player)
    if ( nil != err ) {
      http.Error(w, "Error", http.StatusInternalServerError)
    }
  } else {
    http.Error(w, "Not found", http.StatusNotFound)
  }
}

type SessionsHandler struct{}
func (h SessionsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  err := json.NewEncoder(w).Encode(sessions)
  if ( nil != err ) {
    http.Error(w, "Error", http.StatusInternalServerError)
  }
}

type SessionCreateHandler struct {
  db *sql.DB
}
type SessionCreateHash struct {
  StartedDate string `json:"started_date"`
  Game string `json:"game"`
  Players []string `json:"players"`
}
type SessionCreateRequest struct {
  Session SessionCreateHash `json:"session"`
}

func fetchPlayersById(db *sql.DB, playerIds []int) ([]*game.Player, error) {
  players := make([]*game.Player, len(playerIds))

  for i, playerId := range playerIds {
    var name string
    err := db.QueryRow("SELECT name FROM players WHERE id = $1", playerId).Scan(&name)
    if err != nil {
       return players, err
    }
    players[i] = &game.Player{playerId, name}
  }

  return players, nil
}

// Persist a new session
func (handler SessionCreateHandler) marshalFunc() (func(*url.URL, http.Header, *SessionCreateRequest) (int, http.Header, *session.Session, error)) {
  return func(u *url.URL, h http.Header, rq *SessionCreateRequest) (int, http.Header, *session.Session, error) {
    var err error

    var game_id uint64
    game_id, err = strconv.ParseUint(rq.Session.Game, 10, 64)
    if nil != err {
      return http.StatusBadRequest, nil, nil, errors.New("Expected integer game ID")
    }

    player_ids := make([]int, 0)
    for _, player_id_str := range rq.Session.Players {
      player_id, err := strconv.ParseInt(player_id_str, 10, 32)
      if nil != err {
        return http.StatusBadRequest, nil, nil, errors.New("Expected integer player ID")
      }
      player_ids = append(player_ids, (int)(player_id))
    }

    var players []*game.Player
    players, err = fetchPlayersById(handler.db, player_ids)
    if nil != err {
      return http.StatusInternalServerError, nil, nil, err
    }
    fmt.Printf("Found %d matching players\n", len(players))

    var _session *session.Session
    _session, err = session.NewSession(gameIndex[game_id], players)
    if nil != err {
      return http.StatusInternalServerError, nil, nil, err
    }
    _session.StepAllPlayers()

    err = record.NewSessionRecord(_session).Create(handler.db)
    if nil != err {
      return http.StatusInternalServerError, nil, nil, err
    }

    sessions = append(sessions, _session)
    sessionIndex[(uint64)(_session.Id)] = _session

    _session.Print()

    return http.StatusCreated, nil, _session, nil
  }
}

type SessionHandler struct{}
func (h SessionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  id_str := r.URL.Query().Get("session_id")
  id, err := strconv.ParseUint(id_str, 10, 64)
  if nil != err {
    http.Error(w, "Not found", http.StatusNotFound)
    return
  }

  session, ok := sessionIndex[id]
  if ok {
    err := json.NewEncoder(w).Encode(session)
    if ( nil != err ) {
      http.Error(w, "Error", http.StatusInternalServerError)
    }
  } else {
    http.Error(w, "Not found", http.StatusNotFound)
  }
}

type StepHandler struct{
  db *sql.DB
}
func (h StepHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  session_id_str := r.URL.Query().Get("session_id")
  session_id, err := strconv.ParseUint(session_id_str, 10, 64)
  if nil != err {
    http.Error(w, "Session not found", http.StatusNotFound)
    return
  }
  session,ok := sessionIndex[session_id]
  if !ok {
    http.Error(w, "Session not found", http.StatusNotFound)
    return
  }

  player_id_str := r.URL.Query().Get("player_id")
  player_id, err := strconv.ParseUint(player_id_str, 10, 64)
  if nil != err {
    http.Error(w, "Player not found", http.StatusNotFound)
    return
  }
  player, ok := playerIndex[player_id]
  if !ok {
    http.Error(w, "Player not found", http.StatusNotFound)
    return
  }

  step_desc,err := url.QueryUnescape(r.URL.Query().Get("step_desc"))
  for _,step := range session.SetupSteps {
    if ( step.Rule.Description == step_desc && step.CanBeOwnedBy(player) ) {
      step.Finish()  // FIXME. Should look in request data to see what to change.

      rec := &record.SetupStepRecord{Step: step, SessionId: (int)(session.Id)}
      err := rec.Update(h.db)
      if nil != err {
        // FIXME: Revert to the previous state if we can't save.
        http.Error(w, fmt.Sprintf("Error saving update to step: %s", err), http.StatusInternalServerError)
        return
      }

      nextStep := session.Step(player)

      if nextStep != step && nil != nextStep {
        lastAssignmentRec := &record.SetupStepAssignmentRecord{session, player, step.Rule}
        err = lastAssignmentRec.Delete(h.db)
        if nil != err {
          http.Error(w, fmt.Sprintf("Error removing assignment of last step: %s", err), http.StatusInternalServerError)
          return
        }
        nextAssignmentRec := &record.SetupStepAssignmentRecord{session, player, nextStep.Rule}
        err = nextAssignmentRec.Create(h.db)
        if nil != err {
          http.Error(w, fmt.Sprintf("Error creating assignment of next step: %s", err), http.StatusInternalServerError)
          return
        }
      }

      session.Print()
      return
    }
  }
  http.Error(w, "Step not found", http.StatusNotFound)
}

func main() {
  databaseName := "meeple_mover"
  if databaseNameOption := os.Getenv("MEEPLE_MOVER_DB_NAME"); databaseNameOption != "" {
    databaseName = databaseNameOption
  }
  connectString := fmt.Sprintf("user=ralph dbname=%s sslmode=disable", databaseName)
  connectMsg := fmt.Sprintf("Connected to database %s", databaseName)

  if herokuConnectString := os.Getenv("HEROKU_POSTGRESQL_SILVER_URL"); herokuConnectString != "" {
    connectString = herokuConnectString
    connectMsg = fmt.Sprintf("Connected to Heroku database")
  }

  db, err := sql.Open("postgres", connectString)
  if err != nil {
    fmt.Print(err)
  } else {
    fmt.Println(connectMsg)
  }
  defer db.Close()

  err = initPlayerData(db)
  if err != nil {
    fmt.Printf("Error initializing players: %s\n", err)
  }
  err = initGameData(db)
  if err != nil {
    fmt.Printf("Error initializing games: %s\n", err)
  }
  err = initSessionData(db)
  if err != nil {
    fmt.Printf("Error initializing sessions: %s\n", err)
  }

  var origin string
  origin = os.Getenv("MEEPLE_MOVER_ORIGIN_URL")
  if "" == origin {
    origin = "http://localhost:8000"
  }
  cors := tigertonic.NewCORSBuilder().AddAllowedOrigins(origin).AddAllowedHeaders("Content-Type")
  fmt.Printf("Allowed CORS origin %s\n", origin)

  mux := tigertonic.NewTrieServeMux()
  mux.Handle("GET", "/games", cors.Build(CollectionHandler{}))
  mux.Handle("GET", "/games/{id}", cors.Build(GameHandler{}))
  mux.Handle("GET", "/players", cors.Build(PlayersHandler{}))
  mux.Handle("GET", "/players/{player_id}", cors.Build(PlayerHandler{}))
  mux.Handle("GET", "/sessions", cors.Build(SessionsHandler{}))
  mux.Handle("POST", "/sessions", cors.Build(tigertonic.Marshaled(SessionCreateHandler{db}.marshalFunc())))
  mux.Handle("GET", "/sessions/{session_id}", cors.Build(SessionHandler{}))
  mux.Handle("PUT", "/sessions/{session_id}/players/{player_id}/steps/{step_desc}", cors.Build(StepHandler{db}))

  var port string
  port = os.Getenv("PORT")
  if "" == port {
    port = "8080"
  }

  http.ListenAndServe(fmt.Sprintf(":%s", port), mux)
}
<commit_msg>Endpoint for creating players<commit_after>package main

import (
  "database/sql"
  "encoding/json"
  "errors"
  "fmt"
  "net/http"
  "net/url"
  "os"
  "strconv"
  _ "github.com/lib/pq"
  "github.com/rcrowley/go-tigertonic"
  "github.com/rkbodenner/meeple_mover/record"
  "github.com/rkbodenner/parallel_universe/game"
  "github.com/rkbodenner/parallel_universe/session"
)

var players = make([]*game.Player, 0)
var playerIndex = make(map[uint64]*game.Player)

func initPlayerData(db *sql.DB) error {
  rows, err := db.Query("SELECT * FROM players")
  if nil != err {
    return err
  }
  defer rows.Close()
  for rows.Next() {
    var name string
    var id int
    if err := rows.Scan(&id, &name); err != nil {
      return err
    }
    players = append(players, &game.Player{id, name})
  }

  for _,player := range players {
    playerIndex[(uint64)(player.Id)] = player
  }

  fmt.Printf("Loaded %d players from DB\n", len(players))
  return nil
}

var games []*game.Game
var gameIndex = make(map[uint64]*game.Game)

func initGameData(db *sql.DB) error {
  gameRecords := &record.GameRecordList{}
  err := gameRecords.FindAll(db)
  if nil != err {
    return err
  }
  games = gameRecords.List()

  for _, game := range games {
    gameIndex[(uint64)(game.Id)] = game
  }

  fmt.Printf("Loaded %d games from DB\n", len(games))
  return nil
}

var sessions []*session.Session
var sessionIndex = make(map[uint64]*session.Session)

func initSessionData(db *sql.DB) error {
  records := &record.SessionRecordList{}
  err := records.FindAll(db)
  if nil != err {
    return err
  }
  sessions = records.List()

  // Update global cache of sessions
  for _, s := range sessions {
    sessionIndex[(uint64)(s.Id)] = s
  }

  fmt.Printf("Loaded %d sessions from DB\n", len(sessions))
  return nil
}

type CollectionHandler struct{}
func (h CollectionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  err := json.NewEncoder(w).Encode(games)
  if ( nil != err ) {
    fmt.Fprintln(w, err)
  }
}

type GameHandler struct{}
func (h GameHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  id_str := r.URL.Query().Get("id")
  id, err := strconv.ParseUint(id_str, 10, 64)
  if nil != err {
    http.Error(w, "Not found", http.StatusNotFound)
    return
  }

  game, ok := gameIndex[id]
  if ok {
    err := json.NewEncoder(w).Encode(game)
    if ( nil != err ) {
      http.Error(w, "Error", http.StatusInternalServerError)
    }
  } else {
    http.Error(w, "Not found", http.StatusNotFound)
  }
}

type PlayersHandler struct{}
func (h PlayersHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  err := json.NewEncoder(w).Encode(players)
  if ( nil != err ) {
    fmt.Fprintln(w, err)
  }
}

type PlayerHandler struct{}
func (h PlayerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  player_id_str := r.URL.Query().Get("player_id")
  player_id, err := strconv.ParseUint(player_id_str, 10, 64)
  if nil != err {
    http.Error(w, "Not found", http.StatusNotFound)
    return
  }

  player, ok := playerIndex[player_id]
  if ok {
    err = json.NewEncoder(w).Encode(player)
    if ( nil != err ) {
      http.Error(w, "Error", http.StatusInternalServerError)
    }
  } else {
    http.Error(w, "Not found", http.StatusNotFound)
  }
}

type PlayerCreateRequest struct {
  Player game.Player `json:"player"`
}

type PlayerCreateHandler struct{
  db *sql.DB
}
func (handler PlayerCreateHandler) marshalFunc() (func(*url.URL, http.Header, *PlayerCreateRequest) (int, http.Header, *game.Player, error)) {
  return func(u *url.URL, h http.Header, rq *PlayerCreateRequest) (int, http.Header, *game.Player, error) {
    err := handler.db.QueryRow("INSERT INTO players(id, name) VALUES(default, $1) RETURNING id", rq.Player.Name).Scan(&rq.Player.Id)
    if nil != err {
      return http.StatusInternalServerError, nil, nil, errors.New("Could not create player in database")
    }

    fmt.Printf("Created player #%d: %s\n", rq.Player.Id, rq.Player.Name)
    return http.StatusCreated, nil, &rq.Player, nil
  }
}

type SessionsHandler struct{}
func (h SessionsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  err := json.NewEncoder(w).Encode(sessions)
  if ( nil != err ) {
    http.Error(w, "Error", http.StatusInternalServerError)
  }
}

type SessionCreateHandler struct {
  db *sql.DB
}
type SessionCreateHash struct {
  StartedDate string `json:"started_date"`
  Game string `json:"game"`
  Players []string `json:"players"`
}
type SessionCreateRequest struct {
  Session SessionCreateHash `json:"session"`
}

func fetchPlayersById(db *sql.DB, playerIds []int) ([]*game.Player, error) {
  players := make([]*game.Player, len(playerIds))

  for i, playerId := range playerIds {
    var name string
    err := db.QueryRow("SELECT name FROM players WHERE id = $1", playerId).Scan(&name)
    if err != nil {
       return players, err
    }
    players[i] = &game.Player{playerId, name}
  }

  return players, nil
}

// Persist a new session
func (handler SessionCreateHandler) marshalFunc() (func(*url.URL, http.Header, *SessionCreateRequest) (int, http.Header, *session.Session, error)) {
  return func(u *url.URL, h http.Header, rq *SessionCreateRequest) (int, http.Header, *session.Session, error) {
    var err error

    var game_id uint64
    game_id, err = strconv.ParseUint(rq.Session.Game, 10, 64)
    if nil != err {
      return http.StatusBadRequest, nil, nil, errors.New("Expected integer game ID")
    }

    player_ids := make([]int, 0)
    for _, player_id_str := range rq.Session.Players {
      player_id, err := strconv.ParseInt(player_id_str, 10, 32)
      if nil != err {
        return http.StatusBadRequest, nil, nil, errors.New("Expected integer player ID")
      }
      player_ids = append(player_ids, (int)(player_id))
    }

    var players []*game.Player
    players, err = fetchPlayersById(handler.db, player_ids)
    if nil != err {
      return http.StatusInternalServerError, nil, nil, err
    }
    fmt.Printf("Found %d matching players\n", len(players))

    var _session *session.Session
    _session, err = session.NewSession(gameIndex[game_id], players)
    if nil != err {
      return http.StatusInternalServerError, nil, nil, err
    }
    _session.StepAllPlayers()

    err = record.NewSessionRecord(_session).Create(handler.db)
    if nil != err {
      return http.StatusInternalServerError, nil, nil, err
    }

    sessions = append(sessions, _session)
    sessionIndex[(uint64)(_session.Id)] = _session

    _session.Print()

    return http.StatusCreated, nil, _session, nil
  }
}

type SessionHandler struct{}
func (h SessionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  id_str := r.URL.Query().Get("session_id")
  id, err := strconv.ParseUint(id_str, 10, 64)
  if nil != err {
    http.Error(w, "Not found", http.StatusNotFound)
    return
  }

  session, ok := sessionIndex[id]
  if ok {
    err := json.NewEncoder(w).Encode(session)
    if ( nil != err ) {
      http.Error(w, "Error", http.StatusInternalServerError)
    }
  } else {
    http.Error(w, "Not found", http.StatusNotFound)
  }
}

type StepHandler struct{
  db *sql.DB
}
func (h StepHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  session_id_str := r.URL.Query().Get("session_id")
  session_id, err := strconv.ParseUint(session_id_str, 10, 64)
  if nil != err {
    http.Error(w, "Session not found", http.StatusNotFound)
    return
  }
  session,ok := sessionIndex[session_id]
  if !ok {
    http.Error(w, "Session not found", http.StatusNotFound)
    return
  }

  player_id_str := r.URL.Query().Get("player_id")
  player_id, err := strconv.ParseUint(player_id_str, 10, 64)
  if nil != err {
    http.Error(w, "Player not found", http.StatusNotFound)
    return
  }
  player, ok := playerIndex[player_id]
  if !ok {
    http.Error(w, "Player not found", http.StatusNotFound)
    return
  }

  step_desc,err := url.QueryUnescape(r.URL.Query().Get("step_desc"))
  for _,step := range session.SetupSteps {
    if ( step.Rule.Description == step_desc && step.CanBeOwnedBy(player) ) {
      step.Finish()  // FIXME. Should look in request data to see what to change.

      rec := &record.SetupStepRecord{Step: step, SessionId: (int)(session.Id)}
      err := rec.Update(h.db)
      if nil != err {
        // FIXME: Revert to the previous state if we can't save.
        http.Error(w, fmt.Sprintf("Error saving update to step: %s", err), http.StatusInternalServerError)
        return
      }

      nextStep := session.Step(player)

      if nextStep != step && nil != nextStep {
        lastAssignmentRec := &record.SetupStepAssignmentRecord{session, player, step.Rule}
        err = lastAssignmentRec.Delete(h.db)
        if nil != err {
          http.Error(w, fmt.Sprintf("Error removing assignment of last step: %s", err), http.StatusInternalServerError)
          return
        }
        nextAssignmentRec := &record.SetupStepAssignmentRecord{session, player, nextStep.Rule}
        err = nextAssignmentRec.Create(h.db)
        if nil != err {
          http.Error(w, fmt.Sprintf("Error creating assignment of next step: %s", err), http.StatusInternalServerError)
          return
        }
      }

      session.Print()
      return
    }
  }
  http.Error(w, "Step not found", http.StatusNotFound)
}

func main() {
  databaseName := "meeple_mover"
  if databaseNameOption := os.Getenv("MEEPLE_MOVER_DB_NAME"); databaseNameOption != "" {
    databaseName = databaseNameOption
  }
  connectString := fmt.Sprintf("user=ralph dbname=%s sslmode=disable", databaseName)
  connectMsg := fmt.Sprintf("Connected to database %s", databaseName)

  if herokuConnectString := os.Getenv("HEROKU_POSTGRESQL_SILVER_URL"); herokuConnectString != "" {
    connectString = herokuConnectString
    connectMsg = fmt.Sprintf("Connected to Heroku database")
  }

  db, err := sql.Open("postgres", connectString)
  if err != nil {
    fmt.Print(err)
  } else {
    fmt.Println(connectMsg)
  }
  defer db.Close()

  err = initPlayerData(db)
  if err != nil {
    fmt.Printf("Error initializing players: %s\n", err)
  }
  err = initGameData(db)
  if err != nil {
    fmt.Printf("Error initializing games: %s\n", err)
  }
  err = initSessionData(db)
  if err != nil {
    fmt.Printf("Error initializing sessions: %s\n", err)
  }

  var origin string
  origin = os.Getenv("MEEPLE_MOVER_ORIGIN_URL")
  if "" == origin {
    origin = "http://localhost:8000"
  }
  cors := tigertonic.NewCORSBuilder().AddAllowedOrigins(origin).AddAllowedHeaders("Content-Type")
  fmt.Printf("Allowed CORS origin %s\n", origin)

  mux := tigertonic.NewTrieServeMux()
  mux.Handle("GET", "/games", cors.Build(CollectionHandler{}))
  mux.Handle("GET", "/games/{id}", cors.Build(GameHandler{}))
  mux.Handle("GET", "/players", cors.Build(PlayersHandler{}))
  mux.Handle("GET", "/players/{player_id}", cors.Build(PlayerHandler{}))
  mux.Handle("POST", "/players", cors.Build(tigertonic.Marshaled(PlayerCreateHandler{db}.marshalFunc())))
  mux.Handle("GET", "/sessions", cors.Build(SessionsHandler{}))
  mux.Handle("POST", "/sessions", cors.Build(tigertonic.Marshaled(SessionCreateHandler{db}.marshalFunc())))
  mux.Handle("GET", "/sessions/{session_id}", cors.Build(SessionHandler{}))
  mux.Handle("PUT", "/sessions/{session_id}/players/{player_id}/steps/{step_desc}", cors.Build(StepHandler{db}))

  var port string
  port = os.Getenv("PORT")
  if "" == port {
    port = "8080"
  }

  http.ListenAndServe(fmt.Sprintf(":%s", port), mux)
}
<|endoftext|>


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

// This package implements a basic LISP interpretor for embedding in a go program for scripting.
// This file contains the frame primitive functions.

package golisp

import (
	"errors"
	"fmt"
)

func RegisterFramePrimitives() {
	MakePrimitiveFunction("make-frame", -1, MakeFrameImpl)
	MakePrimitiveFunction("has-slot?", 2, HasSlotImpl)
	MakePrimitiveFunction("get-slot", 2, GetSlotImpl)
	MakePrimitiveFunction("get-slot-or-nil", 2, GetSlotOrNilImpl)
	MakePrimitiveFunction("remove-slot!", 2, RemoveSlotImpl)
	MakePrimitiveFunction("set-slot!", 3, SetSlotImpl)
	MakePrimitiveFunction("send", -1, SendImpl)
	MakePrimitiveFunction("send-super", -1, SendSuperImpl)
	MakePrimitiveFunction("clone", 1, CloneImpl)
}

func MakeFrameImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	if Length(args)%2 != 0 {
		err = errors.New("Frames must be initialized with an even number of arguments.")
	}
	m := make(FrameMap)
	for c := args; NotNilP(c); c = Cddr(c) {
		k := Car(c)
		if !NakedP(k) {
			err = errors.New(fmt.Sprintf("Frame keys must be naked symbols, but was given %s.", String(k)))
			return
		}
		v, e := Eval(Cadr(c), env)
		if e != nil {
			return nil, e
		}
		m[StringValue(k)] = v
	}
	return FrameWithValue(&m), nil
}

func HasSlotImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	f, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !FrameP(f) {
		err = errors.New(fmt.Sprintf("has-slot requires a frame as it's first argument, but was given %s.", String(f)))
	}

	k, err := Eval(Cadr(args), env)
	if err != nil {
		return
	}
	if !SymbolP(k) {
		err = errors.New(fmt.Sprintf("has-slot requires a symbol as it's second argument, but was given %s.", String(k)))
		return
	}

	return BooleanWithValue(f.Frame.HasSlot(StringValue(k))), nil
}

func GetSlotImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	f, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !FrameP(f) {
		err = errors.New(fmt.Sprintf("get-slot requires a frame as it's first argument, but was given %s.", String(f)))
	}

	k, err := Eval(Cadr(args), env)
	if err != nil {
		return
	}
	if !SymbolP(k) {
		err = errors.New(fmt.Sprintf("get-slot requires a symbol as it's second argument, but was given %s.", String(k)))
		return
	}

	if !f.Frame.HasSlot(StringValue(k)) {
		err = errors.New(fmt.Sprintf("get-slot requires an existing slot, but was given %s.", String(k)))
		return
	}

	return f.Frame.Get(StringValue(k)), nil
}

func GetSlotOrNilImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	f, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !FrameP(f) {
		err = errors.New(fmt.Sprintf("get-slot-or-nil requires a frame as it's first argument, but was given %s.", String(f)))
	}

	k, err := Eval(Cadr(args), env)
	if err != nil {
		return
	}
	if !SymbolP(k) {
		err = errors.New(fmt.Sprintf("get-slot-or-nil requires a symbol as it's second argument, but was given %s.", String(k)))
		return
	}

	return f.Frame.Get(StringValue(k)), nil
}

func RemoveSlotImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	f, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !FrameP(f) {
		err = errors.New(fmt.Sprintf("remove-slot! requires a frame as it's first argument, but was given %s.", String(f)))
	}

	k, err := Eval(Cadr(args), env)
	if err != nil {
		return
	}
	if !SymbolP(k) {
		err = errors.New(fmt.Sprintf("remove-slot! requires a symbol as it's second argument, but was given %s.", String(k)))
		return
	}

	return BooleanWithValue(f.Frame.Remove(StringValue(k))), nil
}

func SetSlotImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	f, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !FrameP(f) {
		err = errors.New(fmt.Sprintf("set-slot requires a frame as it's first argument, but was given %s.", String(f)))
	}

	k, err := Eval(Cadr(args), env)
	if err != nil {
		return
	}
	if !NakedP(k) {
		err = errors.New(fmt.Sprintf("set-slot requires a naked symbol as it's second argument, but was given %s.", String(k)))
		return
	}

	v, err := Eval(Caddr(args), env)
	if err != nil {
		return
	}

	return f.Frame.Set(StringValue(k), v), nil
}

func SendImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	f, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !FrameP(f) {
		err = errors.New(fmt.Sprintf("send requires a frame as it's first argument, but was given %s.", String(f)))
	}

	k, err := Eval(Cadr(args), env)
	if err != nil {
		return
	}
	if !SymbolP(k) {
		err = errors.New(fmt.Sprintf("send requires a symbol as it's second argument, but was given %s.", String(k)))
		return
	}

	if !f.Frame.HasSlot(StringValue(k)) {
		err = errors.New(fmt.Sprintf("send requires an existing slot, but was given %s.", String(k)))
		return
	}

	fun := f.Frame.Get(StringValue(k))
	if !FunctionP(fun) {
		err = errors.New(fmt.Sprintf("send requires a function slot, but was given a slot containing a %s.", TypeName(TypeOf(fun))))
	}

	params := Cddr(args)
	frameEnv := NewSymbolTableFrameBelowWithFrame(env, f.Frame)
	frameEnv.BindLocallyTo(SymbolWithName("self"), f)
	return fun.Func.Apply(params, frameEnv)
}

func getSuperFunction(selector string, env *SymbolTableFrame) *Data {
	f := env.Frame
	if f == nil {
		return nil
	}

	for _, p := range f.Parents() {
		fun := p.Get(selector)
		if fun != nil {
			return fun
		}
	}

	return nil
}

func SendSuperImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	if !env.HasFrame() {
		err = errors.New("send-super can only be used within the context of a frame.")
		return
	}

	selector, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !SymbolP(selector) {
		err = errors.New(fmt.Sprintf("Selector must be a symbol but was %s.", TypeName(TypeOf(selector))))
		return
	}

	fun := getSuperFunction(StringValue(selector), env)
	if fun == nil || !FunctionP(fun) {
		err = errors.New(fmt.Sprintf("Message sent must select a function slot but was %s.", TypeName(TypeOf(fun))))
		return
	}

	params := Cdr(args)
	frameEnv := NewSymbolTableFrameBelowWithFrame(env, env.Frame)
	frameEnv.BindLocallyTo(SymbolWithName("self"), FrameWithValue(env.Frame))
	return fun.Func.Apply(params, frameEnv)
}

func CloneImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	f, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !FrameP(f) {
		err = errors.New(fmt.Sprintf("clone requires a frame as it's argument, but was given %s.", String(f)))
	}

	return FrameWithValue(f.Frame.Clone()), nil
}
<commit_msg>Added conversion functions<commit_after>// Copyright 2014 SteelSeries ApS.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This package implements a basic LISP interpretor for embedding in a go program for scripting.
// This file contains the frame primitive functions.

package golisp

import (
	"errors"
	"fmt"
)

func RegisterFramePrimitives() {
	MakePrimitiveFunction("make-frame", -1, MakeFrameImpl)
	MakePrimitiveFunction("has-slot?", 2, HasSlotImpl)
	MakePrimitiveFunction("get-slot", 2, GetSlotImpl)
	MakePrimitiveFunction("get-slot-or-nil", 2, GetSlotOrNilImpl)
	MakePrimitiveFunction("remove-slot!", 2, RemoveSlotImpl)
	MakePrimitiveFunction("set-slot!", 3, SetSlotImpl)
	MakePrimitiveFunction("send", -1, SendImpl)
	MakePrimitiveFunction("send-super", -1, SendSuperImpl)
	MakePrimitiveFunction("clone", 1, CloneImpl)
	MakePrimitiveFunction("json->lisp", 1, JsonToLispImpl)
	MakePrimitiveFunction("lisp->json", 1, LispToJsonImpl)
}

func MakeFrameImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	if Length(args)%2 != 0 {
		err = errors.New("Frames must be initialized with an even number of arguments.")
	}
	m := make(FrameMap)
	for c := args; NotNilP(c); c = Cddr(c) {
		k := Car(c)
		if !NakedP(k) {
			err = errors.New(fmt.Sprintf("Frame keys must be naked symbols, but was given %s.", String(k)))
			return
		}
		v, e := Eval(Cadr(c), env)
		if e != nil {
			return nil, e
		}
		m[StringValue(k)] = v
	}
	return FrameWithValue(&m), nil
}

func HasSlotImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	f, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !FrameP(f) {
		err = errors.New(fmt.Sprintf("has-slot requires a frame as it's first argument, but was given %s.", String(f)))
	}

	k, err := Eval(Cadr(args), env)
	if err != nil {
		return
	}
	if !SymbolP(k) {
		err = errors.New(fmt.Sprintf("has-slot requires a symbol as it's second argument, but was given %s.", String(k)))
		return
	}

	return BooleanWithValue(f.Frame.HasSlot(StringValue(k))), nil
}

func GetSlotImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	f, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !FrameP(f) {
		err = errors.New(fmt.Sprintf("get-slot requires a frame as it's first argument, but was given %s.", String(f)))
	}

	k, err := Eval(Cadr(args), env)
	if err != nil {
		return
	}
	if !SymbolP(k) {
		err = errors.New(fmt.Sprintf("get-slot requires a symbol as it's second argument, but was given %s.", String(k)))
		return
	}

	if !f.Frame.HasSlot(StringValue(k)) {
		err = errors.New(fmt.Sprintf("get-slot requires an existing slot, but was given %s.", String(k)))
		return
	}

	return f.Frame.Get(StringValue(k)), nil
}

func GetSlotOrNilImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	f, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !FrameP(f) {
		err = errors.New(fmt.Sprintf("get-slot-or-nil requires a frame as it's first argument, but was given %s.", String(f)))
	}

	k, err := Eval(Cadr(args), env)
	if err != nil {
		return
	}
	if !SymbolP(k) {
		err = errors.New(fmt.Sprintf("get-slot-or-nil requires a symbol as it's second argument, but was given %s.", String(k)))
		return
	}

	return f.Frame.Get(StringValue(k)), nil
}

func RemoveSlotImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	f, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !FrameP(f) {
		err = errors.New(fmt.Sprintf("remove-slot! requires a frame as it's first argument, but was given %s.", String(f)))
	}

	k, err := Eval(Cadr(args), env)
	if err != nil {
		return
	}
	if !SymbolP(k) {
		err = errors.New(fmt.Sprintf("remove-slot! requires a symbol as it's second argument, but was given %s.", String(k)))
		return
	}

	return BooleanWithValue(f.Frame.Remove(StringValue(k))), nil
}

func SetSlotImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	f, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !FrameP(f) {
		err = errors.New(fmt.Sprintf("set-slot requires a frame as it's first argument, but was given %s.", String(f)))
	}

	k, err := Eval(Cadr(args), env)
	if err != nil {
		return
	}
	if !NakedP(k) {
		err = errors.New(fmt.Sprintf("set-slot requires a naked symbol as it's second argument, but was given %s.", String(k)))
		return
	}

	v, err := Eval(Caddr(args), env)
	if err != nil {
		return
	}

	return f.Frame.Set(StringValue(k), v), nil
}

func SendImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	f, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !FrameP(f) {
		err = errors.New(fmt.Sprintf("send requires a frame as it's first argument, but was given %s.", String(f)))
	}

	k, err := Eval(Cadr(args), env)
	if err != nil {
		return
	}
	if !SymbolP(k) {
		err = errors.New(fmt.Sprintf("send requires a symbol as it's second argument, but was given %s.", String(k)))
		return
	}

	if !f.Frame.HasSlot(StringValue(k)) {
		err = errors.New(fmt.Sprintf("send requires an existing slot, but was given %s.", String(k)))
		return
	}

	fun := f.Frame.Get(StringValue(k))
	if !FunctionP(fun) {
		err = errors.New(fmt.Sprintf("send requires a function slot, but was given a slot containing a %s.", TypeName(TypeOf(fun))))
	}

	params := Cddr(args)
	frameEnv := NewSymbolTableFrameBelowWithFrame(env, f.Frame)
	frameEnv.BindLocallyTo(SymbolWithName("self"), f)
	return fun.Func.Apply(params, frameEnv)
}

func getSuperFunction(selector string, env *SymbolTableFrame) *Data {
	f := env.Frame
	if f == nil {
		return nil
	}

	for _, p := range f.Parents() {
		fun := p.Get(selector)
		if fun != nil {
			return fun
		}
	}

	return nil
}

func SendSuperImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	if !env.HasFrame() {
		err = errors.New("send-super can only be used within the context of a frame.")
		return
	}

	selector, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !SymbolP(selector) {
		err = errors.New(fmt.Sprintf("Selector must be a symbol but was %s.", TypeName(TypeOf(selector))))
		return
	}

	fun := getSuperFunction(StringValue(selector), env)
	if fun == nil || !FunctionP(fun) {
		err = errors.New(fmt.Sprintf("Message sent must select a function slot but was %s.", TypeName(TypeOf(fun))))
		return
	}

	params := Cdr(args)
	frameEnv := NewSymbolTableFrameBelowWithFrame(env, env.Frame)
	frameEnv.BindLocallyTo(SymbolWithName("self"), FrameWithValue(env.Frame))
	return fun.Func.Apply(params, frameEnv)
}

func CloneImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	f, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !FrameP(f) {
		err = errors.New(fmt.Sprintf("clone requires a frame as it's argument, but was given %s.", String(f)))
		return
	}

	return FrameWithValue(f.Frame.Clone()), nil
}

func JsonToLispImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	j, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !StringP(j) {
		err = errors.New(fmt.Sprintf("json->lisp requires a string as it's argument, but was given %s.", String(j)))
		return
	}

	return JsonStringToLispWithFrames(StringValue(j)), nil
}

func LispToJsonImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	l, err := Eval(Car(args), env)
	if err != nil {
		return
	}

	return StringWithValue(LispWithFramesToJsonString(l)), nil
}
<|endoftext|>


<commit_before>package prime

import (
	"testing"
)

func TestPrimes(t *testing.T) {
	r := Primes(1000000)
	if len(r) != 78498 {
		t.Error("Wrong number of prime numbers lower than 1M ", len(r))
	}
	r = Primes(10)
	if len(r) != 4 {
		t.Error("Wrong number of prime numbers lower than 10 ", len(r))
	}
	r = Primes(1)
	if len(r) != 0 {
		t.Error("Edge case of 1 not correct ", len(r))
	}
	r = Primes(2)
	if len(r) != 1 {
		t.Error("Edge case of 2 not correct ", len(r))
	}
}

func TestFill(t *testing.T) {
	p := make([]uint64, 101)
	fill(p, 2, 100)
	var c uint64
	for _, v := range p {
		if v == 1 {
			c++
		}
	}
	if c != 49 {
		t.Error("Filled cells are wrong ", c)
	}
}
<commit_msg>rename the local var<commit_after>package prime

import (
	"testing"
)

func TestPrimes(t *testing.T) {
	p := Primes(1000000)
	if len(p) != 78498 {
		t.Error("Wrong number of prime numbers lower than 1M ", len(p))
	}
	p = Primes(10)
	if len(p) != 4 {
		t.Error("Wrong number of prime numbers lower than 10 ", len(p))
	}
	p = Primes(1)
	if len(p) != 0 {
		t.Error("Edge case of 1 not correct ", len(p))
	}
	p = Primes(2)
	if len(p) != 1 {
		t.Error("Edge case of 2 not correct ", len(p))
	}
}

func TestFill(t *testing.T) {
	p := make([]uint64, 101)
	fill(p, 2, 100)
	var c uint64
	for _, v := range p {
		if v == 1 {
			c++
		}
	}
	if c != 49 {
		t.Error("Filled cells are wrong ", c)
	}
}
<|endoftext|>


<commit_before>package prime

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func Test_Given_1_then_return_empty_list(t *testing.T) {
	assert.Equal(t, []int{}, Prime(1))
}
<commit_msg>add second test case: given 2 renturn {2}<commit_after>package prime

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func Test_given_1_then_return_empty_list(t *testing.T) {
	assert.Equal(t, []int{}, Prime(1))
}

func Test_given_2_then_return_2(t *testing.T) {
	assert.Equal(t, []int{2}, Prime(2))
}
<|endoftext|>


<commit_before>package geoloader

import (
        "errors"
	"log"
	"sync"
        "time"

	"github.com/m-lab/annotation-service/api"
	"github.com/m-lab/annotation-service/geolite2"
)

const (
	// Maximum number of Geolite2 datasets in memory.
	MaxHistoricalGeolite2Dataset = 5
)

var (
	// GeoLite2Annotator points to Geolite2 datasets in memory.
	Geolite2Annotator map[string]api.Annotator

	// A mutex to make sure that we are not reading from the Geolite2Annotator
	// pointer while trying to update it
	archivedDataMutex = &sync.RWMutex{}
)

// GetArchivedAnnotator returns the pointer to the dataset in memory with the filename.
func GetArchivedAnnotator(filename string) api.Annotator {
	ann := Geolite2Annotator[filename]
	if ann != nil {
		return ann
	}

	// load new dataset into memory if it is not there already
	archivedDataMutex.Lock()
	if len(Geolite2Annotator) >= MaxHistoricalGeolite2Dataset {
		// Remove one entry
		for key, _ := range Geolite2Annotator {
			log.Println("remove Geolite2 dataset " + key)
			delete(Geolite2Annotator, key)
			break
		}
	}
	ann, err := geolite2.LoadGeoLite2Dataset(filename, api.MaxmindBucketName)
	if err != nil {
		return nil
	}
	log.Println("historical Geolite2 dataset loaded " + filename)
	Geolite2Annotator[filename] = ann
	log.Printf("number of Geolite2 dataset in memory: %d ", len(Geolite2Annotator))
	log.Println(Geolite2Annotator)
	archivedDataMutex.Unlock()

	return ann
}

// PopulateLatestData will search to the latest Geolite2 files
// available in GCS and will use them to create a new GeoDataset which
// it will place into the global scope as the latest version. It will
// do so safely with use of the currentDataMutex RW mutex. It it
// encounters an error, it will halt the program.
func GetLatestData() api.Annotator {
	data, err := geolite2.LoadLatestGeolite2File()
	if err != nil {
		log.Fatal(err)
	}
	return data
}

// SelectArchivedDataset returns the archived GelLite dataset filename given a date.
// For any input date earlier than 2013/08/28, we will return 2013/08/28 dataset.
// For any input date later than latest available dataset, we will return the latest dataset
// Otherwise, we return the last dataset before the input date.
func SelectArchivedDataset(requestDate time.Time) (string, error) {
	earliestArchiveDate, _ := time.Parse("January 2, 2006", "August 28, 2013")
	if requestDate.Before(earliestArchiveDate) {
		return "Maxmind/2013/08/28/20130828T184800Z-GeoLiteCity.dat.gz", nil
	}
	CutOffDate, _ := time.Parse("January 2, 2006", GeoLite2CutOffDate)
	lastFilename := ""
	for _, fileName := range DatasetNames {
		if requestDate.Before(CutOffDate) && (GeoLegacyRegex.MatchString(fileName) || GeoLegacyv6Regex.MatchString(fileName)) {
			// search legacy dataset
			fileDate, err := ExtractDateFromFilename(fileName)
			if err != nil {
				continue
			}
			// return the last dataset that is earlier than requestDate
			if fileDate.After(requestDate) {
				return lastFilename, nil
			}
			lastFilename = fileName
		} else if !requestDate.Before(CutOffDate) && GeoLite2Regex.MatchString(fileName) {
			// Search GeoLite2 dataset
			fileDate, err := ExtractDateFromFilename(fileName)
			if err != nil {
				continue
			}
			// return the last dataset that is earlier than requestDate
			if fileDate.After(requestDate) {
				return lastFilename, nil
			}
			lastFilename = fileName
		}
	}
	// If there is no filename selected, return the latest dataset
	if lastFilename == "" {
		return "", errors.New("cannot find proper dataset")
	}
	return lastFilename, nil
}
<commit_msg>steal gfr's code<commit_after>package geoloader

import (
	"errors"
	"log"
	"sync"
	"time"

	"github.com/m-lab/annotation-service/api"
	"github.com/m-lab/annotation-service/geolite2"
)

const (
	// Maximum number of Geolite2 datasets in memory.
	MaxHistoricalGeolite2Dataset = 5
)

var (
	ArchivedLoader AnnotatorLoader

	// ErrPendingAnnotatorLoad is returned when a new annotator is requested, but not yet loaded.
	ErrPendingAnnotatorLoad = errors.New("annotator is loading")

	ErrAnnotatorLoadFailed = errors.New("unable to load annoator")
)

// AnnotatorLoader manages all loading of and already loaded Annotators
type AnnotatorLoader struct {
	// Keys are date strings in YYYYMMDD format.
	annotators map[string]api.Annotator
	// Lock to be held when reading or writing the map.
	mutex sync.RWMutex
}

// NOTE: Should only be called by checkAndLoadAnnotator.
// Loads an annotator, and updates the pending map entry.
// On entry, the calling goroutine should "own" the
func (am *AnnotatorLoader) loadAnnotator(dateString string) {
	// On entry, this goroutine has exclusive ownership of the
	// map entry, and the responsibility for loading the annotator.
	var ann api.Annotator = nil
	// TODO actually load the annotator and handle loading errors.

	am.mutex.Lock()
	defer am.mutex.Unlock()

	ann, ok := am.annotators[dateString]
	if !ok {
		// TODO handle error
	}
	if ann != nil {
		// TODO handle error
	}
	am.annotators[dateString] = ann
}

// This asynchronously attempts to set map entry to nil, and
// if successful, proceeds to asynchronously load the new dataset.
func (am *AnnotatorLoader) checkAndLoadAnnotator(dateString string) {
	go func() {
		am.mutex.Lock()

		_, ok := am.annotators[dateString]
		if ok {
			// Another goroutine is already responsible for loading.
			am.mutex.Unlock()
			return
		} else {
			// Place marker so that other requesters know it is loading.
			am.annotators[dateString] = nil
		}

		// Drop the lock before attempting to load the annotator.
		am.mutex.Unlock()
		am.loadAnnotator(dateString)
	}()
}

// Gets the named annotator, if already in the map.
func (am *AnnotatorLoader) GetAnnotator(dateString string) (api.Annotator, error) {
	am.mutex.RLock()
	defer am.mutex.RUnlock()

	ann, ok := am.annotators[dateString]
	if ok {
		return ann, nil
	} else {
		am.checkAndLoadAnnotator(dateString)
		return nil, ErrPendingAnnotatorLoad
	}
}

// GetArchivedAnnotator returns the pointer to the dataset in memory with the filename.
func GetArchivedAnnotator(filename string) api.Annotator {
	ann, err := ArchivedLoader.GetAnnotator(filename)

	if err == nil {
		return ann
	}
	return nil
}

// PopulateLatestData will search to the latest Geolite2 files
// available in GCS and will use them to create a new GeoDataset which
// it will place into the global scope as the latest version. It will
// do so safely with use of the currentDataMutex RW mutex. It it
// encounters an error, it will halt the program.
func GetLatestData() api.Annotator {
	data, err := geolite2.LoadLatestGeolite2File()
	if err != nil {
		log.Fatal(err)
	}
	return data
}

// SelectArchivedDataset returns the archived GelLite dataset filename given a date.
// For any input date earlier than 2013/08/28, we will return 2013/08/28 dataset.
// For any input date later than latest available dataset, we will return the latest dataset
// Otherwise, we return the last dataset before the input date.
func SelectArchivedDataset(requestDate time.Time) (string, error) {
	earliestArchiveDate, _ := time.Parse("January 2, 2006", "August 28, 2013")
	if requestDate.Before(earliestArchiveDate) {
		return "Maxmind/2013/08/28/20130828T184800Z-GeoLiteCity.dat.gz", nil
	}
	CutOffDate, _ := time.Parse("January 2, 2006", GeoLite2CutOffDate)
	lastFilename := ""
	for _, fileName := range DatasetNames {
		if requestDate.Before(CutOffDate) && (GeoLegacyRegex.MatchString(fileName) || GeoLegacyv6Regex.MatchString(fileName)) {
			// search legacy dataset
			fileDate, err := ExtractDateFromFilename(fileName)
			if err != nil {
				continue
			}
			// return the last dataset that is earlier than requestDate
			if fileDate.After(requestDate) {
				return lastFilename, nil
			}
			lastFilename = fileName
		} else if !requestDate.Before(CutOffDate) && GeoLite2Regex.MatchString(fileName) {
			// Search GeoLite2 dataset
			fileDate, err := ExtractDateFromFilename(fileName)
			if err != nil {
				continue
			}
			// return the last dataset that is earlier than requestDate
			if fileDate.After(requestDate) {
				return lastFilename, nil
			}
			lastFilename = fileName
		}
	}
	// If there is no filename selected, return the latest dataset
	if lastFilename == "" {
		return "", errors.New("cannot find proper dataset")
	}
	return lastFilename, nil
}
<|endoftext|>


<commit_before>package geolocate

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

const (
	geoipURI   string = "http://www.telize.com/geoip"
	geocodeURI string = "https://geocode.jessfraz.com/geocode"
)

// Geolocation response from www.telize.com/geoip
// comes back like:
// {
//     timezone: "America/New_York",
//     isp: "Road Runner HoldCo LLC",
//     region_code: "NY",
//     country: "United States",
//     dma_code: "0",
//     area_code: "0",
//     region: "New York",
//     ip: "74.71.83.142",
//     asn: "AS11351",
//     continent_code: "NA",
//     city: "New York",
//     postal_code: "10128",
//     longitude: -73.9512,
//     latitude: 40.7805,
//     country_code: "US",
//     country_code3: "USA"
// }
type Geolocation struct {
	AreaCode      string  `json:"area_code"`
	Asn           string  `json:"asn"`
	City          string  `json:"city"`
	ContinentCode string  `json:"continent_code"`
	Country       string  `json:"country"`
	CountryCode   string  `json:"country_code"`
	CountryCode3  string  `json:"country_code3"`
	DMACode       string  `json:"dma_code"`
	IP            string  `json:"ip"`
	Isp           string  `json:"isp"`
	Latitude      float64 `json:"latitude"`
	Longitude     float64 `json:"longitude"`
	PostalCode    string  `json:"postal_code"`
	Region        string  `json:"region"`
	RegionCode    string  `json:"region_code"`
	Timezone      string  `json:"timezone"`
}

// Autolocate gets the requesters geolocation based off their IP address
func Autolocate() (geolocation Geolocation, err error) {
	// send the request
	resp, err := http.Get(geoipURI)
	if err != nil {
		return geolocation, err
	}
	defer resp.Body.Close()

	// decode the body
	dec := json.NewDecoder(resp.Body)
	if err := dec.Decode(&geolocation); err != nil {
		return geolocation, fmt.Errorf("Decoding autolocate response failed: %v", err)
	}

	return geolocation, nil
}

// Locate gets the geolocation data of a location that is passed as a string
func Locate(location string) (geolocation Geolocation, err error) {
	// create json data
	jsonByte, err := json.Marshal(map[string]string{"location": location})
	if err != nil {
		return geolocation, fmt.Errorf("Marshaling location json failed: %v", err)
	}

	// send the request
	req, err := http.NewRequest("POST", geocodeURI, bytes.NewReader(jsonByte))
	if err != nil {
		return geolocation, err
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return geolocation, err
	}
	defer resp.Body.Close()

	// decode the body
	dec := json.NewDecoder(resp.Body)
	if err := dec.Decode(&geolocation); err != nil {
		return geolocation, fmt.Errorf("Decoding geolocate response failed: %v", err)
	}

	return geolocation, nil
}
<commit_msg>geolocate: handle api error.<commit_after>package geolocate

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

const (
	geoipURI   string = "http://www.telize.com/geoip"
	geocodeURI string = "https://geocode.jessfraz.com/geocode"
)

// Geolocation response from www.telize.com/geoip
// comes back like:
// {
//     timezone: "America/New_York",
//     isp: "Road Runner HoldCo LLC",
//     region_code: "NY",
//     country: "United States",
//     dma_code: "0",
//     area_code: "0",
//     region: "New York",
//     ip: "74.71.83.142",
//     asn: "AS11351",
//     continent_code: "NA",
//     city: "New York",
//     postal_code: "10128",
//     longitude: -73.9512,
//     latitude: 40.7805,
//     country_code: "US",
//     country_code3: "USA"
// }
type Geolocation struct {
	AreaCode        string  `json:"area_code"`
	Asn             string  `json:"asn"`
	City            string  `json:"city"`
	ContinentCode   string  `json:"continent_code"`
	Country         string  `json:"country"`
	CountryCode     string  `json:"country_code"`
	CountryCode3    string  `json:"country_code3"`
	DMACode         string  `json:"dma_code"`
	IP              string  `json:"ip"`
	Isp             string  `json:"isp"`
	Latitude        float64 `json:"latitude"`
	Longitude       float64 `json:"longitude"`
	PostalCode      string  `json:"postal_code"`
	Region          string  `json:"region"`
	RegionCode      string  `json:"region_code"`
	Timezone        string  `json:"timezone"`
	ApiError        bool    `json:"error"`
	ApiErrorMessage string  `json:"message"`
}

// Autolocate gets the requesters geolocation based off their IP address
func Autolocate() (geolocation Geolocation, err error) {
	// send the request
	resp, err := http.Get(geoipURI)
	if err != nil {
		return geolocation, err
	}
	defer resp.Body.Close()

	// decode the body
	dec := json.NewDecoder(resp.Body)
	if err := dec.Decode(&geolocation); err != nil {
		return geolocation, fmt.Errorf("Decoding autolocate response failed: %v", err)
	}

	return geolocation, nil
}

// Locate gets the geolocation data of a location that is passed as a string
func Locate(location string) (geolocation Geolocation, err error) {
	// create json data
	jsonByte, err := json.Marshal(map[string]string{"location": location})
	if err != nil {
		return geolocation, fmt.Errorf("Marshaling location json failed: %v", err)
	}

	// send the request
	req, err := http.NewRequest("POST", geocodeURI, bytes.NewReader(jsonByte))
	if err != nil {
		return geolocation, err
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return geolocation, err
	}
	defer resp.Body.Close()

	// decode the body
	dec := json.NewDecoder(resp.Body)
	if err := dec.Decode(&geolocation); err != nil {
		return geolocation, fmt.Errorf("Decoding geolocate response failed: %v", err)
	}
	if geolocation.ApiError {
		return geolocation, fmt.Errorf("Api error: %s", geolocation.ApiErrorMessage)
	}

	return geolocation, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"os"
	"path/filepath"
)

const (
	HTTP_DELETE  = "DELETE"
	HTTP_GET     = "GET"
	HTTP_HEAD    = "HEAD"
	HTTP_POST    = "POST"
	HTTP_PUT     = "PUT"
	HTTP_OPTIONS = "OPTIONS"
)

var HTTPVerbs = []string{
	HTTP_DELETE,
	HTTP_GET,
	HTTP_HEAD,
	HTTP_POST,
	HTTP_PUT,
	HTTP_OPTIONS,
}

type ErrNoVerbsFound string

func (e ErrNoVerbsFound) Error() string {
	return fmt.Sprintf("No HTTP verb folders found within: %s", string(e))
}

type ErrNoDefinitionsFound string

func (e ErrNoDefinitionsFound) Error() string {
	return fmt.Sprintf("No definition files found within: %s", string(e))
}

type HTTPMuxer interface {
	Handle(string, http.Handler)
	Handler(r *http.Request) (http.Handler, string)
	ServeHTTP(http.ResponseWriter, *http.Request)
}

func NewStartApp(port string, rootDir string) *StartApp {
	return &StartApp{
		RootDir:              rootDir,
		Port:                 port,
		PathToVerb:           make(map[string][]string),
		PathVerbToDefinition: make(map[string][]string),
	}
}

type StartApp struct {
	RootDir              string
	Port                 string
	PathToVerb           map[string][]string
	PathVerbToDefinition map[string][]string
	HTTPMuxer
}

func (s *StartApp) Setup() error {
	err := filepath.Walk(s.RootDir, s.walkFn)
	if err != nil {
		return err
	}

	if len(s.PathToVerb) == 0 {
		return ErrNoVerbsFound(s.RootDir)
	}

	if len(s.PathVerbToDefinition) == 0 {
		return ErrNoDefinitionsFound(s.RootDir)
	}

	for endpoint := range s.PathToVerb {
		s.HTTPMuxer.Handle("/"+endpoint, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
	}

	return nil
}

func (s *StartApp) walkFn(path string, info os.FileInfo, err error) error {
	if err != nil {
		return err
	}

	relativePath, err := filepath.Rel(s.RootDir, path)
	if err != nil {
		return err
	}
	relativeDirPath := filepath.Dir(relativePath)

	if info.IsDir() {
		for _, httpVerb := range HTTPVerbs {
			if info.Name() == httpVerb {
				s.PathToVerb[relativeDirPath] = append(s.PathToVerb[relativeDirPath], httpVerb)
			}
		}
	} else {
		s.PathVerbToDefinition[relativeDirPath] = append(s.PathVerbToDefinition[relativeDirPath], info.Name())
	}

	return nil
}
<commit_msg>Add HTTPServer to start app, with Run and Stop methods<commit_after>package main

import (
	"fmt"
	"net/http"
	"os"
	"path/filepath"

	"github.com/braintree/manners"
)

const (
	HTTP_DELETE  = "DELETE"
	HTTP_GET     = "GET"
	HTTP_HEAD    = "HEAD"
	HTTP_POST    = "POST"
	HTTP_PUT     = "PUT"
	HTTP_OPTIONS = "OPTIONS"
)

var HTTPVerbs = []string{
	HTTP_DELETE,
	HTTP_GET,
	HTTP_HEAD,
	HTTP_POST,
	HTTP_PUT,
	HTTP_OPTIONS,
}

type ErrNoVerbsFound string

func (e ErrNoVerbsFound) Error() string {
	return fmt.Sprintf("No HTTP verb folders found within: %s", string(e))
}

type ErrNoDefinitionsFound string

func (e ErrNoDefinitionsFound) Error() string {
	return fmt.Sprintf("No definition files found within: %s", string(e))
}

type HTTPMuxer interface {
	Handle(string, http.Handler)
	Handler(r *http.Request) (http.Handler, string)
	ServeHTTP(http.ResponseWriter, *http.Request)
}

func NewStartApp(port string, rootDir string) *StartApp {
	return &StartApp{
		RootDir:              rootDir,
		Port:                 port,
		PathToVerb:           make(map[string][]string),
		PathVerbToDefinition: make(map[string][]string),
	}
}

type StartApp struct {
	RootDir              string
	Port                 string
	PathToVerb           map[string][]string
	PathVerbToDefinition map[string][]string
	HTTPServer           *manners.GracefulServer
	HTTPMuxer
}

func (s *StartApp) Setup() error {
	err := filepath.Walk(s.RootDir, s.walkFn)
	if err != nil {
		return err
	}

	if len(s.PathToVerb) == 0 {
		return ErrNoVerbsFound(s.RootDir)
	}

	if len(s.PathVerbToDefinition) == 0 {
		return ErrNoDefinitionsFound(s.RootDir)
	}

	for endpoint := range s.PathToVerb {
		s.HTTPMuxer.Handle("/"+endpoint, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
	}

	s.HTTPServer = manners.NewWithServer(&http.Server{Handler: s.HTTPMuxer, Addr: ":" + s.Port})

	return nil
}

func (s *StartApp) Run() error {
	return s.HTTPServer.ListenAndServe()
}

func (s *StartApp) Stop() bool {
	if s.HTTPServer != nil {
		return s.HTTPServer.BlockingClose()
	}

	return true
}

func (s *StartApp) walkFn(path string, info os.FileInfo, err error) error {
	if err != nil {
		return err
	}

	relativePath, err := filepath.Rel(s.RootDir, path)
	if err != nil {
		return err
	}
	relativeDirPath := filepath.Dir(relativePath)

	if info.IsDir() {
		for _, httpVerb := range HTTPVerbs {
			if info.Name() == httpVerb {
				s.PathToVerb[relativeDirPath] = append(s.PathToVerb[relativeDirPath], httpVerb)
			}
		}
	} else {
		s.PathVerbToDefinition[relativeDirPath] = append(s.PathVerbToDefinition[relativeDirPath], info.Name())
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The Gogs Authors. All rights reserved.
// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package repo

import (
	"context"
	"errors"
	"fmt"
	"time"

	"code.gitea.io/gitea/models/db"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/timeutil"
)

// ErrMirrorNotExist mirror does not exist error
var ErrMirrorNotExist = errors.New("Mirror does not exist")

// Mirror represents mirror information of a repository.
type Mirror struct {
	ID          int64       `xorm:"pk autoincr"`
	RepoID      int64       `xorm:"INDEX"`
	Repo        *Repository `xorm:"-"`
	Interval    time.Duration
	EnablePrune bool `xorm:"NOT NULL DEFAULT true"`

	UpdatedUnix    timeutil.TimeStamp `xorm:"INDEX"`
	NextUpdateUnix timeutil.TimeStamp `xorm:"INDEX"`

	LFS         bool   `xorm:"lfs_enabled NOT NULL DEFAULT false"`
	LFSEndpoint string `xorm:"lfs_endpoint TEXT"`

	Address string `xorm:"-"`
}

func init() {
	db.RegisterModel(new(Mirror))
}

// BeforeInsert will be invoked by XORM before inserting a record
func (m *Mirror) BeforeInsert() {
	if m != nil {
		m.UpdatedUnix = timeutil.TimeStampNow()
		m.NextUpdateUnix = timeutil.TimeStampNow()
	}
}

// GetRepository returns the repository.
func (m *Mirror) GetRepository() *Repository {
	if m.Repo != nil {
		return m.Repo
	}
	var err error
	m.Repo, err = GetRepositoryByIDCtx(db.DefaultContext, m.RepoID)
	if err != nil {
		log.Error("getRepositoryByID[%d]: %v", m.ID, err)
	}
	return m.Repo
}

// GetRemoteName returns the name of the remote.
func (m *Mirror) GetRemoteName() string {
	return "origin"
}

// ScheduleNextUpdate calculates and sets next update time.
func (m *Mirror) ScheduleNextUpdate() {
	if m.Interval != 0 {
		m.NextUpdateUnix = timeutil.TimeStampNow().AddDuration(m.Interval)
	} else {
		m.NextUpdateUnix = 0
	}
}

// GetMirrorByRepoID returns mirror information of a repository.
func GetMirrorByRepoID(ctx context.Context, repoID int64) (*Mirror, error) {
	m := &Mirror{RepoID: repoID}
	has, err := db.GetEngine(ctx).Get(m)
	if err != nil {
		return nil, err
	} else if !has {
		return nil, ErrMirrorNotExist
	}
	return m, nil
}

// UpdateMirror updates the mirror
func UpdateMirror(ctx context.Context, m *Mirror) error {
	_, err := db.GetEngine(ctx).ID(m.ID).AllCols().Update(m)
	return err
}

// TouchMirror updates the mirror updatedUnix
func TouchMirror(ctx context.Context, m *Mirror) error {
	m.UpdatedUnix = timeutil.TimeStampNow()
	_, err := db.GetEngine(ctx).ID(m.ID).Cols("updated_unix").Update(m)
	return err
}

// DeleteMirrorByRepoID deletes a mirror by repoID
func DeleteMirrorByRepoID(repoID int64) error {
	_, err := db.GetEngine(db.DefaultContext).Delete(&Mirror{RepoID: repoID})
	return err
}

// MirrorsIterate iterates all mirror repositories.
func MirrorsIterate(limit int, f func(idx int, bean interface{}) error) error {
	return db.GetEngine(db.DefaultContext).
		Where("next_update_unix<=?", time.Now().Unix()).
		And("next_update_unix!=0").
		OrderBy("updated_unix ASC").
		Limit(limit).
		Iterate(new(Mirror), f)
}

// InsertMirror inserts a mirror to database
func InsertMirror(ctx context.Context, mirror *Mirror) error {
	_, err := db.GetEngine(ctx).Insert(mirror)
	return err
}

// MirrorRepositoryList contains the mirror repositories
type MirrorRepositoryList []*Repository

func (repos MirrorRepositoryList) loadAttributes(ctx context.Context) error {
	if len(repos) == 0 {
		return nil
	}

	// Load mirrors.
	repoIDs := make([]int64, 0, len(repos))
	for i := range repos {
		if !repos[i].IsMirror {
			continue
		}

		repoIDs = append(repoIDs, repos[i].ID)
	}
	mirrors := make([]*Mirror, 0, len(repoIDs))
	if err := db.GetEngine(ctx).
		Where("id > 0").
		In("repo_id", repoIDs).
		Find(&mirrors); err != nil {
		return fmt.Errorf("find mirrors: %v", err)
	}

	set := make(map[int64]*Mirror)
	for i := range mirrors {
		set[mirrors[i].RepoID] = mirrors[i]
	}
	for i := range repos {
		repos[i].Mirror = set[repos[i].ID]
		repos[i].Mirror.Repo = repos[i]
	}
	return nil
}

// LoadAttributes loads the attributes for the given MirrorRepositoryList
func (repos MirrorRepositoryList) LoadAttributes() error {
	return repos.loadAttributes(db.DefaultContext)
}

// GetUserMirrorRepositories returns a list of mirror repositories of given user.
func GetUserMirrorRepositories(userID int64) ([]*Repository, error) {
	repos := make([]*Repository, 0, 10)
	return repos, db.GetEngine(db.DefaultContext).
		Where("owner_id = ?", userID).
		And("is_mirror = ?", true).
		Find(&repos)
}
<commit_msg>Check Mirror exists before linking its Repo (#20840)<commit_after>// Copyright 2016 The Gogs Authors. All rights reserved.
// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package repo

import (
	"context"
	"errors"
	"fmt"
	"time"

	"code.gitea.io/gitea/models/db"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/timeutil"
)

// ErrMirrorNotExist mirror does not exist error
var ErrMirrorNotExist = errors.New("Mirror does not exist")

// Mirror represents mirror information of a repository.
type Mirror struct {
	ID          int64       `xorm:"pk autoincr"`
	RepoID      int64       `xorm:"INDEX"`
	Repo        *Repository `xorm:"-"`
	Interval    time.Duration
	EnablePrune bool `xorm:"NOT NULL DEFAULT true"`

	UpdatedUnix    timeutil.TimeStamp `xorm:"INDEX"`
	NextUpdateUnix timeutil.TimeStamp `xorm:"INDEX"`

	LFS         bool   `xorm:"lfs_enabled NOT NULL DEFAULT false"`
	LFSEndpoint string `xorm:"lfs_endpoint TEXT"`

	Address string `xorm:"-"`
}

func init() {
	db.RegisterModel(new(Mirror))
}

// BeforeInsert will be invoked by XORM before inserting a record
func (m *Mirror) BeforeInsert() {
	if m != nil {
		m.UpdatedUnix = timeutil.TimeStampNow()
		m.NextUpdateUnix = timeutil.TimeStampNow()
	}
}

// GetRepository returns the repository.
func (m *Mirror) GetRepository() *Repository {
	if m.Repo != nil {
		return m.Repo
	}
	var err error
	m.Repo, err = GetRepositoryByIDCtx(db.DefaultContext, m.RepoID)
	if err != nil {
		log.Error("getRepositoryByID[%d]: %v", m.ID, err)
	}
	return m.Repo
}

// GetRemoteName returns the name of the remote.
func (m *Mirror) GetRemoteName() string {
	return "origin"
}

// ScheduleNextUpdate calculates and sets next update time.
func (m *Mirror) ScheduleNextUpdate() {
	if m.Interval != 0 {
		m.NextUpdateUnix = timeutil.TimeStampNow().AddDuration(m.Interval)
	} else {
		m.NextUpdateUnix = 0
	}
}

// GetMirrorByRepoID returns mirror information of a repository.
func GetMirrorByRepoID(ctx context.Context, repoID int64) (*Mirror, error) {
	m := &Mirror{RepoID: repoID}
	has, err := db.GetEngine(ctx).Get(m)
	if err != nil {
		return nil, err
	} else if !has {
		return nil, ErrMirrorNotExist
	}
	return m, nil
}

// UpdateMirror updates the mirror
func UpdateMirror(ctx context.Context, m *Mirror) error {
	_, err := db.GetEngine(ctx).ID(m.ID).AllCols().Update(m)
	return err
}

// TouchMirror updates the mirror updatedUnix
func TouchMirror(ctx context.Context, m *Mirror) error {
	m.UpdatedUnix = timeutil.TimeStampNow()
	_, err := db.GetEngine(ctx).ID(m.ID).Cols("updated_unix").Update(m)
	return err
}

// DeleteMirrorByRepoID deletes a mirror by repoID
func DeleteMirrorByRepoID(repoID int64) error {
	_, err := db.GetEngine(db.DefaultContext).Delete(&Mirror{RepoID: repoID})
	return err
}

// MirrorsIterate iterates all mirror repositories.
func MirrorsIterate(limit int, f func(idx int, bean interface{}) error) error {
	return db.GetEngine(db.DefaultContext).
		Where("next_update_unix<=?", time.Now().Unix()).
		And("next_update_unix!=0").
		OrderBy("updated_unix ASC").
		Limit(limit).
		Iterate(new(Mirror), f)
}

// InsertMirror inserts a mirror to database
func InsertMirror(ctx context.Context, mirror *Mirror) error {
	_, err := db.GetEngine(ctx).Insert(mirror)
	return err
}

// MirrorRepositoryList contains the mirror repositories
type MirrorRepositoryList []*Repository

func (repos MirrorRepositoryList) loadAttributes(ctx context.Context) error {
	if len(repos) == 0 {
		return nil
	}

	// Load mirrors.
	repoIDs := make([]int64, 0, len(repos))
	for i := range repos {
		if !repos[i].IsMirror {
			continue
		}

		repoIDs = append(repoIDs, repos[i].ID)
	}
	mirrors := make([]*Mirror, 0, len(repoIDs))
	if err := db.GetEngine(ctx).
		Where("id > 0").
		In("repo_id", repoIDs).
		Find(&mirrors); err != nil {
		return fmt.Errorf("find mirrors: %v", err)
	}

	set := make(map[int64]*Mirror)
	for i := range mirrors {
		set[mirrors[i].RepoID] = mirrors[i]
	}
	for i := range repos {
		repos[i].Mirror = set[repos[i].ID]
		if repos[i].Mirror != nil {
			repos[i].Mirror.Repo = repos[i]
		}
	}
	return nil
}

// LoadAttributes loads the attributes for the given MirrorRepositoryList
func (repos MirrorRepositoryList) LoadAttributes() error {
	return repos.loadAttributes(db.DefaultContext)
}

// GetUserMirrorRepositories returns a list of mirror repositories of given user.
func GetUserMirrorRepositories(userID int64) ([]*Repository, error) {
	repos := make([]*Repository, 0, 10)
	return repos, db.GetEngine(db.DefaultContext).
		Where("owner_id = ?", userID).
		And("is_mirror = ?", true).
		Find(&repos)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"expvar"
	"flag"
	"fmt"
	"html/template"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/NYTimes/gziphandler"
	"github.com/mat/besticon/besticon"
	"github.com/mat/besticon/besticon/iconserver/assets"
)

func indexHandler(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "" || r.URL.Path == "/" {
		renderHTMLTemplate(w, 200, indexHTML, nil)
	} else {
		renderHTMLTemplate(w, 404, notFoundHTML, nil)
	}
}

func iconsHandler(w http.ResponseWriter, r *http.Request) {
	url := r.FormValue(urlParam)
	if len(url) == 0 {
		http.Redirect(w, r, "/", 302)
		return
	}

	icons, e := fetchIcons(url)
	switch {
	case e != nil:
		renderHTMLTemplate(w, 404, iconsHTML, pageInfo{URL: url, Error: e})
	case len(icons) <= 0:
		errNoIcons := errors.New("this poor site has no icons at all :-(")
		renderHTMLTemplate(w, 404, iconsHTML, pageInfo{URL: url, Error: errNoIcons})
	default:
		renderHTMLTemplate(w, 200, iconsHTML, pageInfo{Icons: icons, URL: url})
	}
}

const (
	urlParam          = "url"
	feelingLuckyParam = "i_am_feeling_lucky"
	prettyParam       = "pretty"
	maxAge            = "max_age"
)

const defaultMaxAge = time.Duration(604800) * time.Second // 7 days

func apiHandler(w http.ResponseWriter, r *http.Request) {
	pretty := r.FormValue(prettyParam) == "yes"
	url := r.FormValue(urlParam)
	if len(url) == 0 {
		errMissingURL := errors.New("need url query parameter")
		writeAPIError(w, 400, errMissingURL, pretty)
		return
	}

	bestOnly := r.FormValue(feelingLuckyParam) == "yes"
	if bestOnly {
		icon, e := fetchBestIcon(url)
		if e != nil {
			writeAPIError(w, 404, e, pretty)
			return
		}

		http.Redirect(w, r, icon.URL, 302)
	} else {
		icons, e := fetchIcons(url)
		if e != nil {
			writeAPIError(w, 404, e, pretty)
			return
		}

		maxAge, err := time.ParseDuration(r.FormValue(maxAge))
		if err != nil || maxAge.Seconds() < 1 {
			maxAge = defaultMaxAge
		}
		writeAPIIcons(w, url, icons, maxAge, pretty)
	}
}

func fetchIcons(url string) ([]besticon.Icon, error) {
	fetchCount.Add(1)
	icons, err := besticon.FetchIcons(url, true)
	if err != nil {
		fetchErrors.Add(1)
	}
	return icons, err
}

func fetchBestIcon(url string) (*besticon.Icon, error) {
	fetchCount.Add(1)
	icon, err := besticon.FetchBestIcon(url, true)
	if err != nil {
		fetchErrors.Add(1)
	}
	return icon, err
}

func writeAPIError(w http.ResponseWriter, httpStatus int, e error, pretty bool) {
	data := struct {
		Error string `json:"error"`
	}{
		e.Error(),
	}

	if pretty {
		renderJSONResponsePretty(w, httpStatus, data)
	} else {
		renderJSONResponse(w, httpStatus, data)
	}
}

func writeAPIIcons(w http.ResponseWriter, url string, icons []besticon.Icon, maxAge time.Duration, pretty bool) {
	data := struct {
		URL   string          `json:"url"`
		Icons []besticon.Icon `json:"icons"`
	}{
		url,
		icons,
	}

	w.Header().Add(cacheControl, fmt.Sprintf("max-age=%d", int64(maxAge.Seconds())))
	if pretty {
		renderJSONResponsePretty(w, 200, data)
	} else {
		renderJSONResponse(w, 200, data)
	}
}

const (
	contentType     = "Content-Type"
	applicationJSON = "application/json"
	cacheControl    = "Cache-Control"
)

func renderJSONResponse(w http.ResponseWriter, httpStatus int, data interface{}) {
	w.Header().Add(contentType, applicationJSON)
	w.WriteHeader(httpStatus)
	enc := json.NewEncoder(w)
	enc.Encode(data)
}

func renderJSONResponsePretty(w http.ResponseWriter, httpStatus int, data interface{}) {
	w.Header().Add(contentType, applicationJSON)
	w.WriteHeader(httpStatus)
	b, _ := json.MarshalIndent(data, "", "  ")
	w.Write(b)
}

type pageInfo struct {
	URL   string
	Icons []besticon.Icon
	Error error
}

func (pi pageInfo) Host() string {
	u := pi.URL
	url, _ := url.Parse(u)
	if url != nil && url.Host != "" {
		return url.Host
	}
	return pi.URL
}

func (pi pageInfo) Best() string {
	if len(pi.Icons) > 0 {
		best := pi.Icons[0]
		return best.URL
	}
	return ""
}

func renderHTMLTemplate(w http.ResponseWriter, httpStatus int, templ *template.Template, data interface{}) {
	w.Header().Add(contentType, "text/html; charset=utf-8")
	w.WriteHeader(httpStatus)

	err := templ.Execute(w, data)
	if err != nil {
		err = fmt.Errorf("server: could not generate output: %s", err)
		logger.Print(err)
		w.Write([]byte(err.Error()))
	}
}

func startServer(port int) {
	httpHandle("/", indexHandler)
	httpHandle("/icons", iconsHandler)
	httpHandle("/api/icons", apiHandler)

	serveAsset("/pure-0.5.0-min.css", "besticon/iconserver/assets/pure-0.5.0-min.css", oneYear)
	serveAsset("/grids-responsive-0.5.0-min.css", "besticon/iconserver/assets/grids-responsive-0.5.0-min.css", oneYear)
	serveAsset("/main-min.css", "besticon/iconserver/assets/main-min.css", oneYear)

	serveAsset("/icon.svg", "besticon/iconserver/assets/icon.svg", oneYear)
	serveAsset("/favicon.ico", "besticon/iconserver/assets/favicon.ico", oneYear)
	serveAsset("/apple-touch-icon.png", "besticon/iconserver/assets/apple-touch-icon.png", oneYear)
	serveAsset("/robots.txt", "besticon/iconserver/assets/robots.txt", nocache)

	logger.Print("Starting server on port ", port, "...")
	e := http.ListenAndServe(":"+strconv.Itoa(port), newLoggingMux())
	if e != nil {
		logger.Fatal("cannot start server: %s\n", e)
	}
}

const (
	oneYear = 365 * 24 * 3600
	nocache = -1
)

func serveAsset(path string, assetPath string, maxAgeSeconds int) {
	httpHandle(path, func(w http.ResponseWriter, r *http.Request) {
		assetInfo, err := assets.AssetInfo(assetPath)
		if err != nil {
			panic(err)
		}

		if maxAgeSeconds != nocache {
			w.Header().Add(cacheControl, fmt.Sprintf("max-age=%d", maxAgeSeconds))
		}

		http.ServeContent(w, r, assetInfo.Name(), assetInfo.ModTime(),
			bytes.NewReader(assets.MustAsset(assetPath)))
	})
}

func httpHandle(path string, f http.HandlerFunc) {
	http.Handle(path, gziphandler.GzipHandler(http.HandlerFunc(f)))
}

func main() {
	port := flag.Int("port", 0, "Port in server mode")
	flag.Parse()

	if *port > 0 {
		startServer(*port)
	} else {
		flag.PrintDefaults()
	}
}

func init() {
	indexHTML = templateFromAsset("besticon/iconserver/assets/index.html", "index.html")
	iconsHTML = templateFromAsset("besticon/iconserver/assets/icons.html", "icons.html")
	notFoundHTML = templateFromAsset("besticon/iconserver/assets/not_found.html", "not_found.html")
}

func templateFromAsset(assetPath, templateName string) *template.Template {
	bytes := assets.MustAsset(assetPath)
	return template.Must(template.New(templateName).Funcs(funcMap).Parse(string(bytes)))
}

var indexHTML *template.Template
var iconsHTML *template.Template
var notFoundHTML *template.Template

var funcMap = template.FuncMap{
	"ImgWidth": imgWidth,
}

func imgWidth(i *besticon.Icon) int {
	return i.Width / 2.0
}

func init() {
	besticon.SetCacheMaxSize(64)

	expvar.Publish("cacheBytes", expvar.Func(func() interface{} { return besticon.GetCacheStats().Bytes }))
	expvar.Publish("cacheItems", expvar.Func(func() interface{} { return besticon.GetCacheStats().Items }))
	expvar.Publish("cacheGets", expvar.Func(func() interface{} { return besticon.GetCacheStats().Gets }))
	expvar.Publish("cacheHits", expvar.Func(func() interface{} { return besticon.GetCacheStats().Hits }))
	expvar.Publish("cacheEvictions", expvar.Func(func() interface{} { return besticon.GetCacheStats().Evictions }))

	ticker := time.NewTicker(time.Minute * 1)
	go func() {
		for range ticker.C {
			logger.Printf("Cache: %+v", besticon.GetCacheStats())
		}
	}()
}
<commit_msg>fix: use Fatalf instead of Fatal in server<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"expvar"
	"flag"
	"fmt"
	"html/template"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/NYTimes/gziphandler"
	"github.com/mat/besticon/besticon"
	"github.com/mat/besticon/besticon/iconserver/assets"
)

func indexHandler(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "" || r.URL.Path == "/" {
		renderHTMLTemplate(w, 200, indexHTML, nil)
	} else {
		renderHTMLTemplate(w, 404, notFoundHTML, nil)
	}
}

func iconsHandler(w http.ResponseWriter, r *http.Request) {
	url := r.FormValue(urlParam)
	if len(url) == 0 {
		http.Redirect(w, r, "/", 302)
		return
	}

	icons, e := fetchIcons(url)
	switch {
	case e != nil:
		renderHTMLTemplate(w, 404, iconsHTML, pageInfo{URL: url, Error: e})
	case len(icons) <= 0:
		errNoIcons := errors.New("this poor site has no icons at all :-(")
		renderHTMLTemplate(w, 404, iconsHTML, pageInfo{URL: url, Error: errNoIcons})
	default:
		renderHTMLTemplate(w, 200, iconsHTML, pageInfo{Icons: icons, URL: url})
	}
}

const (
	urlParam          = "url"
	feelingLuckyParam = "i_am_feeling_lucky"
	prettyParam       = "pretty"
	maxAge            = "max_age"
)

const defaultMaxAge = time.Duration(604800) * time.Second // 7 days

func apiHandler(w http.ResponseWriter, r *http.Request) {
	pretty := r.FormValue(prettyParam) == "yes"
	url := r.FormValue(urlParam)
	if len(url) == 0 {
		errMissingURL := errors.New("need url query parameter")
		writeAPIError(w, 400, errMissingURL, pretty)
		return
	}

	bestOnly := r.FormValue(feelingLuckyParam) == "yes"
	if bestOnly {
		icon, e := fetchBestIcon(url)
		if e != nil {
			writeAPIError(w, 404, e, pretty)
			return
		}

		http.Redirect(w, r, icon.URL, 302)
	} else {
		icons, e := fetchIcons(url)
		if e != nil {
			writeAPIError(w, 404, e, pretty)
			return
		}

		maxAge, err := time.ParseDuration(r.FormValue(maxAge))
		if err != nil || maxAge.Seconds() < 1 {
			maxAge = defaultMaxAge
		}
		writeAPIIcons(w, url, icons, maxAge, pretty)
	}
}

func fetchIcons(url string) ([]besticon.Icon, error) {
	fetchCount.Add(1)
	icons, err := besticon.FetchIcons(url, true)
	if err != nil {
		fetchErrors.Add(1)
	}
	return icons, err
}

func fetchBestIcon(url string) (*besticon.Icon, error) {
	fetchCount.Add(1)
	icon, err := besticon.FetchBestIcon(url, true)
	if err != nil {
		fetchErrors.Add(1)
	}
	return icon, err
}

func writeAPIError(w http.ResponseWriter, httpStatus int, e error, pretty bool) {
	data := struct {
		Error string `json:"error"`
	}{
		e.Error(),
	}

	if pretty {
		renderJSONResponsePretty(w, httpStatus, data)
	} else {
		renderJSONResponse(w, httpStatus, data)
	}
}

func writeAPIIcons(w http.ResponseWriter, url string, icons []besticon.Icon, maxAge time.Duration, pretty bool) {
	data := struct {
		URL   string          `json:"url"`
		Icons []besticon.Icon `json:"icons"`
	}{
		url,
		icons,
	}

	w.Header().Add(cacheControl, fmt.Sprintf("max-age=%d", int64(maxAge.Seconds())))
	if pretty {
		renderJSONResponsePretty(w, 200, data)
	} else {
		renderJSONResponse(w, 200, data)
	}
}

const (
	contentType     = "Content-Type"
	applicationJSON = "application/json"
	cacheControl    = "Cache-Control"
)

func renderJSONResponse(w http.ResponseWriter, httpStatus int, data interface{}) {
	w.Header().Add(contentType, applicationJSON)
	w.WriteHeader(httpStatus)
	enc := json.NewEncoder(w)
	enc.Encode(data)
}

func renderJSONResponsePretty(w http.ResponseWriter, httpStatus int, data interface{}) {
	w.Header().Add(contentType, applicationJSON)
	w.WriteHeader(httpStatus)
	b, _ := json.MarshalIndent(data, "", "  ")
	w.Write(b)
}

type pageInfo struct {
	URL   string
	Icons []besticon.Icon
	Error error
}

func (pi pageInfo) Host() string {
	u := pi.URL
	url, _ := url.Parse(u)
	if url != nil && url.Host != "" {
		return url.Host
	}
	return pi.URL
}

func (pi pageInfo) Best() string {
	if len(pi.Icons) > 0 {
		best := pi.Icons[0]
		return best.URL
	}
	return ""
}

func renderHTMLTemplate(w http.ResponseWriter, httpStatus int, templ *template.Template, data interface{}) {
	w.Header().Add(contentType, "text/html; charset=utf-8")
	w.WriteHeader(httpStatus)

	err := templ.Execute(w, data)
	if err != nil {
		err = fmt.Errorf("server: could not generate output: %s", err)
		logger.Print(err)
		w.Write([]byte(err.Error()))
	}
}

func startServer(port int) {
	httpHandle("/", indexHandler)
	httpHandle("/icons", iconsHandler)
	httpHandle("/api/icons", apiHandler)

	serveAsset("/pure-0.5.0-min.css", "besticon/iconserver/assets/pure-0.5.0-min.css", oneYear)
	serveAsset("/grids-responsive-0.5.0-min.css", "besticon/iconserver/assets/grids-responsive-0.5.0-min.css", oneYear)
	serveAsset("/main-min.css", "besticon/iconserver/assets/main-min.css", oneYear)

	serveAsset("/icon.svg", "besticon/iconserver/assets/icon.svg", oneYear)
	serveAsset("/favicon.ico", "besticon/iconserver/assets/favicon.ico", oneYear)
	serveAsset("/apple-touch-icon.png", "besticon/iconserver/assets/apple-touch-icon.png", oneYear)
	serveAsset("/robots.txt", "besticon/iconserver/assets/robots.txt", nocache)

	logger.Print("Starting server on port ", port, "...")
	e := http.ListenAndServe(":"+strconv.Itoa(port), newLoggingMux())
	if e != nil {
		logger.Fatalf("cannot start server: %s\n", e)
	}
}

const (
	oneYear = 365 * 24 * 3600
	nocache = -1
)

func serveAsset(path string, assetPath string, maxAgeSeconds int) {
	httpHandle(path, func(w http.ResponseWriter, r *http.Request) {
		assetInfo, err := assets.AssetInfo(assetPath)
		if err != nil {
			panic(err)
		}

		if maxAgeSeconds != nocache {
			w.Header().Add(cacheControl, fmt.Sprintf("max-age=%d", maxAgeSeconds))
		}

		http.ServeContent(w, r, assetInfo.Name(), assetInfo.ModTime(),
			bytes.NewReader(assets.MustAsset(assetPath)))
	})
}

func httpHandle(path string, f http.HandlerFunc) {
	http.Handle(path, gziphandler.GzipHandler(http.HandlerFunc(f)))
}

func main() {
	port := flag.Int("port", 0, "Port in server mode")
	flag.Parse()

	if *port > 0 {
		startServer(*port)
	} else {
		flag.PrintDefaults()
	}
}

func init() {
	indexHTML = templateFromAsset("besticon/iconserver/assets/index.html", "index.html")
	iconsHTML = templateFromAsset("besticon/iconserver/assets/icons.html", "icons.html")
	notFoundHTML = templateFromAsset("besticon/iconserver/assets/not_found.html", "not_found.html")
}

func templateFromAsset(assetPath, templateName string) *template.Template {
	bytes := assets.MustAsset(assetPath)
	return template.Must(template.New(templateName).Funcs(funcMap).Parse(string(bytes)))
}

var indexHTML *template.Template
var iconsHTML *template.Template
var notFoundHTML *template.Template

var funcMap = template.FuncMap{
	"ImgWidth": imgWidth,
}

func imgWidth(i *besticon.Icon) int {
	return i.Width / 2.0
}

func init() {
	besticon.SetCacheMaxSize(64)

	expvar.Publish("cacheBytes", expvar.Func(func() interface{} { return besticon.GetCacheStats().Bytes }))
	expvar.Publish("cacheItems", expvar.Func(func() interface{} { return besticon.GetCacheStats().Items }))
	expvar.Publish("cacheGets", expvar.Func(func() interface{} { return besticon.GetCacheStats().Gets }))
	expvar.Publish("cacheHits", expvar.Func(func() interface{} { return besticon.GetCacheStats().Hits }))
	expvar.Publish("cacheEvictions", expvar.Func(func() interface{} { return besticon.GetCacheStats().Evictions }))

	ticker := time.NewTicker(time.Minute * 1)
	go func() {
		for range ticker.C {
			logger.Printf("Cache: %+v", besticon.GetCacheStats())
		}
	}()
}
<|endoftext|>