<commit_before>package game

import (
	"github.com/MichaelDiBernardo/srl/lib/math"
	"testing"
)

var (
	actorTestSpec = &Spec{
		Family:  FamActor,
		Genus:   GenMonster,
		Species: "TestSpecies",
		Name:    "Hi",
		Traits: &Traits{
			Mover:    NewActorMover,
			Packer:   NewActorPacker,
			Equipper: NewActorEquipper,
		},
	}

	actorTestItemSpec = &Spec{
		Family:  FamItem,
		Genus:   GenEquip,
		Species: "testspec",
		Name:    "Item",
		Traits:  &Traits{},
	}
)

func TestOkMove(t *testing.T) {
	g := NewGame()
	l := NewLevel(4, 4, g, IdentLevel)
	obj := g.NewObj(actorTestSpec)
	startpos := math.Pt(1, 1)

	l.Place(obj, startpos)

	ok := obj.Mover.Move(math.Pt(1, 0))

	if !ok {
		t.Error(`Move( (1, 0)) = false, want true`)
	}

	newpos := obj.Pos()
	want := math.Pt(2, 1)
	if newpos != want {
		t.Errorf(`Move((1, 0)) = %v, want %v`, newpos, want)
	}

	if l.At(startpos).Actor != nil {
		t.Error(`Move((1, 0)) did not set start tile actor to nil`)
	}
	if l.At(newpos).Actor != obj {
		t.Error(`Move((1, 0)) did not set dest tile actor to obj`)
	}
}

func TestActorCollision(t *testing.T) {
	g := NewGame()
	l := NewLevel(4, 4, g, IdentLevel)
	a1, a2 := g.NewObj(actorTestSpec), g.NewObj(actorTestSpec)
	l.Place(a1, math.Pt(1, 1))
	l.Place(a2, math.Pt(2, 1))

	ok := a1.Mover.Move(math.Pt(1, 0))

	if ok {
		t.Error(`a1.Move( (1, 0)) = true, want false`)
	}
}

func TestPlayerMaxHPCalc(t *testing.T) {
	g := NewGame()
	obj := g.NewObj(PlayerSpec)
	obj.Stats = &stats{Trait: Trait{obj: obj}, vit: 1}
	obj.Sheet = &PlayerSheet{Trait: Trait{obj: obj}}

	if maxhp, want := obj.Sheet.MaxHP(), 20; maxhp != want {
		t.Errorf(`MaxHP() was %d, want %d`, maxhp, want)
	}
}

func TestPlayerMaxMPCalc(t *testing.T) {
	g := NewGame()
	obj := g.NewObj(PlayerSpec)
	obj.Stats = &stats{Trait: Trait{obj: obj}, mnd: 2}
	obj.Sheet = &PlayerSheet{Trait: Trait{obj: obj}}

	if maxmp, want := obj.Sheet.MaxMP(), 30; maxmp != want {
		t.Errorf(`MaxMP() was %d, want %d`, maxmp, want)
	}
}

type fakefighter struct {
	Trait
	Called bool
}

func (f *fakefighter) Hit(other Fighter) {
	f.Called = true
}

func (_ *fakefighter) MeleeRoll() int {
	return 0
}

func (_ *fakefighter) EvasionRoll() int {
	return 0
}

func (_ *fakefighter) DamRoll() int {
	return 0
}

func (_ *fakefighter) ProtRoll() int {
	return 0
}

func TestPlayerMonsterCollisionsHit(t *testing.T) {
	g := NewGame()
	player := g.NewObj(PlayerSpec)
	pf := &fakefighter{Trait: Trait{obj: player}}
	player.Fighter = pf

	monster := g.NewObj(actorTestSpec)
	mf := &fakefighter{Trait: Trait{obj: player}}
	monster.Fighter = mf

	l := NewLevel(4, 4, nil, IdentLevel)
	l.Place(player, math.Pt(0, 0))
	l.Place(monster, math.Pt(1, 1))

	player.Mover.Move(math.Pt(1, 1))

	if !pf.Called {
		t.Error("Moving player into other did not try to hit.")
	}

	monster.Mover.Move(math.Pt(-1, -1))

	if !mf.Called {
		t.Error("Moving other into player did not try to hit.")
	}
}

func TestMonsterMonsterCollisionsHit(t *testing.T) {
	g := NewGame()
	mon1 := g.NewObj(actorTestSpec)
	mf1 := &fakefighter{Trait: Trait{obj: mon1}}
	mon1.Fighter = mf1

	mon2 := g.NewObj(actorTestSpec)
	mf2 := &fakefighter{Trait: Trait{obj: mon2}}
	mon2.Fighter = mf2

	l := NewLevel(4, 4, nil, IdentLevel)
	l.Place(mon1, math.Pt(0, 0))
	l.Place(mon2, math.Pt(1, 1))

	mon1.Mover.Move(math.Pt(1, 1))

	if mf1.Called {
		t.Error("Moving monster into monster tried to hit.")
	}
}

func TestTryPickupNoItemsOnGround(t *testing.T) {
	g := NewGame()
	taker := g.NewObj(actorTestSpec)

	l := NewLevel(4, 4, nil, IdentLevel)
	l.Place(taker, math.Pt(0, 0))

	taker.Packer.TryPickup()
	if size := taker.Packer.Inventory().Len(); size > 0 {
		t.Errorf(`TryPickup() on empty square gave inven size %d; want 0`, size)
	}
}

func TestTryPickupOneItemOnGround(t *testing.T) {
	g := NewGame()
	taker := g.NewObj(actorTestSpec)
	item := g.NewObj(actorTestItemSpec)

	l := NewLevel(4, 4, nil, IdentLevel)
	l.Place(taker, math.Pt(0, 0))
	l.Place(item, math.Pt(0, 0))

	taker.Packer.TryPickup()
	if size := taker.Packer.Inventory().Len(); size != 1 {
		t.Errorf(`TryPickup() on 1-item square gave inven size %d; want 1`, size)
	}
	if size := l.At(math.Pt(0, 0)).Items.Len(); size != 0 {
		t.Errorf(`TryPickup() on 1-item square left %d items on ground; want 0`, size)
	}
}

func TestTryPickupFromStack(t *testing.T) {
	g := NewGame()
	taker := g.NewObj(actorTestSpec)
	item := g.NewObj(actorTestItemSpec)
	item2 := g.NewObj(actorTestItemSpec)

	l := NewLevel(4, 4, nil, IdentLevel)
	l.Place(taker, math.Pt(0, 0))
	l.Place(item, math.Pt(0, 0))
	l.Place(item2, math.Pt(0, 0))

	taker.Packer.TryPickup()
	if size := taker.Packer.Inventory().Len(); size != 0 {
		t.Errorf(`TryPickup() on stack took something instead of opening menu; took %d things`, size)
	}
	if size := l.At(math.Pt(0, 0)).Items.Len(); size != 2 {
		t.Errorf(`TryPickup() took from ground instead of opening menu; left %d things`, size)
	}
	if size := g.Events.Len(); size != 1 {
		t.Errorf(`TryPickup() pushed wrong # of events to queue; found %d, want 1`, size)
	}

	e, ok := g.Events.Next().(ModeEvent)
	if !ok {
		t.Error(`TryPickup pushed wrong event type to queue.`)
	}
	if e.Mode != ModePickup {
		t.Errorf(`TryPickup switched to mode %v, want %v`, e.Mode, ModePickup)
	}

}

func TestTryEquipWithNoEquipsInInventory(t *testing.T) {
	g := NewGame()
	equipper := g.NewObj(actorTestSpec)
	equipper.Equipper.TryEquip()
	if mode := g.mode; mode != ModeHud {
		t.Errorf(`TryEquip w no equips switched to mode %v, want %v`, mode, ModeHud)
	}
}

func TestTryEquipWithEquipsInInventory(t *testing.T) {
	g := NewGame()

	equipper := g.NewObj(actorTestSpec)
	equipper.Equipper.TryEquip()

	equip := g.NewObj(actorTestItemSpec)
	equipper.Packer.Inventory().Add(equip)

	equipper.Equipper.TryEquip()

	if mode := g.mode; mode != ModeEquip {
		t.Errorf(`TryEquip switched to mode %v, want %v`, mode, ModeEquip)
	}
}
<commit_msg>Testing `Equip`<commit_after>package game

import (
	"github.com/MichaelDiBernardo/srl/lib/math"
	"testing"
)

var (
	actorTestSpec = &Spec{
		Family:  FamActor,
		Genus:   GenMonster,
		Species: "TestSpecies",
		Name:    "Hi",
		Traits: &Traits{
			Mover:    NewActorMover,
			Packer:   NewActorPacker,
			Equipper: NewActorEquipper,
		},
	}

	actorTestItemSpec = &Spec{
		Family:  FamItem,
		Genus:   GenEquip,
		Species: "testspec",
		Name:    "Item",
		Traits: &Traits{
			Equip: NewEquip(Equip{Slot: SlotHand}),
		},
	}
)

func TestOkMove(t *testing.T) {
	g := NewGame()
	l := NewLevel(4, 4, g, IdentLevel)
	obj := g.NewObj(actorTestSpec)
	startpos := math.Pt(1, 1)

	l.Place(obj, startpos)

	ok := obj.Mover.Move(math.Pt(1, 0))

	if !ok {
		t.Error(`Move( (1, 0)) = false, want true`)
	}

	newpos := obj.Pos()
	want := math.Pt(2, 1)
	if newpos != want {
		t.Errorf(`Move((1, 0)) = %v, want %v`, newpos, want)
	}

	if l.At(startpos).Actor != nil {
		t.Error(`Move((1, 0)) did not set start tile actor to nil`)
	}
	if l.At(newpos).Actor != obj {
		t.Error(`Move((1, 0)) did not set dest tile actor to obj`)
	}
}

func TestActorCollision(t *testing.T) {
	g := NewGame()
	l := NewLevel(4, 4, g, IdentLevel)
	a1, a2 := g.NewObj(actorTestSpec), g.NewObj(actorTestSpec)
	l.Place(a1, math.Pt(1, 1))
	l.Place(a2, math.Pt(2, 1))

	ok := a1.Mover.Move(math.Pt(1, 0))

	if ok {
		t.Error(`a1.Move( (1, 0)) = true, want false`)
	}
}

func TestPlayerMaxHPCalc(t *testing.T) {
	g := NewGame()
	obj := g.NewObj(PlayerSpec)
	obj.Stats = &stats{Trait: Trait{obj: obj}, vit: 1}
	obj.Sheet = &PlayerSheet{Trait: Trait{obj: obj}}

	if maxhp, want := obj.Sheet.MaxHP(), 20; maxhp != want {
		t.Errorf(`MaxHP() was %d, want %d`, maxhp, want)
	}
}

func TestPlayerMaxMPCalc(t *testing.T) {
	g := NewGame()
	obj := g.NewObj(PlayerSpec)
	obj.Stats = &stats{Trait: Trait{obj: obj}, mnd: 2}
	obj.Sheet = &PlayerSheet{Trait: Trait{obj: obj}}

	if maxmp, want := obj.Sheet.MaxMP(), 30; maxmp != want {
		t.Errorf(`MaxMP() was %d, want %d`, maxmp, want)
	}
}

type fakefighter struct {
	Trait
	Called bool
}

func (f *fakefighter) Hit(other Fighter) {
	f.Called = true
}

func (_ *fakefighter) MeleeRoll() int {
	return 0
}

func (_ *fakefighter) EvasionRoll() int {
	return 0
}

func (_ *fakefighter) DamRoll() int {
	return 0
}

func (_ *fakefighter) ProtRoll() int {
	return 0
}

func TestPlayerMonsterCollisionsHit(t *testing.T) {
	g := NewGame()
	player := g.NewObj(PlayerSpec)
	pf := &fakefighter{Trait: Trait{obj: player}}
	player.Fighter = pf

	monster := g.NewObj(actorTestSpec)
	mf := &fakefighter{Trait: Trait{obj: player}}
	monster.Fighter = mf

	l := NewLevel(4, 4, nil, IdentLevel)
	l.Place(player, math.Pt(0, 0))
	l.Place(monster, math.Pt(1, 1))

	player.Mover.Move(math.Pt(1, 1))

	if !pf.Called {
		t.Error("Moving player into other did not try to hit.")
	}

	monster.Mover.Move(math.Pt(-1, -1))

	if !mf.Called {
		t.Error("Moving other into player did not try to hit.")
	}
}

func TestMonsterMonsterCollisionsHit(t *testing.T) {
	g := NewGame()
	mon1 := g.NewObj(actorTestSpec)
	mf1 := &fakefighter{Trait: Trait{obj: mon1}}
	mon1.Fighter = mf1

	mon2 := g.NewObj(actorTestSpec)
	mf2 := &fakefighter{Trait: Trait{obj: mon2}}
	mon2.Fighter = mf2

	l := NewLevel(4, 4, nil, IdentLevel)
	l.Place(mon1, math.Pt(0, 0))
	l.Place(mon2, math.Pt(1, 1))

	mon1.Mover.Move(math.Pt(1, 1))

	if mf1.Called {
		t.Error("Moving monster into monster tried to hit.")
	}
}

func TestTryPickupNoItemsOnGround(t *testing.T) {
	g := NewGame()
	taker := g.NewObj(actorTestSpec)

	l := NewLevel(4, 4, nil, IdentLevel)
	l.Place(taker, math.Pt(0, 0))

	taker.Packer.TryPickup()
	if size := taker.Packer.Inventory().Len(); size > 0 {
		t.Errorf(`TryPickup() on empty square gave inven size %d; want 0`, size)
	}
}

func TestTryPickupOneItemOnGround(t *testing.T) {
	g := NewGame()
	taker := g.NewObj(actorTestSpec)
	item := g.NewObj(actorTestItemSpec)

	l := NewLevel(4, 4, nil, IdentLevel)
	l.Place(taker, math.Pt(0, 0))
	l.Place(item, math.Pt(0, 0))

	taker.Packer.TryPickup()
	if size := taker.Packer.Inventory().Len(); size != 1 {
		t.Errorf(`TryPickup() on 1-item square gave inven size %d; want 1`, size)
	}
	if size := l.At(math.Pt(0, 0)).Items.Len(); size != 0 {
		t.Errorf(`TryPickup() on 1-item square left %d items on ground; want 0`, size)
	}
}

func TestTryPickupFromStack(t *testing.T) {
	g := NewGame()
	taker := g.NewObj(actorTestSpec)
	item := g.NewObj(actorTestItemSpec)
	item2 := g.NewObj(actorTestItemSpec)

	l := NewLevel(4, 4, nil, IdentLevel)
	l.Place(taker, math.Pt(0, 0))
	l.Place(item, math.Pt(0, 0))
	l.Place(item2, math.Pt(0, 0))

	taker.Packer.TryPickup()
	if size := taker.Packer.Inventory().Len(); size != 0 {
		t.Errorf(`TryPickup() on stack took something instead of opening menu; took %d things`, size)
	}
	if size := l.At(math.Pt(0, 0)).Items.Len(); size != 2 {
		t.Errorf(`TryPickup() took from ground instead of opening menu; left %d things`, size)
	}
	if size := g.Events.Len(); size != 1 {
		t.Errorf(`TryPickup() pushed wrong # of events to queue; found %d, want 1`, size)
	}

	e, ok := g.Events.Next().(ModeEvent)
	if !ok {
		t.Error(`TryPickup pushed wrong event type to queue.`)
	}
	if e.Mode != ModePickup {
		t.Errorf(`TryPickup switched to mode %v, want %v`, e.Mode, ModePickup)
	}

}

func TestTryEquipWithNoEquipsInInventory(t *testing.T) {
	g := NewGame()
	equipper := g.NewObj(actorTestSpec)
	equipper.Equipper.TryEquip()
	if mode := g.mode; mode != ModeHud {
		t.Errorf(`TryEquip w no equips switched to mode %v, want %v`, mode, ModeHud)
	}
}

func TestTryEquipWithEquipsInInventory(t *testing.T) {
	g := NewGame()

	equipper := g.NewObj(actorTestSpec)
	equipper.Equipper.TryEquip()

	equip := g.NewObj(actorTestItemSpec)
	equipper.Packer.Inventory().Add(equip)

	equipper.Equipper.TryEquip()

	if mode := g.mode; mode != ModeEquip {
		t.Errorf(`TryEquip switched to mode %v, want %v`, mode, ModeEquip)
	}
}

func TestEquipIntoEmptySlot(t *testing.T) {
	g := NewGame()

	equipper := g.NewObj(actorTestSpec)
	equipper.Equipper.TryEquip()

	equip := g.NewObj(actorTestItemSpec)
	inv := equipper.Packer.Inventory()
	inv.Add(equip)

	equipper.Equipper.TryEquip()
	equipper.Equipper.Equip(0)

	if mode := g.mode; mode != ModeHud {
		t.Errorf(`Was mode %v after equip; want %v`, mode, ModeHud)
	}

	if !inv.Empty() {
		t.Errorf(`Item did not leave inventory after equipping.`)
	}

	slot := equip.Equip.Slot
	if equipped := equipper.Equipper.(*ActorEquipper).body.Slots[slot]; equipped != equip {
		t.Errorf(`Equipped item was %v, want %v`, equipped, equip)
	}
}

func TestEquipIntoOccupiedSlot(t *testing.T) {
	g := NewGame()

	equipper := g.NewObj(actorTestSpec)
	equipper.Equipper.TryEquip()

	equip1 := g.NewObj(actorTestItemSpec)
	equip2 := g.NewObj(actorTestItemSpec)

	inv := equipper.Packer.Inventory()
	inv.Add(equip1)
	inv.Add(equip2)

	// Wield equip1
	equipper.Equipper.TryEquip()
	equipper.Equipper.Equip(0)
	// Wield equip2, swapping out equip1
	equipper.Equipper.TryEquip()
	equipper.Equipper.Equip(0)

	if swapped := inv.Top(); swapped != equip1 {
		t.Errorf(`First wield was not swapped out; got %v, want %v.`, swapped, equip1)
	}

	slot := equip2.Equip.Slot
	if equipped := equipper.Equipper.(*ActorEquipper).body.Slots[slot]; equipped != equip2 {
		t.Errorf(`Equipped item was %v, want %v`, equipped, equip2)
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"sort"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/autoscaling"
	"github.com/hashicorp/terraform/helper/schema"
)

func dataSourceAwsAutoscalingGroups() *schema.Resource {
	return &schema.Resource{
		Read: dataSourceAwsAutoscalingGroupsRead,

		Schema: map[string]*schema.Schema{
			"names": {
				Type:     schema.TypeList,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
			"arns": {
				Type:     schema.TypeList,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
			"filter": {
				Type:     schema.TypeSet,
				Optional: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": {
							Type:     schema.TypeString,
							Required: true,
						},
						"values": {
							Type:     schema.TypeSet,
							Required: true,
							Elem:     &schema.Schema{Type: schema.TypeString},
							Set:      schema.HashString,
						},
					},
				},
			},
		},
	}
}

func dataSourceAwsAutoscalingGroupsRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).autoscalingconn

	log.Printf("[DEBUG] Reading Autoscaling Groups.")
	d.SetId(time.Now().UTC().String())

	var rawName []string
	var rawArn []string
	var err error

	tf := d.Get("filter").(*schema.Set)
	if tf.Len() > 0 {
		input := &autoscaling.DescribeTagsInput{
			Filters: expandAsgTagFilters(tf.List()),
		}
		err = conn.DescribeTagsPages(input, func(resp *autoscaling.DescribeTagsOutput, lastPage bool) bool {
			for _, v := range resp.Tags {
				rawName = append(rawName, aws.StringValue(v.ResourceId))
			}
			return !lastPage
		})

		maxAutoScalingGroupNames := 1600
		for i := 0; i < len(rawName); i += maxAutoScalingGroupNames {
			end := i + maxAutoScalingGroupNames

			if end > len(rawName) {
				end = len(rawName)
			}

			nameInput := &autoscaling.DescribeAutoScalingGroupsInput{
				AutoScalingGroupNames: aws.StringSlice(rawName[i:end]),
				MaxRecords:            aws.Int64(100),
			}

			err = conn.DescribeAutoScalingGroupsPages(nameInput, func(resp *autoscaling.DescribeAutoScalingGroupsOutput, lastPage bool) bool {
				for _, group := range resp.AutoScalingGroups {
					rawArn = append(rawArn, aws.StringValue(group.AutoScalingGroupARN))
				}
				return !lastPage
			})
		}
	} else {
		err = conn.DescribeAutoScalingGroupsPages(&autoscaling.DescribeAutoScalingGroupsInput{}, func(resp *autoscaling.DescribeAutoScalingGroupsOutput, lastPage bool) bool {
			for _, group := range resp.AutoScalingGroups {
				rawName = append(rawName, aws.StringValue(group.AutoScalingGroupName))
				rawArn = append(rawArn, aws.StringValue(group.AutoScalingGroupARN))
			}
			return !lastPage
		})
	}
	if err != nil {
		return fmt.Errorf("Error fetching Autoscaling Groups: %s", err)
	}

	sort.Strings(rawName)
	sort.Strings(rawArn)

  if err := d.Set("names", rawName); err != nil {
		return fmt.Errorf("[WARN] Error setting Autoscaling Group Names: %s", err)
	}

	if err := d.Set("arns", rawArn); err != nil {
		return fmt.Errorf("[WARN] Error setting Autoscaling Group Arns: %s", err)
	}

	return nil

}

func expandAsgTagFilters(in []interface{}) []*autoscaling.Filter {
	out := make([]*autoscaling.Filter, len(in), len(in))
	for i, filter := range in {
		m := filter.(map[string]interface{})
		values := expandStringList(m["values"].(*schema.Set).List())

		out[i] = &autoscaling.Filter{
			Name:   aws.String(m["name"].(string)),
			Values: values,
		}
	}
	return out
}
<commit_msg>correct fmt<commit_after>package aws

import (
	"fmt"
	"log"
	"sort"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/autoscaling"
	"github.com/hashicorp/terraform/helper/schema"
)

func dataSourceAwsAutoscalingGroups() *schema.Resource {
	return &schema.Resource{
		Read: dataSourceAwsAutoscalingGroupsRead,

		Schema: map[string]*schema.Schema{
			"names": {
				Type:     schema.TypeList,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
			"arns": {
				Type:     schema.TypeList,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
			"filter": {
				Type:     schema.TypeSet,
				Optional: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": {
							Type:     schema.TypeString,
							Required: true,
						},
						"values": {
							Type:     schema.TypeSet,
							Required: true,
							Elem:     &schema.Schema{Type: schema.TypeString},
							Set:      schema.HashString,
						},
					},
				},
			},
		},
	}
}

func dataSourceAwsAutoscalingGroupsRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).autoscalingconn

	log.Printf("[DEBUG] Reading Autoscaling Groups.")
	d.SetId(time.Now().UTC().String())

	var rawName []string
	var rawArn []string
	var err error

	tf := d.Get("filter").(*schema.Set)
	if tf.Len() > 0 {
		input := &autoscaling.DescribeTagsInput{
			Filters: expandAsgTagFilters(tf.List()),
		}
		err = conn.DescribeTagsPages(input, func(resp *autoscaling.DescribeTagsOutput, lastPage bool) bool {
			for _, v := range resp.Tags {
				rawName = append(rawName, aws.StringValue(v.ResourceId))
			}
			return !lastPage
		})

		maxAutoScalingGroupNames := 1600
		for i := 0; i < len(rawName); i += maxAutoScalingGroupNames {
			end := i + maxAutoScalingGroupNames

			if end > len(rawName) {
				end = len(rawName)
			}

			nameInput := &autoscaling.DescribeAutoScalingGroupsInput{
				AutoScalingGroupNames: aws.StringSlice(rawName[i:end]),
				MaxRecords:            aws.Int64(100),
			}

			err = conn.DescribeAutoScalingGroupsPages(nameInput, func(resp *autoscaling.DescribeAutoScalingGroupsOutput, lastPage bool) bool {
				for _, group := range resp.AutoScalingGroups {
					rawArn = append(rawArn, aws.StringValue(group.AutoScalingGroupARN))
				}
				return !lastPage
			})
		}
	} else {
		err = conn.DescribeAutoScalingGroupsPages(&autoscaling.DescribeAutoScalingGroupsInput{}, func(resp *autoscaling.DescribeAutoScalingGroupsOutput, lastPage bool) bool {
			for _, group := range resp.AutoScalingGroups {
				rawName = append(rawName, aws.StringValue(group.AutoScalingGroupName))
				rawArn = append(rawArn, aws.StringValue(group.AutoScalingGroupARN))
			}
			return !lastPage
		})
	}
	if err != nil {
		return fmt.Errorf("Error fetching Autoscaling Groups: %s", err)
	}

	sort.Strings(rawName)
	sort.Strings(rawArn)

	if err := d.Set("names", rawName); err != nil {
		return fmt.Errorf("[WARN] Error setting Autoscaling Group Names: %s", err)
	}

	if err := d.Set("arns", rawArn); err != nil {
		return fmt.Errorf("[WARN] Error setting Autoscaling Group Arns: %s", err)
	}

	return nil

}

func expandAsgTagFilters(in []interface{}) []*autoscaling.Filter {
	out := make([]*autoscaling.Filter, len(in), len(in))
	for i, filter := range in {
		m := filter.(map[string]interface{})
		values := expandStringList(m["values"].(*schema.Set).List())

		out[i] = &autoscaling.Filter{
			Name:   aws.String(m["name"].(string)),
			Values: values,
		}
	}
	return out
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"regexp"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ssoadmin"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
)

func dataSourceAwsSsoPermissionSet() *schema.Resource {
	return &schema.Resource{
		Read: dataSourceAwsSsoPermissionSetRead,

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"created_date": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"instance_arn": {
				Type:     schema.TypeString,
				Required: true,
				ValidateFunc: validation.All(
					validation.StringLenBetween(10, 1224),
					validation.StringMatch(regexp.MustCompile(`^arn:aws:sso:::instance/(sso)?ins-[a-zA-Z0-9-.]{16}$`), "must match arn:aws:sso:::instance/(sso)?ins-[a-zA-Z0-9-.]{16}"),
				),
			},

			"name": {
				Type:     schema.TypeString,
				Required: true,
				ValidateFunc: validation.All(
					validation.StringLenBetween(1, 32),
					validation.StringMatch(regexp.MustCompile(`^[\w+=,.@-]+$`), "must match [\\w+=,.@-]"),
				),
			},

			"description": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"session_duration": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"relay_state": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"inline_policy": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"managed_policy_arns": {
				Type:     schema.TypeSet,
				Computed: true,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
			},

			"tags": tagsSchemaComputed(),
		},
	}
}

func dataSourceAwsSsoPermissionSetRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ssoadminconn
	ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig

	instanceArn := d.Get("instance_arn").(string)
	name := d.Get("name").(string)

	log.Printf("[DEBUG] Reading AWS SSO Permission Sets")
	resp, err := conn.ListPermissionSets(&ssoadmin.ListPermissionSetsInput{
		InstanceArn: aws.String(instanceArn),
		MaxResults:  aws.Int64(100),
	})
	if err != nil {
		return fmt.Errorf("Error getting AWS SSO Permission Sets: %s", err)
	}
	if resp == nil || len(resp.PermissionSets) == 0 {
		log.Printf("[DEBUG] No AWS SSO Permission Sets found")
		d.SetId("")
		return nil
	}

	// TODO: paging (if resp.NextToken != nil)
	var permissionSetArn string
	var permissionSet *ssoadmin.PermissionSet
	for _, permissionSetArns := range resp.PermissionSets {
		permissionSetArn = aws.StringValue(permissionSetArns)
		log.Printf("[DEBUG] Reading AWS SSO Permission Set: %v", permissionSetArn)
		permissionSetResp, permissionSetErr := conn.DescribePermissionSet(&ssoadmin.DescribePermissionSetInput{
			InstanceArn:      aws.String(instanceArn),
			PermissionSetArn: aws.String(permissionSetArn),
		})
		if permissionSetErr != nil {
			return fmt.Errorf("Error getting AWS SSO Permission Set: %s", permissionSetErr)
		}
		if aws.StringValue(permissionSetResp.PermissionSet.Name) == name {
			permissionSet = permissionSetResp.PermissionSet
			break
		}
	}

	if permissionSet == nil {
		log.Printf("[DEBUG] AWS SSO Permission Set %v not found", name)
		d.SetId("")
		return nil
	}

	log.Printf("[DEBUG] Found AWS SSO Permission Set: %s", permissionSet)

	log.Printf("[DEBUG] Getting Inline Policy for AWS SSO Permission Set")
	inlinePolicyResp, inlinePolicyErr := conn.GetInlinePolicyForPermissionSet(&ssoadmin.GetInlinePolicyForPermissionSetInput{
		InstanceArn:      aws.String(instanceArn),
		PermissionSetArn: aws.String(permissionSetArn),
	})
	if inlinePolicyErr != nil {
		return fmt.Errorf("Error getting Inline Policy for AWS SSO Permission Set: %s", inlinePolicyErr)
	}

	log.Printf("[DEBUG] Getting Managed Policies for AWS SSO Permission Set")
	managedPoliciesResp, managedPoliciesErr := conn.ListManagedPoliciesInPermissionSet(&ssoadmin.ListManagedPoliciesInPermissionSetInput{
		InstanceArn:      aws.String(instanceArn),
		PermissionSetArn: aws.String(permissionSetArn),
	})
	if managedPoliciesErr != nil {
		return fmt.Errorf("Error getting Managed Policies for AWS SSO Permission Set: %s", managedPoliciesErr)
	}
	var managedPolicyArns []string
	for _, managedPolicy := range managedPoliciesResp.AttachedManagedPolicies {
		managedPolicyArns = append(managedPolicyArns, aws.StringValue(managedPolicy.Arn))
	}

	tags, err := keyvaluetags.SsoListTags(conn, permissionSetArn, instanceArn)
	if err != nil {
		return fmt.Errorf("Error listing tags for ASW SSO Permission Set (%s): %s", permissionSetArn, err)
	}

	d.SetId(permissionSetArn)
	d.Set("arn", permissionSetArn)
	d.Set("created_date", permissionSet.CreatedDate.Format(time.RFC3339))
	d.Set("instance_arn", instanceArn)
	d.Set("name", permissionSet.Name)
	d.Set("description", permissionSet.Description)
	d.Set("session_duration", permissionSet.SessionDuration)
	d.Set("relay_state", permissionSet.RelayState)
	d.Set("inline_policy", inlinePolicyResp.InlinePolicy)
	d.Set("managed_policy_arns", managedPolicyArns)
	if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil {
		return fmt.Errorf("error setting tags: %s", err)
	}

	return nil
}
<commit_msg>update to use paging with data.aws_sso_permission_set<commit_after>package aws

import (
	"fmt"
	"log"
	"regexp"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ssoadmin"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
)

func dataSourceAwsSsoPermissionSet() *schema.Resource {
	return &schema.Resource{
		Read: dataSourceAwsSsoPermissionSetRead,

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"created_date": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"instance_arn": {
				Type:     schema.TypeString,
				Required: true,
				ValidateFunc: validation.All(
					validation.StringLenBetween(10, 1224),
					validation.StringMatch(regexp.MustCompile(`^arn:aws:sso:::instance/(sso)?ins-[a-zA-Z0-9-.]{16}$`), "must match arn:aws:sso:::instance/(sso)?ins-[a-zA-Z0-9-.]{16}"),
				),
			},

			"name": {
				Type:     schema.TypeString,
				Required: true,
				ValidateFunc: validation.All(
					validation.StringLenBetween(1, 32),
					validation.StringMatch(regexp.MustCompile(`^[\w+=,.@-]+$`), "must match [\\w+=,.@-]"),
				),
			},

			"description": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"session_duration": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"relay_state": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"inline_policy": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"managed_policy_arns": {
				Type:     schema.TypeSet,
				Computed: true,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
			},

			"tags": tagsSchemaComputed(),
		},
	}
}

func dataSourceAwsSsoPermissionSetRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ssoadminconn
	ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig

	instanceArn := d.Get("instance_arn").(string)
	name := d.Get("name").(string)

	log.Printf("[DEBUG] Reading AWS SSO Permission Sets")

	var permissionSetArn string
	var permissionSet *ssoadmin.PermissionSet
	var permissionSetErr error

	req := &ssoadmin.ListPermissionSetsInput{
		InstanceArn: aws.String(instanceArn),
	}
	err := conn.ListPermissionSetsPages(req, func(page *ssoadmin.ListPermissionSetsOutput, lastPage bool) bool {
		if page != nil && len(page.PermissionSets) != 0 {
			for _, ps := range page.PermissionSets {
				permissionSetArn = aws.StringValue(ps)
				log.Printf("[DEBUG] Reading AWS SSO Permission Set: %v", permissionSetArn)
				var permissionSetResp *ssoadmin.DescribePermissionSetOutput
				permissionSetResp, permissionSetErr = conn.DescribePermissionSet(&ssoadmin.DescribePermissionSetInput{
					InstanceArn:      aws.String(instanceArn),
					PermissionSetArn: aws.String(permissionSetArn),
				})
				if permissionSetErr != nil {
					return false
				}
				if aws.StringValue(permissionSetResp.PermissionSet.Name) == name {
					permissionSet = permissionSetResp.PermissionSet
					return false
				}
			}
		}
		return !lastPage
	})

	if err != nil {
		return fmt.Errorf("Error getting AWS SSO Permission Sets: %s", err)
	}

	if permissionSetErr != nil {
		return fmt.Errorf("Error getting AWS SSO Permission Set: %s", permissionSetErr)
	}

	if permissionSet == nil {
		log.Printf("[DEBUG] AWS SSO Permission Set %v not found", name)
		d.SetId("")
		return nil
	}

	log.Printf("[DEBUG] Found AWS SSO Permission Set: %s", permissionSet)

	log.Printf("[DEBUG] Getting Inline Policy for AWS SSO Permission Set")
	inlinePolicyResp, inlinePolicyErr := conn.GetInlinePolicyForPermissionSet(&ssoadmin.GetInlinePolicyForPermissionSetInput{
		InstanceArn:      aws.String(instanceArn),
		PermissionSetArn: aws.String(permissionSetArn),
	})
	if inlinePolicyErr != nil {
		return fmt.Errorf("Error getting Inline Policy for AWS SSO Permission Set: %s", inlinePolicyErr)
	}

	log.Printf("[DEBUG] Getting Managed Policies for AWS SSO Permission Set")
	var managedPolicyArns []string
	managedPoliciesReq := &ssoadmin.ListManagedPoliciesInPermissionSetInput{
		InstanceArn:      aws.String(instanceArn),
		PermissionSetArn: aws.String(permissionSetArn),
	}
	managedPoliciesErr := conn.ListManagedPoliciesInPermissionSetPages(managedPoliciesReq, func(page *ssoadmin.ListManagedPoliciesInPermissionSetOutput, lastPage bool) bool {
		for _, managedPolicy := range page.AttachedManagedPolicies {
			managedPolicyArns = append(managedPolicyArns, aws.StringValue(managedPolicy.Arn))
		}
		return !lastPage
	})
	if managedPoliciesErr != nil {
		return fmt.Errorf("Error getting Managed Policies for AWS SSO Permission Set: %s", managedPoliciesErr)
	}

	tags, tagsErr := keyvaluetags.SsoListTags(conn, permissionSetArn, instanceArn)
	if tagsErr != nil {
		return fmt.Errorf("Error listing tags for ASW SSO Permission Set (%s): %s", permissionSetArn, tagsErr)
	}

	d.SetId(permissionSetArn)
	d.Set("arn", permissionSetArn)
	d.Set("created_date", permissionSet.CreatedDate.Format(time.RFC3339))
	d.Set("instance_arn", instanceArn)
	d.Set("name", permissionSet.Name)
	d.Set("description", permissionSet.Description)
	d.Set("session_duration", permissionSet.SessionDuration)
	d.Set("relay_state", permissionSet.RelayState)
	d.Set("inline_policy", inlinePolicyResp.InlinePolicy)
	d.Set("managed_policy_arns", managedPolicyArns)
	tagsMapErr := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map())
	if tagsMapErr != nil {
		return fmt.Errorf("Error setting tags: %s", tagsMapErr)
	}

	return nil
}
<|endoftext|>


<commit_before>package resource_test

import (
	"database/sql/driver"
	"fmt"
	"reflect"
	"testing"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/qor/admin"
	"github.com/qor/publish2"
	"github.com/qor/qor"
	"github.com/qor/qor/resource"
	testutils "github.com/qor/qor/test/utils"
	"github.com/qor/qor/utils"
	"github.com/qor/sorting"
)

func format(value interface{}) string {
	return fmt.Sprint(utils.Indirect(reflect.ValueOf(value)).Interface())
}

func checkMeta(record interface{}, meta *resource.Meta, value interface{}, t *testing.T, expectedValues ...string) {
	var (
		context       = &qor.Context{DB: testutils.TestDB()}
		metaValue     = &resource.MetaValue{Name: meta.Name, Value: value}
		expectedValue = fmt.Sprint(value)
	)

	for _, v := range expectedValues {
		expectedValue = v
	}

	meta.PreInitialize()
	meta.Initialize()

	if meta.Setter != nil {
		meta.Setter(record, metaValue, context)
		if context.HasError() {
			t.Errorf("No error should happen, but got %v", context.Errors)
		}

		result := meta.Valuer(record, context)
		if resultValuer, ok := result.(driver.Valuer); ok {
			if v, err := resultValuer.Value(); err == nil {
				result = v
			}
		}

		if format(result) != expectedValue {
			t.Errorf("Wrong value, should be %v, but got %v", expectedValue, format(result))
		}
	} else {
		t.Errorf("No setter generated for meta %v", meta.Name)
	}
}

func TestStringMetaValuerAndSetter(t *testing.T) {
	user := &struct {
		Name  string
		Name2 *string
	}{}

	res := resource.New(user)

	meta := &resource.Meta{
		Name:         "Name",
		BaseResource: res,
	}

	checkMeta(user, meta, "hello world", t)

	meta2 := &resource.Meta{
		Name:         "Name2",
		BaseResource: res,
	}

	checkMeta(user, meta2, "hello world2", t)
}

func TestIntMetaValuerAndSetter(t *testing.T) {
	user := &struct {
		Age  int
		Age2 uint
		Age3 *int8
		Age4 *uint8
	}{}

	res := resource.New(user)

	meta := &resource.Meta{
		Name:         "Age",
		BaseResource: res,
	}

	checkMeta(user, meta, 18, t)

	meta2 := &resource.Meta{
		Name:         "Age2",
		BaseResource: res,
	}

	checkMeta(user, meta2, "28", t)

	meta3 := &resource.Meta{
		Name:         "Age3",
		BaseResource: res,
	}

	checkMeta(user, meta3, 38, t)

	meta4 := &resource.Meta{
		Name:         "Age4",
		BaseResource: res,
	}

	checkMeta(user, meta4, "48", t)
}

func TestFloatMetaValuerAndSetter(t *testing.T) {
	user := &struct {
		Age  float64
		Age2 *float64
	}{}

	res := resource.New(user)

	meta := &resource.Meta{
		Name:         "Age",
		BaseResource: res,
	}

	checkMeta(user, meta, 18.5, t)

	meta2 := &resource.Meta{
		Name:         "Age2",
		BaseResource: res,
	}

	checkMeta(user, meta2, "28.5", t)
}

func TestBoolMetaValuerAndSetter(t *testing.T) {
	user := &struct {
		Actived  bool
		Actived2 *bool
	}{}

	res := resource.New(user)

	meta := &resource.Meta{
		Name:         "Actived",
		BaseResource: res,
	}

	checkMeta(user, meta, "true", t)

	meta2 := &resource.Meta{
		Name:         "Actived2",
		BaseResource: res,
	}

	checkMeta(user, meta2, "true", t)

	meta3 := &resource.Meta{
		Name:         "Actived",
		BaseResource: res,
	}

	checkMeta(user, meta3, "", t, "false")

	meta4 := &resource.Meta{
		Name:         "Actived2",
		BaseResource: res,
	}

	checkMeta(user, meta4, "f", t, "false")
}

type scanner struct {
	Body string
}

func (s *scanner) Scan(value interface{}) error {
	s.Body = fmt.Sprint(value)
	return nil
}

func (s scanner) Value() (driver.Value, error) {
	return s.Body, nil
}

func TestScannerMetaValuerAndSetter(t *testing.T) {
	user := &struct {
		Scanner scanner
	}{}

	res := resource.New(user)

	meta := &resource.Meta{
		Name:         "Scanner",
		BaseResource: res,
	}

	checkMeta(user, meta, "scanner", t)
}

func TestSliceMetaValuerAndSetter(t *testing.T) {
	t.Skip()

	user := &struct {
		Names  []string
		Names2 []*string
		Names3 *[]string
		Names4 []*string
	}{}

	res := resource.New(user)

	meta := &resource.Meta{
		Name:         "Names",
		BaseResource: res,
	}

	checkMeta(user, meta, []string{"name1", "name2"}, t)

	meta2 := &resource.Meta{
		Name:         "Names2",
		BaseResource: res,
	}

	checkMeta(user, meta2, []string{"name1", "name2"}, t)

	meta3 := &resource.Meta{
		Name:         "Names3",
		BaseResource: res,
	}

	checkMeta(user, meta3, []string{"name1", "name2"}, t)

	meta4 := &resource.Meta{
		Name:         "Names4",
		BaseResource: res,
	}

	checkMeta(user, meta4, []string{"name1", "name2"}, t)
}

type Collection struct {
	gorm.Model

	publish2.Version
	publish2.Schedule

	Name string

	Products       []Product `gorm:"many2many:collection_products;association_autoupdate:false"`
	ProductsSorter sorting.SortableCollection
}

type Product struct {
	gorm.Model

	publish2.Schedule
	publish2.Version

	Name string
}

func WithoutVersion(db *gorm.DB) *gorm.DB {
	return db.Set(admin.DisableCompositePrimaryKeyMode, "on").Set(publish2.VersionMode, publish2.VersionMultipleMode).Set(publish2.ScheduleMode, publish2.ModeOff)
}

func updateVersionPriority() func(scope *gorm.Scope) {
	return func(scope *gorm.Scope) {
		if field, ok := scope.FieldByName("VersionPriority"); ok {
			createdAtField, _ := scope.FieldByName("CreatedAt")
			createdAt := createdAtField.Field.Interface().(time.Time)

			versionNameField, _ := scope.FieldByName("VersionName")
			versionName := versionNameField.Field.Interface().(string)

			versionPriority := fmt.Sprintf("%v_%v", createdAt.UTC().Format(time.RFC3339), versionName)
			field.Set(versionPriority)
		}
	}
}
func updateCallback(scope *gorm.Scope) {
	return
}
func TestMany2ManyRelation(t *testing.T) {
	db := testutils.TestDB()
	db.Callback().Create().Before("gorm:begin_transaction").Register("publish2:versions", func(scope *gorm.Scope) {
		if field, ok := scope.FieldByName("VersionName"); ok {
			if !field.IsBlank {
				return
			}

			name := time.Now().Format("2006-01-02")

			idField, _ := scope.FieldByName("ID")
			id := idField.Field.Interface().(uint)

			var count int
			scope.DB().Table(scope.TableName()).Unscoped().Scopes(WithoutVersion).Where("id = ? AND version_name like ?", id, name+"%").Count(&count)

			versionName := fmt.Sprintf("%s-v%v", name, count+1)
			field.Set(versionName)
		}
	})

	db.Callback().Create().After("gorm:begin_transaction").Register("publish2:version_priority", updateVersionPriority())
	db.Callback().Update().Before("gorm:begin_transaction").Register("publish:versions", updateCallback)
	publish2.RegisterCallbacks(db)
	testutils.ResetDBTables(db, &Collection{}, &Product{})

	adm := admin.New(&qor.Config{DB: db.Set(publish2.ScheduleMode, publish2.ModeOff)})
	c := adm.AddResource(&Collection{})

	productsMeta := resource.Meta{
		Name:         "Products",
		FieldName:    "Products",
		BaseResource: c,
		Config: &admin.SelectManyConfig{
			Collection: func(value interface{}, ctx *qor.Context) (results [][]string) {
				if c, ok := value.(*Collection); ok {
					var products []Product
					ctx.GetDB().Model(c).Related(&products, "Products")

					for _, product := range products {
						results = append(results, []string{fmt.Sprintf("%v", product.ID), product.Name})
					}
				}
				return
			},
		},
	}

	var scope = &gorm.Scope{Value: c.Value}
	var getField = func(fields []*gorm.StructField, name string) *gorm.StructField {
		for _, field := range fields {
			if field.Name == name || field.DBName == name {
				return field
			}
		}
		return nil
	}

	productsMeta.FieldStruct = getField(scope.GetStructFields(), productsMeta.FieldName)

	if err := productsMeta.Initialize(); err != nil {
		t.Fatal(err)
	}

	p1 := Product{Name: "p1"}
	p2 := Product{Name: "p2"}
	testutils.AssertNoErr(t, db.Save(&p1).Error)
	testutils.AssertNoErr(t, db.Save(&p2).Error)

	record := Collection{Name: "test"}
	testutils.AssertNoErr(t, db.Save(&record).Error)
	ctx := &qor.Context{DB: db}
	metaValue := &resource.MetaValue{Name: productsMeta.Name, Value: []string{fmt.Sprintf("%d", p1.ID), fmt.Sprintf("%d", p2.ID)}}

	productsMeta.Setter(&record, metaValue, ctx)

	testutils.AssertNoErr(t, db.Preload("Products").Find(&record).Error)
	if len(record.Products) != 2 {
		t.Error("products not set to collection")
	}
}
<commit_msg>Expand many2many relation test to support versions. (test fails because the feature is not implemented)<commit_after>package resource_test

import (
	"database/sql/driver"
	"fmt"
	"reflect"
	"testing"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/qor/admin"
	"github.com/qor/publish2"
	"github.com/qor/qor"
	"github.com/qor/qor/resource"
	testutils "github.com/qor/qor/test/utils"
	"github.com/qor/qor/utils"
	"github.com/qor/sorting"
)

func format(value interface{}) string {
	return fmt.Sprint(utils.Indirect(reflect.ValueOf(value)).Interface())
}

func checkMeta(record interface{}, meta *resource.Meta, value interface{}, t *testing.T, expectedValues ...string) {
	var (
		context       = &qor.Context{DB: testutils.TestDB()}
		metaValue     = &resource.MetaValue{Name: meta.Name, Value: value}
		expectedValue = fmt.Sprint(value)
	)

	for _, v := range expectedValues {
		expectedValue = v
	}

	meta.PreInitialize()
	meta.Initialize()

	if meta.Setter != nil {
		meta.Setter(record, metaValue, context)
		if context.HasError() {
			t.Errorf("No error should happen, but got %v", context.Errors)
		}

		result := meta.Valuer(record, context)
		if resultValuer, ok := result.(driver.Valuer); ok {
			if v, err := resultValuer.Value(); err == nil {
				result = v
			}
		}

		if format(result) != expectedValue {
			t.Errorf("Wrong value, should be %v, but got %v", expectedValue, format(result))
		}
	} else {
		t.Errorf("No setter generated for meta %v", meta.Name)
	}
}

func TestStringMetaValuerAndSetter(t *testing.T) {
	user := &struct {
		Name  string
		Name2 *string
	}{}

	res := resource.New(user)

	meta := &resource.Meta{
		Name:         "Name",
		BaseResource: res,
	}

	checkMeta(user, meta, "hello world", t)

	meta2 := &resource.Meta{
		Name:         "Name2",
		BaseResource: res,
	}

	checkMeta(user, meta2, "hello world2", t)
}

func TestIntMetaValuerAndSetter(t *testing.T) {
	user := &struct {
		Age  int
		Age2 uint
		Age3 *int8
		Age4 *uint8
	}{}

	res := resource.New(user)

	meta := &resource.Meta{
		Name:         "Age",
		BaseResource: res,
	}

	checkMeta(user, meta, 18, t)

	meta2 := &resource.Meta{
		Name:         "Age2",
		BaseResource: res,
	}

	checkMeta(user, meta2, "28", t)

	meta3 := &resource.Meta{
		Name:         "Age3",
		BaseResource: res,
	}

	checkMeta(user, meta3, 38, t)

	meta4 := &resource.Meta{
		Name:         "Age4",
		BaseResource: res,
	}

	checkMeta(user, meta4, "48", t)
}

func TestFloatMetaValuerAndSetter(t *testing.T) {
	user := &struct {
		Age  float64
		Age2 *float64
	}{}

	res := resource.New(user)

	meta := &resource.Meta{
		Name:         "Age",
		BaseResource: res,
	}

	checkMeta(user, meta, 18.5, t)

	meta2 := &resource.Meta{
		Name:         "Age2",
		BaseResource: res,
	}

	checkMeta(user, meta2, "28.5", t)
}

func TestBoolMetaValuerAndSetter(t *testing.T) {
	user := &struct {
		Actived  bool
		Actived2 *bool
	}{}

	res := resource.New(user)

	meta := &resource.Meta{
		Name:         "Actived",
		BaseResource: res,
	}

	checkMeta(user, meta, "true", t)

	meta2 := &resource.Meta{
		Name:         "Actived2",
		BaseResource: res,
	}

	checkMeta(user, meta2, "true", t)

	meta3 := &resource.Meta{
		Name:         "Actived",
		BaseResource: res,
	}

	checkMeta(user, meta3, "", t, "false")

	meta4 := &resource.Meta{
		Name:         "Actived2",
		BaseResource: res,
	}

	checkMeta(user, meta4, "f", t, "false")
}

type scanner struct {
	Body string
}

func (s *scanner) Scan(value interface{}) error {
	s.Body = fmt.Sprint(value)
	return nil
}

func (s scanner) Value() (driver.Value, error) {
	return s.Body, nil
}

func TestScannerMetaValuerAndSetter(t *testing.T) {
	user := &struct {
		Scanner scanner
	}{}

	res := resource.New(user)

	meta := &resource.Meta{
		Name:         "Scanner",
		BaseResource: res,
	}

	checkMeta(user, meta, "scanner", t)
}

func TestSliceMetaValuerAndSetter(t *testing.T) {
	t.Skip()

	user := &struct {
		Names  []string
		Names2 []*string
		Names3 *[]string
		Names4 []*string
	}{}

	res := resource.New(user)

	meta := &resource.Meta{
		Name:         "Names",
		BaseResource: res,
	}

	checkMeta(user, meta, []string{"name1", "name2"}, t)

	meta2 := &resource.Meta{
		Name:         "Names2",
		BaseResource: res,
	}

	checkMeta(user, meta2, []string{"name1", "name2"}, t)

	meta3 := &resource.Meta{
		Name:         "Names3",
		BaseResource: res,
	}

	checkMeta(user, meta3, []string{"name1", "name2"}, t)

	meta4 := &resource.Meta{
		Name:         "Names4",
		BaseResource: res,
	}

	checkMeta(user, meta4, []string{"name1", "name2"}, t)
}

type Collection struct {
	gorm.Model

	Name string

	Products       []Product `gorm:"many2many:collection_products;association_autoupdate:false"`
	ProductsSorter sorting.SortableCollection
}

type CollectionWithVersion struct {
	gorm.Model

	publish2.Version
	publish2.Schedule

	Name string

	Products       []ProductWithVersion `gorm:"many2many:collection_with_version_product_with_versions;association_autoupdate:false"`
	ProductsSorter sorting.SortableCollection
}

type ProductWithVersion struct {
	gorm.Model

	publish2.Schedule
	publish2.Version

	Name string
}

type Product struct {
	gorm.Model

	Name string
}

func WithoutVersion(db *gorm.DB) *gorm.DB {
	return db.Set(admin.DisableCompositePrimaryKeyMode, "on").Set(publish2.VersionMode, publish2.VersionMultipleMode).Set(publish2.ScheduleMode, publish2.ModeOff)
}

func updateVersionPriority() func(scope *gorm.Scope) {
	return func(scope *gorm.Scope) {
		if field, ok := scope.FieldByName("VersionPriority"); ok {
			createdAtField, _ := scope.FieldByName("CreatedAt")
			createdAt := createdAtField.Field.Interface().(time.Time)

			versionNameField, _ := scope.FieldByName("VersionName")
			versionName := versionNameField.Field.Interface().(string)

			versionPriority := fmt.Sprintf("%v_%v", createdAt.UTC().Format(time.RFC3339), versionName)
			field.Set(versionPriority)
		}
	}
}
func updateCallback(scope *gorm.Scope) {
	return
}
func TestMany2ManyRelation(t *testing.T) {
	db := testutils.TestDB()
	testutils.ResetDBTables(db, &Collection{}, &Product{}, "collection_products")

	adm := admin.New(&qor.Config{DB: db.Set(publish2.ScheduleMode, publish2.ModeOff)})
	c := adm.AddResource(&Collection{})

	productsMeta := resource.Meta{
		Name:         "Products",
		FieldName:    "Products",
		BaseResource: c,
		Config: &admin.SelectManyConfig{
			Collection: func(value interface{}, ctx *qor.Context) (results [][]string) {
				if c, ok := value.(*Collection); ok {
					var products []Product
					ctx.GetDB().Model(c).Related(&products, "Products")

					for _, product := range products {
						results = append(results, []string{fmt.Sprintf("%v", product.ID), product.Name})
					}
				}
				return
			},
		},
	}

	var scope = &gorm.Scope{Value: c.Value}
	var getField = func(fields []*gorm.StructField, name string) *gorm.StructField {
		for _, field := range fields {
			if field.Name == name || field.DBName == name {
				return field
			}
		}
		return nil
	}

	productsMeta.FieldStruct = getField(scope.GetStructFields(), productsMeta.FieldName)

	if err := productsMeta.Initialize(); err != nil {
		t.Fatal(err)
	}

	p1 := Product{Name: "p1"}
	p2 := Product{Name: "p2"}
	testutils.AssertNoErr(t, db.Save(&p1).Error)
	testutils.AssertNoErr(t, db.Save(&p2).Error)

	record := Collection{Name: "test"}
	testutils.AssertNoErr(t, db.Save(&record).Error)
	ctx := &qor.Context{DB: db}
	metaValue := &resource.MetaValue{Name: productsMeta.Name, Value: []string{fmt.Sprintf("%d", p1.ID), fmt.Sprintf("%d", p2.ID)}}

	productsMeta.Setter(&record, metaValue, ctx)

	testutils.AssertNoErr(t, db.Preload("Products").Find(&record).Error)
	if len(record.Products) != 2 {
		t.Error("products not set to collection")
	}
}

func TestManyToManyRelation_WithVersion(t *testing.T) {
	db := testutils.TestDB()
	registerVersionNameCallback(db)
	publish2.RegisterCallbacks(db)
	testutils.ResetDBTables(db, &CollectionWithVersion{}, &ProductWithVersion{}, "collection_with_versions_product_with_versions")

	adm := admin.New(&qor.Config{DB: db.Set(publish2.ScheduleMode, publish2.ModeOff)})
	c := adm.AddResource(&CollectionWithVersion{})

	productsMeta := resource.Meta{
		Name:         "Products",
		FieldName:    "Products",
		BaseResource: c,
		Config: &admin.SelectManyConfig{
			Collection: func(value interface{}, ctx *qor.Context) (results [][]string) {
				if c, ok := value.(*CollectionWithVersion); ok {
					var products []ProductWithVersion
					ctx.GetDB().Model(c).Related(&products, "Products")

					for _, product := range products {
						results = append(results, []string{fmt.Sprintf("%v", product.ID), product.Name})
					}
				}
				return
			},
		},
	}

	var scope = &gorm.Scope{Value: c.Value}
	var getField = func(fields []*gorm.StructField, name string) *gorm.StructField {
		for _, field := range fields {
			if field.Name == name || field.DBName == name {
				return field
			}
		}
		return nil
	}

	productsMeta.FieldStruct = getField(scope.GetStructFields(), productsMeta.FieldName)

	if err := productsMeta.Initialize(); err != nil {
		t.Fatal(err)
	}

	p1 := ProductWithVersion{Name: "p1"}
	p2_v1 := ProductWithVersion{Name: "p2"}
	testutils.AssertNoErr(t, db.Save(&p1).Error)
	testutils.AssertNoErr(t, db.Save(&p2_v1).Error)
	p2_v2 := ProductWithVersion{Name: "p2"}
	p2_v2.ID = p2_v1.ID
	testutils.AssertNoErr(t, db.Save(&p2_v2).Error)

	record := CollectionWithVersion{Name: "test"}
	testutils.AssertNoErr(t, db.Save(&record).Error)
	ctx := &qor.Context{DB: db}
	metaValue := &resource.MetaValue{Name: productsMeta.Name, Value: []map[string]string{
		{"id": fmt.Sprintf("%d", p1.ID), "version_name": p1.GetVersionName()},
		{"id": fmt.Sprintf("%d", p2_v2.ID), "version_name": p2_v2.GetVersionName()},
	}}

	productsMeta.Setter(&record, metaValue, ctx)

	testutils.AssertNoErr(t, db.Preload("Products").Find(&record).Error)
	if len(record.Products) != 2 {
		t.Error("products not set to collection")
	}

	hasCorrectVersion := false
	for _, p := range record.Products {
		if p.ID == p2_v2.ID && p.GetVersionName() == p2_v2.VersionName {
			hasCorrectVersion = true
		}
	}

	if !hasCorrectVersion {
		t.Error("p2 is not associated with collection with correct version")
	}
}

func registerVersionNameCallback(db *gorm.DB) {
	db.Callback().Create().Before("gorm:begin_transaction").Register("publish2:versions", func(scope *gorm.Scope) {
		if field, ok := scope.FieldByName("VersionName"); ok {
			if !field.IsBlank {
				return
			}

			name := time.Now().Format("2006-01-02")

			idField, _ := scope.FieldByName("ID")
			id := idField.Field.Interface().(uint)

			var count int
			scope.DB().Table(scope.TableName()).Unscoped().Scopes(WithoutVersion).Where("id = ? AND version_name like ?", id, name+"%").Count(&count)

			versionName := fmt.Sprintf("%s-v%v", name, count+1)
			field.Set(versionName)
		}
	})

	db.Callback().Create().After("gorm:begin_transaction").Register("publish2:version_priority", updateVersionPriority())
}
<|endoftext|>


<commit_before>package model
import "time"

type CEP struct {
	ID           int64  `db:"id" json:"id,omitempty"`
	City         string `db:"city" json:"city,omitempty"`
	State        string `db:"state" json:"state,omitempty"`
	Uf           string `db:"uf" json:"uf,omitempty"`
	Logradouro   string `db:"logradouro" json:"logradouro,omitempty"`
	Neighborhood string `db:"neighborhood" json:"neighborhood,omitempty"`
	Address      string `db:"address" json:"address,omitempty"`
	Complement   string `db:"complement" json:"complement,omitempty"`
	Value        string `db:"value" json:"value,omitempty"`
	UpdateAt     time.Time `db:"updated_at" json:"updated_at,omitempty"`
	CreatedAt    time.Time `db:"created_at" json:"created_at,omitempty"`
}
<commit_msg>goimports<commit_after>package model

import "time"

type CEP struct {
	ID           int64     `db:"id" json:"id,omitempty"`
	City         string    `db:"city" json:"city,omitempty"`
	State        string    `db:"state" json:"state,omitempty"`
	Uf           string    `db:"uf" json:"uf,omitempty"`
	Logradouro   string    `db:"logradouro" json:"logradouro,omitempty"`
	Neighborhood string    `db:"neighborhood" json:"neighborhood,omitempty"`
	Address      string    `db:"address" json:"address,omitempty"`
	Complement   string    `db:"complement" json:"complement,omitempty"`
	Value        string    `db:"value" json:"value,omitempty"`
	UpdateAt     time.Time `db:"updated_at" json:"updated_at,omitempty"`
	CreatedAt    time.Time `db:"created_at" json:"created_at,omitempty"`
}
<|endoftext|>


<commit_before>package mark

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

// A Node is an element in the parse tree.
type Node interface {
	Type() NodeType
	Render() string
}

// NodeType identifies the type of a parse tree node.
type NodeType int

// Type returns itself and provides an easy default implementation
// for embedding in a Node. Embedded in all non-trivial Nodes.
func (t NodeType) Type() NodeType {
	return t
}

const (
	NodeText NodeType = iota // Plain text.
	NodeParagraph
	NodeEmphasis
	NodeHeading
	NodeNewLine
	NodeBr
	NodeHr
	NodeImage
	NodeList
	NodeListItem
	NodeCode // Code block.
	NodeLink
	NodeDefLink
	NodeTable
	NodeRow
	NodeCell
	NodeBlockQuote // Blockquote block.
	NodeHTML
)

// ParagraphNode hold simple paragraph node contains text
// that may be emphasis.
type ParagraphNode struct {
	NodeType
	Pos
	Nodes []Node
}

// Render return the html representation of ParagraphNode
func (n *ParagraphNode) Render() (s string) {
	for _, node := range n.Nodes {
		s += node.Render()
	}
	return render("p", s)
}

func (t *ParagraphNode) append(n Node) {
	t.Nodes = append(t.Nodes, n)
}

func (t *Tree) newParagraph(pos Pos) *ParagraphNode {
	return &ParagraphNode{NodeType: NodeParagraph, Pos: pos}
}

// TextNode holds plain text.
type TextNode struct {
	NodeType
	Pos
	Text []byte
}

// Render return the string representation of TexNode
func (n *TextNode) Render() string {
	return string(n.Text)
}

func (t *Tree) newText(pos Pos, text string) *TextNode {
	return &TextNode{NodeType: NodeText, Pos: pos, Text: []byte(text)}
}

// NewLineNode represent simple `\n`.
type NewLineNode struct {
	NodeType
	Pos
}

// Render return the string \n for representing new line.
func (n *NewLineNode) Render() string {
	return "\n"
}

func (t *Tree) newLine(pos Pos) *NewLineNode {
	return &NewLineNode{NodeType: NodeNewLine, Pos: pos}
}

// HrNode represent horizontal rule
type HrNode struct {
	NodeType
	Pos
}

// Render return the html representation of hr.
func (n *HrNode) Render() string {
	return "
"
}

func (t *Tree) newHr(pos Pos) *HrNode {
	return &HrNode{NodeType: NodeHr, Pos: pos}
}

// BrNode represent br element
type BrNode struct {
	NodeType
	Pos
}

// Render return the html representation of br.
func (n *BrNode) Render() string {
	return "
"
}

func (t *Tree) newBr(pos Pos) *BrNode {
	return &BrNode{NodeType: NodeBr, Pos: pos}
}

// EmphasisNode holds text with style.
type EmphasisNode struct {
	NodeType
	Pos
	Style itemType
	Nodes []Node
}

// Tag return the tagName based on Style field
func (n *EmphasisNode) Tag() (s string) {
	switch n.Style {
	case itemStrong:
		s = "strong"
	case itemItalic:
		s = "em"
	case itemStrike:
		s = "del"
	case itemCode:
		s = "code"
	}
	return
}

// Return the html representation of emphasis text(string, italic, ..).
func (n *EmphasisNode) Render() string {
	var s string
	for _, node := range n.Nodes {
		s += node.Render()
	}
	return render(n.Tag(), s)
}

func (n *EmphasisNode) append(node Node) {
	n.Nodes = append(n.Nodes, node)
}

func (t *Tree) newEmphasis(pos Pos, style itemType) *EmphasisNode {
	return &EmphasisNode{NodeType: NodeEmphasis, Pos: pos, Style: style}
}

// Heading holds heaing node with specific level.
type HeadingNode struct {
	NodeType
	Pos
	Level int
	Text  []byte
}

// Render return the html representation based on heading level.
func (n *HeadingNode) Render() string {
	re := regexp.MustCompile(`[^\w]+`)
	id := re.ReplaceAllString(string(n.Text), "-")
	// ToLowerCase
	id = strings.ToLower(id)
	return fmt.Sprintf("<%[1]s id=\"%s\">%s</%[1]s>", "h"+strconv.Itoa(n.Level), id, n.Text)
}

func (t *Tree) newHeading(pos Pos, level int, text string) *HeadingNode {
	return &HeadingNode{NodeType: NodeHeading, Pos: pos, Level: level, Text: []byte(text)}
}

// Code holds CodeBlock node with specific lang
type CodeNode struct {
	NodeType
	Pos
	Lang string
	Text []byte
}

// Return the html representation of codeBlock
func (n *CodeNode) Render() string {
	var attr string
	if n.Lang != "" {
		attr = fmt.Sprintf(" class=\"lang-%s\"", n.Lang)
	}
	code := fmt.Sprintf("<%[1]s%s>%s</%[1]s>", "code", attr, n.Text)
	return render("pre", code)
}

func (t *Tree) newCode(pos Pos, lang, text string) *CodeNode {
	return &CodeNode{NodeType: NodeCode, Pos: pos, Lang: lang, Text: []byte(text)}
}

// Link holds a tag with optional title
type LinkNode struct {
	NodeType
	Pos
	Title, Href string
	Text        []byte
}

// Return the html representation of link node
func (n *LinkNode) Render() string {
	attrs := fmt.Sprintf("href=\"%s\"", n.Href)
	if n.Title != "" {
		attrs += fmt.Sprintf(" title=\"%s\"", n.Title)
	}
	return fmt.Sprintf("%s", attrs, n.Text)
}

func (t *Tree) newLink(pos Pos, title, href, text string) *LinkNode {
	return &LinkNode{NodeType: NodeLink, Pos: pos, Title: title, Href: href, Text: []byte(text)}
}

// DefLinkNode refresent single reference to link-definition
type DefLinkNode struct {
	NodeType
	Pos
	Name, Href, Title string
}

// Deflink have no representation(Transparent node)
func (n *DefLinkNode) Render() string {
	return ""
}

func (t *Tree) newDefLink(pos Pos, name, href, title string) *DefLinkNode {
	return &DefLinkNode{NodeType: NodeLink, Pos: pos, Name: name, Href: href, Title: title}
}

// Image holds img tag with optional title
type ImageNode struct {
	NodeType
	Pos
	Title, Src string
	Alt        []byte
}

// Return the html representation on img node
func (n *ImageNode) Render() string {
	attrs := fmt.Sprintf("src=\"%s\" alt=\"%s\"", n.Src, n.Alt)
	if n.Title != "" {
		attrs += fmt.Sprintf(" title=\"%s\"", n.Title)
	}
	return fmt.Sprintf("", attrs)
}

func (t *Tree) newImage(pos Pos, title, src, alt string) *ImageNode {
	return &ImageNode{NodeType: NodeImage, Pos: pos, Title: title, Src: src, Alt: []byte(alt)}
}

// List holds list items nodes in ordered or unordered states.
type ListNode struct {
	NodeType
	Pos
	Ordered bool
	Items   []*ListItemNode
}

func (t *ListNode) append(item *ListItemNode) {
	t.Items = append(t.Items, item)
}

// Return the html representation of list(ul|ol)
func (n *ListNode) Render() (s string) {
	tag := "ul"
	if n.Ordered {
		tag = "ol"
	}
	for _, item := range n.Items {
		s += item.Render()
	}
	return render(tag, s)
}

func (t *Tree) newList(pos Pos, ordered bool) *ListNode {
	return &ListNode{NodeType: NodeList, Pos: pos, Ordered: ordered}
}

// ListItem represent single item in ListNode that may contains nested nodes.
type ListItemNode struct {
	NodeType
	Pos
	Nodes []Node
}

func (t *ListItemNode) append(n Node) {
	t.Nodes = append(t.Nodes, n)
}

// Return the html representation of listItem
func (n *ListItemNode) Render() (s string) {
	for _, node := range n.Nodes {
		s += node.Render()
	}
	return render("li", s)
}

func (t *Tree) newListItem(pos Pos) *ListItemNode {
	return &ListItemNode{NodeType: NodeListItem, Pos: pos}
}

// TableNode represent table elment contains head and body
type TableNode struct {
	NodeType
	Pos
	Rows []*RowNode
}

func (t *TableNode) append(row *RowNode) {
	t.Rows = append(t.Rows, row)
}

// Return the htnml representation of a table
func (n *TableNode) Render() string {
	var s string
	for i, row := range n.Rows {
		switch i {
		case 0:
			s += render("thead", row.Render())
		case 1:
			s += "
"
			fallthrough
		default:
			s += row.Render()
			if i == len(n.Rows)-1 {
				s += "
"
			}
		}
	}
	return render("table", s)
}

func (t *Tree) newTable(pos Pos) *TableNode {
	return &TableNode{NodeType: NodeTable, Pos: pos}
}

// TableRowNode represnt tr that holds batch of table-data/cells
type RowNode struct {
	NodeType
	Pos
	Cells []*CellNode
}

func (r *RowNode) append(cell *CellNode) {
	r.Cells = append(r.Cells, cell)
}

func (n *RowNode) Render() string {
	var s string
	for _, cell := range n.Cells {
		s += cell.Render()
	}
	return render("tr", s)
}

func (t *Tree) newRow(pos Pos) *RowNode {
	return &RowNode{NodeType: NodeRow, Pos: pos}
}

// AlignType identifies the aligment-type of specfic cell.
type AlignType int

// Align returns itself and provides an easy default implementation
// for embedding in a Node.
func (t AlignType) Align() AlignType {
	return t
}

// Alignment
const (
	None AlignType = iota
	Right
	Left
	Center
)

// Cell types
const (
	Header = iota
	Data
)

// TableCellNode represent table-data/cell that holds simple text(may be emphasis)
// Note: the text in  elements are bold and centered by default.
type CellNode struct {
	NodeType
	Pos
	AlignType
	Kind  int
	Nodes []Node
}

func (t *CellNode) append(n Node) {
	t.Nodes = append(t.Nodes, n)
}

// Return the html reprenestation of table-cell
func (n *CellNode) Render() string {
	var s string
	tag := "td"
	if n.Kind == Header {
		tag = "th"
	}
	for _, node := range n.Nodes {
		s += node.Render()
	}
	return fmt.Sprintf("<%[1]s%s>%s</%[1]s>", tag, n.Style(), s)
}

// Return the cell-style based on alignment
func (n *CellNode) Style() string {
	s := " style=\"text-align:"
	switch n.Align() {
	case Right:
		s += "right\""
	case Left:
		s += "left\""
	case Center:
		s += "center\""
	default:
		s = ""
	}
	return s
}

func (t *Tree) newCell(pos Pos, kind int, align AlignType) *CellNode {
	return &CellNode{NodeType: NodeCell, Pos: pos, Kind: kind, AlignType: align}
}

// BlockQuote element
type BlockQuoteNode struct {
	NodeType
	Pos
	Nodes []Node
}

func (n *BlockQuoteNode) Render() string {
	var s string
	for _, node := range n.Nodes {
		s += node.Render()
	}
	return render("blockquote", s)
}

func (t *Tree) newBlockQuote(pos Pos) *BlockQuoteNode {
	return &BlockQuoteNode{NodeType: NodeBlockQuote, Pos: pos}
}

// TODO(Ariel): rename to wrap()
// Wrap text with specific tag.
func render(tag, body string) string {
	return fmt.Sprintf("<%[1]s>%s</%[1]s>", tag, body)
}
<commit_msg>feat(node): HTMLNode<commit_after>package mark

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

// A Node is an element in the parse tree.
type Node interface {
	Type() NodeType
	Render() string
}

// NodeType identifies the type of a parse tree node.
type NodeType int

// Type returns itself and provides an easy default implementation
// for embedding in a Node. Embedded in all non-trivial Nodes.
func (t NodeType) Type() NodeType {
	return t
}

const (
	NodeText NodeType = iota // Plain text.
	NodeParagraph
	NodeEmphasis
	NodeHeading
	NodeNewLine
	NodeBr
	NodeHr
	NodeImage
	NodeList
	NodeListItem
	NodeCode // Code block.
	NodeLink
	NodeDefLink
	NodeTable
	NodeRow
	NodeCell
	NodeBlockQuote // Blockquote block.
	NodeHTML
)

// ParagraphNode hold simple paragraph node contains text
// that may be emphasis.
type ParagraphNode struct {
	NodeType
	Pos
	Nodes []Node
}

// Render return the html representation of ParagraphNode
func (n *ParagraphNode) Render() (s string) {
	for _, node := range n.Nodes {
		s += node.Render()
	}
	return render("p", s)
}

func (t *ParagraphNode) append(n Node) {
	t.Nodes = append(t.Nodes, n)
}

func (t *Tree) newParagraph(pos Pos) *ParagraphNode {
	return &ParagraphNode{NodeType: NodeParagraph, Pos: pos}
}

// TextNode holds plain text.
type TextNode struct {
	NodeType
	Pos
	Text []byte
}

// Render return the string representation of TexNode
func (n *TextNode) Render() string {
	return string(n.Text)
}

func (t *Tree) newText(pos Pos, text string) *TextNode {
	return &TextNode{NodeType: NodeText, Pos: pos, Text: []byte(text)}
}

// HTMLNode holds html source.
type HTMLNode struct {
	NodeType
	Pos
	Src []byte
}

// Render return the src of the HTMLNode
func (n *HTMLNode) Render() string {
	return string(n.Src)
}

func (t *Tree) newHTML(pos Pos, src string) *HTMLNode {
	return &HTMLNode{NodeType: NodeHTML, Pos: pos, Src: []byte(src)}
}

// NewLineNode represent simple `\n`.
type NewLineNode struct {
	NodeType
	Pos
}

// Render return the string \n for representing new line.
func (n *NewLineNode) Render() string {
	return "\n"
}

func (t *Tree) newLine(pos Pos) *NewLineNode {
	return &NewLineNode{NodeType: NodeNewLine, Pos: pos}
}

// HrNode represent horizontal rule
type HrNode struct {
	NodeType
	Pos
}

// Render return the html representation of hr.
func (n *HrNode) Render() string {
	return "
"
}

func (t *Tree) newHr(pos Pos) *HrNode {
	return &HrNode{NodeType: NodeHr, Pos: pos}
}

// BrNode represent br element
type BrNode struct {
	NodeType
	Pos
}

// Render return the html representation of br.
func (n *BrNode) Render() string {
	return "
"
}

func (t *Tree) newBr(pos Pos) *BrNode {
	return &BrNode{NodeType: NodeBr, Pos: pos}
}

// EmphasisNode holds text with style.
type EmphasisNode struct {
	NodeType
	Pos
	Style itemType
	Nodes []Node
}

// Tag return the tagName based on Style field
func (n *EmphasisNode) Tag() (s string) {
	switch n.Style {
	case itemStrong:
		s = "strong"
	case itemItalic:
		s = "em"
	case itemStrike:
		s = "del"
	case itemCode:
		s = "code"
	}
	return
}

// Return the html representation of emphasis text(string, italic, ..).
func (n *EmphasisNode) Render() string {
	var s string
	for _, node := range n.Nodes {
		s += node.Render()
	}
	return render(n.Tag(), s)
}

func (n *EmphasisNode) append(node Node) {
	n.Nodes = append(n.Nodes, node)
}

func (t *Tree) newEmphasis(pos Pos, style itemType) *EmphasisNode {
	return &EmphasisNode{NodeType: NodeEmphasis, Pos: pos, Style: style}
}

// Heading holds heaing node with specific level.
type HeadingNode struct {
	NodeType
	Pos
	Level int
	Text  []byte
}

// Render return the html representation based on heading level.
func (n *HeadingNode) Render() string {
	re := regexp.MustCompile(`[^\w]+`)
	id := re.ReplaceAllString(string(n.Text), "-")
	// ToLowerCase
	id = strings.ToLower(id)
	return fmt.Sprintf("<%[1]s id=\"%s\">%s</%[1]s>", "h"+strconv.Itoa(n.Level), id, n.Text)
}

func (t *Tree) newHeading(pos Pos, level int, text string) *HeadingNode {
	return &HeadingNode{NodeType: NodeHeading, Pos: pos, Level: level, Text: []byte(text)}
}

// Code holds CodeBlock node with specific lang
type CodeNode struct {
	NodeType
	Pos
	Lang string
	Text []byte
}

// Return the html representation of codeBlock
func (n *CodeNode) Render() string {
	var attr string
	if n.Lang != "" {
		attr = fmt.Sprintf(" class=\"lang-%s\"", n.Lang)
	}
	code := fmt.Sprintf("<%[1]s%s>%s</%[1]s>", "code", attr, n.Text)
	return render("pre", code)
}

func (t *Tree) newCode(pos Pos, lang, text string) *CodeNode {
	return &CodeNode{NodeType: NodeCode, Pos: pos, Lang: lang, Text: []byte(text)}
}

// Link holds a tag with optional title
type LinkNode struct {
	NodeType
	Pos
	Title, Href string
	Text        []byte
}

// Return the html representation of link node
func (n *LinkNode) Render() string {
	attrs := fmt.Sprintf("href=\"%s\"", n.Href)
	if n.Title != "" {
		attrs += fmt.Sprintf(" title=\"%s\"", n.Title)
	}
	return fmt.Sprintf("%s", attrs, n.Text)
}

func (t *Tree) newLink(pos Pos, title, href, text string) *LinkNode {
	return &LinkNode{NodeType: NodeLink, Pos: pos, Title: title, Href: href, Text: []byte(text)}
}

// DefLinkNode refresent single reference to link-definition
type DefLinkNode struct {
	NodeType
	Pos
	Name, Href, Title string
}

// Deflink have no representation(Transparent node)
func (n *DefLinkNode) Render() string {
	return ""
}

func (t *Tree) newDefLink(pos Pos, name, href, title string) *DefLinkNode {
	return &DefLinkNode{NodeType: NodeLink, Pos: pos, Name: name, Href: href, Title: title}
}

// Image holds img tag with optional title
type ImageNode struct {
	NodeType
	Pos
	Title, Src string
	Alt        []byte
}

// Return the html representation on img node
func (n *ImageNode) Render() string {
	attrs := fmt.Sprintf("src=\"%s\" alt=\"%s\"", n.Src, n.Alt)
	if n.Title != "" {
		attrs += fmt.Sprintf(" title=\"%s\"", n.Title)
	}
	return fmt.Sprintf("", attrs)
}

func (t *Tree) newImage(pos Pos, title, src, alt string) *ImageNode {
	return &ImageNode{NodeType: NodeImage, Pos: pos, Title: title, Src: src, Alt: []byte(alt)}
}

// List holds list items nodes in ordered or unordered states.
type ListNode struct {
	NodeType
	Pos
	Ordered bool
	Items   []*ListItemNode
}

func (t *ListNode) append(item *ListItemNode) {
	t.Items = append(t.Items, item)
}

// Return the html representation of list(ul|ol)
func (n *ListNode) Render() (s string) {
	tag := "ul"
	if n.Ordered {
		tag = "ol"
	}
	for _, item := range n.Items {
		s += item.Render()
	}
	return render(tag, s)
}

func (t *Tree) newList(pos Pos, ordered bool) *ListNode {
	return &ListNode{NodeType: NodeList, Pos: pos, Ordered: ordered}
}

// ListItem represent single item in ListNode that may contains nested nodes.
type ListItemNode struct {
	NodeType
	Pos
	Nodes []Node
}

func (t *ListItemNode) append(n Node) {
	t.Nodes = append(t.Nodes, n)
}

// Return the html representation of listItem
func (n *ListItemNode) Render() (s string) {
	for _, node := range n.Nodes {
		s += node.Render()
	}
	return render("li", s)
}

func (t *Tree) newListItem(pos Pos) *ListItemNode {
	return &ListItemNode{NodeType: NodeListItem, Pos: pos}
}

// TableNode represent table elment contains head and body
type TableNode struct {
	NodeType
	Pos
	Rows []*RowNode
}

func (t *TableNode) append(row *RowNode) {
	t.Rows = append(t.Rows, row)
}

// Return the htnml representation of a table
func (n *TableNode) Render() string {
	var s string
	for i, row := range n.Rows {
		switch i {
		case 0:
			s += render("thead", row.Render())
		case 1:
			s += "
"
			fallthrough
		default:
			s += row.Render()
			if i == len(n.Rows)-1 {
				s += "
"
			}
		}
	}
	return render("table", s)
}

func (t *Tree) newTable(pos Pos) *TableNode {
	return &TableNode{NodeType: NodeTable, Pos: pos}
}

// TableRowNode represnt tr that holds batch of table-data/cells
type RowNode struct {
	NodeType
	Pos
	Cells []*CellNode
}

func (r *RowNode) append(cell *CellNode) {
	r.Cells = append(r.Cells, cell)
}

func (n *RowNode) Render() string {
	var s string
	for _, cell := range n.Cells {
		s += cell.Render()
	}
	return render("tr", s)
}

func (t *Tree) newRow(pos Pos) *RowNode {
	return &RowNode{NodeType: NodeRow, Pos: pos}
}

// AlignType identifies the aligment-type of specfic cell.
type AlignType int

// Align returns itself and provides an easy default implementation
// for embedding in a Node.
func (t AlignType) Align() AlignType {
	return t
}

// Alignment
const (
	None AlignType = iota
	Right
	Left
	Center
)

// Cell types
const (
	Header = iota
	Data
)

// TableCellNode represent table-data/cell that holds simple text(may be emphasis)
// Note: the text in  elements are bold and centered by default.
type CellNode struct {
	NodeType
	Pos
	AlignType
	Kind  int
	Nodes []Node
}

func (t *CellNode) append(n Node) {
	t.Nodes = append(t.Nodes, n)
}

// Return the html reprenestation of table-cell
func (n *CellNode) Render() string {
	var s string
	tag := "td"
	if n.Kind == Header {
		tag = "th"
	}
	for _, node := range n.Nodes {
		s += node.Render()
	}
	return fmt.Sprintf("<%[1]s%s>%s</%[1]s>", tag, n.Style(), s)
}

// Return the cell-style based on alignment
func (n *CellNode) Style() string {
	s := " style=\"text-align:"
	switch n.Align() {
	case Right:
		s += "right\""
	case Left:
		s += "left\""
	case Center:
		s += "center\""
	default:
		s = ""
	}
	return s
}

func (t *Tree) newCell(pos Pos, kind int, align AlignType) *CellNode {
	return &CellNode{NodeType: NodeCell, Pos: pos, Kind: kind, AlignType: align}
}

// BlockQuote element
type BlockQuoteNode struct {
	NodeType
	Pos
	Nodes []Node
}

func (n *BlockQuoteNode) Render() string {
	var s string
	for _, node := range n.Nodes {
		s += node.Render()
	}
	return render("blockquote", s)
}

func (t *Tree) newBlockQuote(pos Pos) *BlockQuoteNode {
	return &BlockQuoteNode{NodeType: NodeBlockQuote, Pos: pos}
}

// TODO(Ariel): rename to wrap()
// Wrap text with specific tag.
func render(tag, body string) string {
	return fmt.Sprintf("<%[1]s>%s</%[1]s>", tag, body)
}
<|endoftext|>


<commit_before>package db

import (
	sq "github.com/Masterminds/squirrel"
	"github.com/concourse/atc/db/lock"
)

//go:generate counterfeiter . PipelineFactory

type PipelineFactory interface {
	VisiblePipelines([]string) ([]Pipeline, error)
	PublicPipelines() ([]Pipeline, error)
	AllPipelines() ([]Pipeline, error)
}

type pipelineFactory struct {
	conn        Conn
	lockFactory lock.LockFactory
}

func NewPipelineFactory(conn Conn, lockFactory lock.LockFactory) PipelineFactory {
	return &pipelineFactory{
		conn:        conn,
		lockFactory: lockFactory,
	}
}

func (f *pipelineFactory) VisiblePipelines(teamNames []string) ([]Pipeline, error) {
	rows, err := pipelinesQuery.
		Where(sq.Eq{"t.name": teamNames}).
		OrderBy("team_id ASC", "ordering ASC").
		RunWith(f.conn).
		Query()
	if err != nil {
		return nil, err
	}

	currentTeamPipelines, err := scanPipelines(f.conn, f.lockFactory, rows)
	if err != nil {
		return nil, err
	}

	stmt := pipelinesQuery.
		Where(sq.Eq{"public": true}).
		OrderBy("team_id ASC", "ordering ASC")

	if len(teamNames) > 0 {
		// otherwise we get NOT IN (NULL) which postgres
		// considers an undefined list, and returns nothing
		stmt = stmt.Where(sq.NotEq{"t.name": teamNames})
	}

	rows, err = stmt.
		RunWith(f.conn).
		Query()
	if err != nil {
		return nil, err
	}

	otherTeamPublicPipelines, err := scanPipelines(f.conn, f.lockFactory, rows)
	if err != nil {
		return nil, err
	}

	return append(currentTeamPipelines, otherTeamPublicPipelines...), nil
}

func (f *pipelineFactory) PublicPipelines() ([]Pipeline, error) {
	rows, err := pipelinesQuery.
		Where(sq.Eq{"p.public": true}).
		OrderBy("t.name, ordering").
		RunWith(f.conn).
		Query()
	if err != nil {
		return nil, err
	}

	pipelines, err := scanPipelines(f.conn, f.lockFactory, rows)
	if err != nil {
		return nil, err
	}

	return pipelines, nil
}

func (f *pipelineFactory) AllPipelines() ([]Pipeline, error) {
	rows, err := pipelinesQuery.
		OrderBy("ordering").
		RunWith(f.conn).
		Query()
	if err != nil {
		return nil, err
	}

	return scanPipelines(f.conn, f.lockFactory, rows)
}
<commit_msg>Revert handling empty teams in pipelines query<commit_after>package db

import (
	sq "github.com/Masterminds/squirrel"
	"github.com/concourse/atc/db/lock"
)

//go:generate counterfeiter . PipelineFactory

type PipelineFactory interface {
	VisiblePipelines([]string) ([]Pipeline, error)
	PublicPipelines() ([]Pipeline, error)
	AllPipelines() ([]Pipeline, error)
}

type pipelineFactory struct {
	conn        Conn
	lockFactory lock.LockFactory
}

func NewPipelineFactory(conn Conn, lockFactory lock.LockFactory) PipelineFactory {
	return &pipelineFactory{
		conn:        conn,
		lockFactory: lockFactory,
	}
}

func (f *pipelineFactory) VisiblePipelines(teamNames []string) ([]Pipeline, error) {
	rows, err := pipelinesQuery.
		Where(sq.Eq{"t.name": teamNames}).
		OrderBy("team_id ASC", "ordering ASC").
		RunWith(f.conn).
		Query()
	if err != nil {
		return nil, err
	}

	currentTeamPipelines, err := scanPipelines(f.conn, f.lockFactory, rows)
	if err != nil {
		return nil, err
	}

	rows, err = pipelinesQuery.
		Where(sq.NotEq{"t.name": teamNames}).
		Where(sq.Eq{"public": true}).
		OrderBy("team_id ASC", "ordering ASC").
		RunWith(f.conn).
		Query()
	if err != nil {
		return nil, err
	}

	otherTeamPublicPipelines, err := scanPipelines(f.conn, f.lockFactory, rows)
	if err != nil {
		return nil, err
	}

	return append(currentTeamPipelines, otherTeamPublicPipelines...), nil
}

func (f *pipelineFactory) PublicPipelines() ([]Pipeline, error) {
	rows, err := pipelinesQuery.
		Where(sq.Eq{"p.public": true}).
		OrderBy("t.name, ordering").
		RunWith(f.conn).
		Query()
	if err != nil {
		return nil, err
	}

	pipelines, err := scanPipelines(f.conn, f.lockFactory, rows)
	if err != nil {
		return nil, err
	}

	return pipelines, nil
}

func (f *pipelineFactory) AllPipelines() ([]Pipeline, error) {
	rows, err := pipelinesQuery.
		OrderBy("ordering").
		RunWith(f.conn).
		Query()
	if err != nil {
		return nil, err
	}

	return scanPipelines(f.conn, f.lockFactory, rows)
}
<|endoftext|>


<commit_before>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

// Package dl implements a simple downloads frontend server.
//
// It accepts HTTP POST requests to create a new download metadata entity, and
// lists entities with sorting and filtering.
// It is designed to run only on the instance of godoc that serves golang.org.
//
// The package also serves the list of downloads at:
//     https://golang.org/dl/
//
// An optional query param, mode=json, serves the list of stable release
// downloads in JSON format:
//     https://golang.org/dl/?mode=json
//
// An additional query param, include=all, when used with the mode=json
// query param, will serve a full list of available downloads, including
// stable, unstable, and archived releases in JSON format:
//     https://golang.org/dl/?mode=json&include=all
package dl

import (
	"fmt"
	"html/template"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	downloadBaseURL = "https://dl.google.com/go/"
	cacheKey        = "download_list_4" // increment if listTemplateData changes
	cacheDuration   = time.Hour
)

// File represents a file on the golang.org downloads page.
// It should be kept in sync with the upload code in x/build/cmd/release.
type File struct {
	Filename       string    `json:"filename"`
	OS             string    `json:"os"`
	Arch           string    `json:"arch"`
	Version        string    `json:"version"`
	Checksum       string    `json:"-" datastore:",noindex"` // SHA1; deprecated
	ChecksumSHA256 string    `json:"sha256" datastore:",noindex"`
	Size           int64     `json:"size" datastore:",noindex"`
	Kind           string    `json:"kind"` // "archive", "installer", "source"
	Uploaded       time.Time `json:"-"`
}

func (f File) ChecksumType() string {
	if f.ChecksumSHA256 != "" {
		return "SHA256"
	}
	return "SHA1"
}

func (f File) PrettyChecksum() string {
	if f.ChecksumSHA256 != "" {
		return f.ChecksumSHA256
	}
	return f.Checksum
}

func (f File) PrettyOS() string {
	if f.OS == "darwin" {
		switch {
		case strings.Contains(f.Filename, "osx10.8"):
			return "OS X 10.8+"
		case strings.Contains(f.Filename, "osx10.6"):
			return "OS X 10.6+"
		}
	}
	return pretty(f.OS)
}

func (f File) PrettySize() string {
	const mb = 1 << 20
	if f.Size == 0 {
		return ""
	}
	if f.Size < mb {
		// All Go releases are >1mb, but handle this case anyway.
		return fmt.Sprintf("%v bytes", f.Size)
	}
	return fmt.Sprintf("%.0fMB", float64(f.Size)/mb)
}

var primaryPorts = map[string]bool{
	"darwin/amd64":  true,
	"linux/386":     true,
	"linux/amd64":   true,
	"linux/armv6l":  true,
	"windows/386":   true,
	"windows/amd64": true,
}

func (f File) PrimaryPort() bool {
	if f.Kind == "source" {
		return true
	}
	return primaryPorts[f.OS+"/"+f.Arch]
}

func (f File) Highlight() bool {
	switch {
	case f.Kind == "source":
		return true
	case f.Arch == "amd64" && f.OS == "linux":
		return true
	case f.Arch == "amd64" && f.Kind == "installer":
		switch f.OS {
		case "windows":
			return true
		case "darwin":
			if !strings.Contains(f.Filename, "osx10.6") {
				return true
			}
		}
	}
	return false
}

func (f File) URL() string {
	return downloadBaseURL + f.Filename
}

type Release struct {
	Version        string `json:"version"`
	Stable         bool   `json:"stable"`
	Files          []File `json:"files"`
	Visible        bool   `json:"-"` // show files on page load
	SplitPortTable bool   `json:"-"` // whether files should be split by primary/other ports.
}

type Feature struct {
	// The File field will be filled in by the first stable File
	// whose name matches the given fileRE.
	File
	fileRE *regexp.Regexp

	Platform     string // "Microsoft Windows", "Apple macOS", "Linux"
	Requirements string // "Windows XP and above, 64-bit Intel Processor"
}

// featuredFiles lists the platforms and files to be featured
// at the top of the downloads page.
var featuredFiles = []Feature{
	{
		Platform:     "Microsoft Windows",
		Requirements: "Windows 7 or later, Intel 64-bit processor",
		fileRE:       regexp.MustCompile(`\.windows-amd64\.msi$`),
	},
	{
		Platform:     "Apple macOS",
		Requirements: "macOS 10.10 or later, Intel 64-bit processor",
		fileRE:       regexp.MustCompile(`\.darwin-amd64(-osx10\.8)?\.pkg$`),
	},
	{
		Platform:     "Linux",
		Requirements: "Linux 2.6.23 or later, Intel 64-bit processor",
		fileRE:       regexp.MustCompile(`\.linux-amd64\.tar\.gz$`),
	},
	{
		Platform: "Source",
		fileRE:   regexp.MustCompile(`\.src\.tar\.gz$`),
	},
}

// data to send to the template; increment cacheKey if you change this.
type listTemplateData struct {
	Featured                  []Feature
	Stable, Unstable, Archive []Release
	GoogleCN                  bool
}

var (
	listTemplate  = template.Must(template.New("").Funcs(templateFuncs).Parse(templateHTML))
	templateFuncs = template.FuncMap{"pretty": pretty}
)

func filesToFeatured(fs []File) (featured []Feature) {
	for _, feature := range featuredFiles {
		for _, file := range fs {
			if feature.fileRE.MatchString(file.Filename) {
				feature.File = file
				featured = append(featured, feature)
				break
			}
		}
	}
	return
}

func filesToReleases(fs []File) (stable, unstable, archive []Release) {
	sort.Sort(fileOrder(fs))

	var r *Release
	var stableMaj, stableMin int
	add := func() {
		if r == nil {
			return
		}
		if !r.Stable {
			if len(unstable) != 0 {
				// Only show one (latest) unstable version.
				return
			}
			maj, min, _ := parseVersion(r.Version)
			if maj < stableMaj || maj == stableMaj && min <= stableMin {
				// Display unstable version only if newer than the
				// latest stable release.
				return
			}
			unstable = append(unstable, *r)
			return
		}

		// Reports whether the release is the most recent minor version of the
		// two most recent major versions.
		shouldAddStable := func() bool {
			if len(stable) >= 2 {
				// Show up to two stable versions.
				return false
			}
			if len(stable) == 0 {
				// Most recent stable version.
				stableMaj, stableMin, _ = parseVersion(r.Version)
				return true
			}
			if maj, _, _ := parseVersion(r.Version); maj == stableMaj {
				// Older minor version of most recent major version.
				return false
			}
			// Second most recent stable version.
			return true
		}
		if !shouldAddStable() {
			archive = append(archive, *r)
			return
		}

		// Split the file list into primary/other ports for the stable releases.
		// NOTE(cbro): This is only done for stable releases because maintaining the historical
		// nature of primary/other ports for older versions is infeasible.
		// If freebsd is considered primary some time in the future, we'd not want to
		// mark all of the older freebsd binaries as "primary".
		// It might be better if we set that as a flag when uploading.
		r.SplitPortTable = true
		r.Visible = true // Toggle open all stable releases.
		stable = append(stable, *r)
	}
	for _, f := range fs {
		if r == nil || f.Version != r.Version {
			add()
			r = &Release{
				Version: f.Version,
				Stable:  isStable(f.Version),
			}
		}
		r.Files = append(r.Files, f)
	}
	add()
	return
}

// isStable reports whether the version string v is a stable version.
func isStable(v string) bool {
	return !strings.Contains(v, "beta") && !strings.Contains(v, "rc")
}

type fileOrder []File

func (s fileOrder) Len() int      { return len(s) }
func (s fileOrder) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s fileOrder) Less(i, j int) bool {
	a, b := s[i], s[j]
	if av, bv := a.Version, b.Version; av != bv {
		return versionLess(av, bv)
	}
	if a.OS != b.OS {
		return a.OS < b.OS
	}
	if a.Arch != b.Arch {
		return a.Arch < b.Arch
	}
	if a.Kind != b.Kind {
		return a.Kind < b.Kind
	}
	return a.Filename < b.Filename
}

func versionLess(a, b string) bool {
	// Put stable releases first.
	if isStable(a) != isStable(b) {
		return isStable(a)
	}
	maja, mina, ta := parseVersion(a)
	majb, minb, tb := parseVersion(b)
	if maja == majb {
		if mina == minb {
			return ta >= tb
		}
		return mina >= minb
	}
	return maja >= majb
}

func parseVersion(v string) (maj, min int, tail string) {
	if i := strings.Index(v, "beta"); i > 0 {
		tail = v[i:]
		v = v[:i]
	}
	if i := strings.Index(v, "rc"); i > 0 {
		tail = v[i:]
		v = v[:i]
	}
	p := strings.Split(strings.TrimPrefix(v, "go1."), ".")
	maj, _ = strconv.Atoi(p[0])
	if len(p) < 2 {
		return
	}
	min, _ = strconv.Atoi(p[1])
	return
}

func validUser(user string) bool {
	switch user {
	case "adg", "bradfitz", "cbro", "andybons", "valsorda", "dmitshur", "katiehockman", "julieqiu":
		return true
	}
	return false
}

var (
	fileRe  = regexp.MustCompile(`^go[0-9a-z.]+\.[0-9a-z.-]+\.(tar\.gz|pkg|msi|zip)$`)
	goGetRe = regexp.MustCompile(`^go[0-9a-z.]+\.[0-9a-z.-]+$`)
)

// pretty returns a human-readable version of the given OS, Arch, or Kind.
func pretty(s string) string {
	t, ok := prettyStrings[s]
	if !ok {
		return s
	}
	return t
}

var prettyStrings = map[string]string{
	"darwin":  "macOS",
	"freebsd": "FreeBSD",
	"linux":   "Linux",
	"windows": "Windows",

	"386":    "x86",
	"amd64":  "x86-64",
	"armv6l": "ARMv6",
	"arm64":  "ARMv8",

	"archive":   "Archive",
	"installer": "Installer",
	"source":    "Source",
}
<commit_msg>internal/dl: add rakoczy to validUser<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

// Package dl implements a simple downloads frontend server.
//
// It accepts HTTP POST requests to create a new download metadata entity, and
// lists entities with sorting and filtering.
// It is designed to run only on the instance of godoc that serves golang.org.
//
// The package also serves the list of downloads at:
//     https://golang.org/dl/
//
// An optional query param, mode=json, serves the list of stable release
// downloads in JSON format:
//     https://golang.org/dl/?mode=json
//
// An additional query param, include=all, when used with the mode=json
// query param, will serve a full list of available downloads, including
// stable, unstable, and archived releases in JSON format:
//     https://golang.org/dl/?mode=json&include=all
package dl

import (
	"fmt"
	"html/template"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	downloadBaseURL = "https://dl.google.com/go/"
	cacheKey        = "download_list_4" // increment if listTemplateData changes
	cacheDuration   = time.Hour
)

// File represents a file on the golang.org downloads page.
// It should be kept in sync with the upload code in x/build/cmd/release.
type File struct {
	Filename       string    `json:"filename"`
	OS             string    `json:"os"`
	Arch           string    `json:"arch"`
	Version        string    `json:"version"`
	Checksum       string    `json:"-" datastore:",noindex"` // SHA1; deprecated
	ChecksumSHA256 string    `json:"sha256" datastore:",noindex"`
	Size           int64     `json:"size" datastore:",noindex"`
	Kind           string    `json:"kind"` // "archive", "installer", "source"
	Uploaded       time.Time `json:"-"`
}

func (f File) ChecksumType() string {
	if f.ChecksumSHA256 != "" {
		return "SHA256"
	}
	return "SHA1"
}

func (f File) PrettyChecksum() string {
	if f.ChecksumSHA256 != "" {
		return f.ChecksumSHA256
	}
	return f.Checksum
}

func (f File) PrettyOS() string {
	if f.OS == "darwin" {
		switch {
		case strings.Contains(f.Filename, "osx10.8"):
			return "OS X 10.8+"
		case strings.Contains(f.Filename, "osx10.6"):
			return "OS X 10.6+"
		}
	}
	return pretty(f.OS)
}

func (f File) PrettySize() string {
	const mb = 1 << 20
	if f.Size == 0 {
		return ""
	}
	if f.Size < mb {
		// All Go releases are >1mb, but handle this case anyway.
		return fmt.Sprintf("%v bytes", f.Size)
	}
	return fmt.Sprintf("%.0fMB", float64(f.Size)/mb)
}

var primaryPorts = map[string]bool{
	"darwin/amd64":  true,
	"linux/386":     true,
	"linux/amd64":   true,
	"linux/armv6l":  true,
	"windows/386":   true,
	"windows/amd64": true,
}

func (f File) PrimaryPort() bool {
	if f.Kind == "source" {
		return true
	}
	return primaryPorts[f.OS+"/"+f.Arch]
}

func (f File) Highlight() bool {
	switch {
	case f.Kind == "source":
		return true
	case f.Arch == "amd64" && f.OS == "linux":
		return true
	case f.Arch == "amd64" && f.Kind == "installer":
		switch f.OS {
		case "windows":
			return true
		case "darwin":
			if !strings.Contains(f.Filename, "osx10.6") {
				return true
			}
		}
	}
	return false
}

func (f File) URL() string {
	return downloadBaseURL + f.Filename
}

type Release struct {
	Version        string `json:"version"`
	Stable         bool   `json:"stable"`
	Files          []File `json:"files"`
	Visible        bool   `json:"-"` // show files on page load
	SplitPortTable bool   `json:"-"` // whether files should be split by primary/other ports.
}

type Feature struct {
	// The File field will be filled in by the first stable File
	// whose name matches the given fileRE.
	File
	fileRE *regexp.Regexp

	Platform     string // "Microsoft Windows", "Apple macOS", "Linux"
	Requirements string // "Windows XP and above, 64-bit Intel Processor"
}

// featuredFiles lists the platforms and files to be featured
// at the top of the downloads page.
var featuredFiles = []Feature{
	{
		Platform:     "Microsoft Windows",
		Requirements: "Windows 7 or later, Intel 64-bit processor",
		fileRE:       regexp.MustCompile(`\.windows-amd64\.msi$`),
	},
	{
		Platform:     "Apple macOS",
		Requirements: "macOS 10.10 or later, Intel 64-bit processor",
		fileRE:       regexp.MustCompile(`\.darwin-amd64(-osx10\.8)?\.pkg$`),
	},
	{
		Platform:     "Linux",
		Requirements: "Linux 2.6.23 or later, Intel 64-bit processor",
		fileRE:       regexp.MustCompile(`\.linux-amd64\.tar\.gz$`),
	},
	{
		Platform: "Source",
		fileRE:   regexp.MustCompile(`\.src\.tar\.gz$`),
	},
}

// data to send to the template; increment cacheKey if you change this.
type listTemplateData struct {
	Featured                  []Feature
	Stable, Unstable, Archive []Release
	GoogleCN                  bool
}

var (
	listTemplate  = template.Must(template.New("").Funcs(templateFuncs).Parse(templateHTML))
	templateFuncs = template.FuncMap{"pretty": pretty}
)

func filesToFeatured(fs []File) (featured []Feature) {
	for _, feature := range featuredFiles {
		for _, file := range fs {
			if feature.fileRE.MatchString(file.Filename) {
				feature.File = file
				featured = append(featured, feature)
				break
			}
		}
	}
	return
}

func filesToReleases(fs []File) (stable, unstable, archive []Release) {
	sort.Sort(fileOrder(fs))

	var r *Release
	var stableMaj, stableMin int
	add := func() {
		if r == nil {
			return
		}
		if !r.Stable {
			if len(unstable) != 0 {
				// Only show one (latest) unstable version.
				return
			}
			maj, min, _ := parseVersion(r.Version)
			if maj < stableMaj || maj == stableMaj && min <= stableMin {
				// Display unstable version only if newer than the
				// latest stable release.
				return
			}
			unstable = append(unstable, *r)
			return
		}

		// Reports whether the release is the most recent minor version of the
		// two most recent major versions.
		shouldAddStable := func() bool {
			if len(stable) >= 2 {
				// Show up to two stable versions.
				return false
			}
			if len(stable) == 0 {
				// Most recent stable version.
				stableMaj, stableMin, _ = parseVersion(r.Version)
				return true
			}
			if maj, _, _ := parseVersion(r.Version); maj == stableMaj {
				// Older minor version of most recent major version.
				return false
			}
			// Second most recent stable version.
			return true
		}
		if !shouldAddStable() {
			archive = append(archive, *r)
			return
		}

		// Split the file list into primary/other ports for the stable releases.
		// NOTE(cbro): This is only done for stable releases because maintaining the historical
		// nature of primary/other ports for older versions is infeasible.
		// If freebsd is considered primary some time in the future, we'd not want to
		// mark all of the older freebsd binaries as "primary".
		// It might be better if we set that as a flag when uploading.
		r.SplitPortTable = true
		r.Visible = true // Toggle open all stable releases.
		stable = append(stable, *r)
	}
	for _, f := range fs {
		if r == nil || f.Version != r.Version {
			add()
			r = &Release{
				Version: f.Version,
				Stable:  isStable(f.Version),
			}
		}
		r.Files = append(r.Files, f)
	}
	add()
	return
}

// isStable reports whether the version string v is a stable version.
func isStable(v string) bool {
	return !strings.Contains(v, "beta") && !strings.Contains(v, "rc")
}

type fileOrder []File

func (s fileOrder) Len() int      { return len(s) }
func (s fileOrder) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s fileOrder) Less(i, j int) bool {
	a, b := s[i], s[j]
	if av, bv := a.Version, b.Version; av != bv {
		return versionLess(av, bv)
	}
	if a.OS != b.OS {
		return a.OS < b.OS
	}
	if a.Arch != b.Arch {
		return a.Arch < b.Arch
	}
	if a.Kind != b.Kind {
		return a.Kind < b.Kind
	}
	return a.Filename < b.Filename
}

func versionLess(a, b string) bool {
	// Put stable releases first.
	if isStable(a) != isStable(b) {
		return isStable(a)
	}
	maja, mina, ta := parseVersion(a)
	majb, minb, tb := parseVersion(b)
	if maja == majb {
		if mina == minb {
			return ta >= tb
		}
		return mina >= minb
	}
	return maja >= majb
}

func parseVersion(v string) (maj, min int, tail string) {
	if i := strings.Index(v, "beta"); i > 0 {
		tail = v[i:]
		v = v[:i]
	}
	if i := strings.Index(v, "rc"); i > 0 {
		tail = v[i:]
		v = v[:i]
	}
	p := strings.Split(strings.TrimPrefix(v, "go1."), ".")
	maj, _ = strconv.Atoi(p[0])
	if len(p) < 2 {
		return
	}
	min, _ = strconv.Atoi(p[1])
	return
}

func validUser(user string) bool {
	switch user {
	case "adg", "bradfitz", "cbro", "andybons", "valsorda", "dmitshur", "katiehockman", "julieqiu", "rakoczy":
		return true
	}
	return false
}

var (
	fileRe  = regexp.MustCompile(`^go[0-9a-z.]+\.[0-9a-z.-]+\.(tar\.gz|pkg|msi|zip)$`)
	goGetRe = regexp.MustCompile(`^go[0-9a-z.]+\.[0-9a-z.-]+$`)
)

// pretty returns a human-readable version of the given OS, Arch, or Kind.
func pretty(s string) string {
	t, ok := prettyStrings[s]
	if !ok {
		return s
	}
	return t
}

var prettyStrings = map[string]string{
	"darwin":  "macOS",
	"freebsd": "FreeBSD",
	"linux":   "Linux",
	"windows": "Windows",

	"386":    "x86",
	"amd64":  "x86-64",
	"armv6l": "ARMv6",
	"arm64":  "ARMv8",

	"archive":   "Archive",
	"installer": "Installer",
	"source":    "Source",
}
<|endoftext|>