<commit_before>package plugins

import (
    "fmt"
    "github.com/Seklfreak/Robyul2/helpers"
    "github.com/bwmarrin/discordgo"
    "strings"
    "time"
    rethink "github.com/gorethink/gorethink"
)

type Bias struct{}

type AssignableRole_Channel struct {
    ID         string  `gorethink:"id,omitempty"`
    ServerID   string  `gorethink:"serverid"`
    ChannelID  string  `gorethink:"channelid"`
    Categories []AssignableRole_Category  `gorethink:"categories"`
}

type AssignableRole_Category struct {
    Label   string
    Pool    string
    Hidden  bool
    Limit   int
    Roles   []AssignableRole_Role
    Message string
}

type AssignableRole_Role struct {
    Name    string
    Print   string
    Aliases []string
}

func (m *Bias) Commands() []string {
    return []string{
        "bias",
    }
}

var (
    biasChannels []AssignableRole_Channel
)

func (m *Bias) Init(session *discordgo.Session) {
    biasChannels = m.GetBiasChannels()
}

func (m *Bias) Action(command string, content string, msg *discordgo.Message, session *discordgo.Session) {
    args := strings.Fields(content)
    if len(args) >= 1 {
        switch args[0] {
        case "help":
            helpers.RequireAdmin(msg, func() {
                session.ChannelTyping(msg.ChannelID)
                for _, biasChannel := range biasChannels {
                    if msg.ChannelID == biasChannel.ChannelID {
                        exampleRoleName := ""
                        biasListText := ""
                        for _, biasCategory := range biasChannel.Categories {
                            if biasCategory.Hidden == true {
                                continue
                            }
                            if biasCategory.Message != "" {
                                biasListText += "\n" + biasCategory.Message
                            }
                            biasListText += fmt.Sprintf("\n%s: ", biasCategory.Label)
                            for i, biasRole := range biasCategory.Roles {
                                if exampleRoleName == "" {
                                    exampleRoleName = biasRole.Print
                                }
                                if i != 0 {
                                    if i+1 < len(biasCategory.Roles) {
                                        biasListText += ", "
                                    } else {
                                        biasListText += " and "
                                    }
                                }
                                biasListText += fmt.Sprintf("**`%s`**", biasRole.Print)
                            }
                            calculatedLimit := biasCategory.Limit
                            if biasCategory.Pool != "" {
                                calculatedLimit = 0
                                for _, poolCategorie := range biasChannel.Categories {
                                    if poolCategorie.Pool == biasCategory.Pool {
                                        calculatedLimit += poolCategorie.Limit
                                    }
                                }
                            }
                            if calculatedLimit == 1 {
                                biasListText += " (**`One Role`** Max)"
                            } else if calculatedLimit > 1 {
                                biasListText += fmt.Sprintf(" (**`%s Roles`** Max)", strings.Title(helpers.HumanizeNumber(calculatedLimit)))
                            }
                        }
                        for _, page := range helpers.Pagify(helpers.GetTextF("plugins.bias.bias-help-message",
                            biasListText, exampleRoleName, exampleRoleName), ",") {
                            session.ChannelMessageSend(msg.ChannelID, page)
                        }
                        return
                    }
                }

                _, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.bias.no-bias-config"))
                helpers.Relax(err)
            })
        case "refresh":
            helpers.RequireAdmin(msg, func() {
                session.ChannelTyping(msg.ChannelID)
                biasChannels = m.GetBiasChannels()
                _, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.bias.refreshed-config"))
                helpers.Relax(err)
            })
        case "new-config":
            helpers.RequireBotAdmin(msg, func() {
                session.ChannelTyping(msg.ChannelID)

                insert := rethink.Table("bias").Insert(AssignableRole_Channel{})
                _, err := insert.RunWrite(helpers.GetDB())
                helpers.Relax(err)

                _, err = session.ChannelMessageSend(msg.ChannelID, "Created a new entry in the Database. Please fill it manually.")
                helpers.Relax(err)
            })
        case "stats":
            session.ChannelTyping(msg.ChannelID)

            channel, err := session.State.Channel(msg.ChannelID)
            helpers.Relax(err)
            guild, err := session.State.Guild(channel.GuildID)
            helpers.Relax(err)

            members := make([]*discordgo.Member, 0)
            lastAfterMemberId := ""
            for {
                additionalMembers, err := session.GuildMembers(guild.ID, lastAfterMemberId, 1000)
                if len(additionalMembers) <= 0 {
                    break
                }
                lastAfterMemberId = additionalMembers[len(additionalMembers)-1].User.ID
                helpers.Relax(err)
                for _, member := range additionalMembers {
                    members = append(members, member)
                }
            }

            statsText := ""

            statsPrinted := 0
            for _, biasChannel := range biasChannels {
                if biasChannel.ServerID == channel.GuildID {
                    for _, biasCategory := range biasChannel.Categories {
                        categoryNumbers := make(map[string]int, 0)
                        if biasCategory.Hidden == true && biasCategory.Pool == "" {
                            continue
                        }
                        for _, biasRole := range biasCategory.Roles {
                            discordRole := m.GetDiscordRole(biasRole, guild)
                            if discordRole != nil {
                                for _, member := range members {
                                    for _, memberRole := range member.Roles {
                                        if memberRole == discordRole.ID {
                                            // user has the role
                                            if _, ok := categoryNumbers[discordRole.Name]; ok {
                                                categoryNumbers[discordRole.Name]++
                                            } else {
                                                categoryNumbers[discordRole.Name] = 1
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        if len(categoryNumbers) > 0 {
                            statsText += fmt.Sprintf("__**%s:**__\n", biasCategory.Label)
                            for roleName, roleNumber := range categoryNumbers {
                                statsText += fmt.Sprintf("**%s**: %d Members\n", roleName, roleNumber)
                            }
                        }
                    }
                    statsPrinted++
                }
            }

            if statsPrinted <= 0 {
                _, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.bias.no-stats"))
                helpers.Relax(err)
            } else {
                for _, page := range helpers.Pagify(statsText, "\n") {
                    _, err := session.ChannelMessageSend(msg.ChannelID, page)
                    helpers.Relax(err)
                }
            }
        }
    }
}

func (m *Bias) OnMessage(content string, msg *discordgo.Message, session *discordgo.Session) {
    for _, biasChannel := range biasChannels {
        if msg.ChannelID == biasChannel.ChannelID {
            channel, err := session.Channel(msg.ChannelID)
            helpers.Relax(err)
            guild, err := session.Guild(channel.GuildID)
            helpers.Relax(err)
            member, err := session.GuildMember(guild.ID, msg.Author.ID)
            helpers.Relax(err)
            guildRoles, err := session.GuildRoles(guild.ID)
            if err != nil {
                if err, ok := err.(*discordgo.RESTError); ok && err.Message.Code == 50013 {
                    newMessage, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.bias.generic-error"))
                    helpers.Relax(err)
                    // Delete messages after ten seconds
                    time.Sleep(10 * time.Second)
                    session.ChannelMessageDelete(newMessage.ChannelID, newMessage.ID)
                    session.ChannelMessageDelete(msg.ChannelID, msg.ID)
                    return
                } else {
                    helpers.Relax(err)
                }
            }
            helpers.Relax(err)
            var messagesToDelete []*discordgo.Message
            messagesToDelete = append(messagesToDelete, msg)
            var requestIsAddRole bool
            isRequest := false
            if strings.HasPrefix(content, "+") {
                requestIsAddRole = true
                isRequest = true
            } else if strings.HasPrefix(content, "-") {
                requestIsAddRole = false
                isRequest = true
            }
            if isRequest == true {
                requestedRoleName := m.CleanUpRoleName(content)
                denyReason := ""
                type Role_Information struct {
                    Role        AssignableRole_Role
                    DiscordRole *discordgo.Role
                }
                var roleToAddOrDelete Role_Information
            FindRoleLoop:
                for _, category := range biasChannel.Categories {
                TryRoleLoop:
                    for _, role := range category.Roles {
                        for _, label := range role.Aliases {
                            if strings.ToLower(label) == requestedRoleName {
                                discordRole := m.GetDiscordRole(role, guild)
                                if discordRole != nil && discordRole.ID != "" {
                                    memberHasRole := m.MemberHasRole(member, discordRole)
                                    if requestIsAddRole == true && memberHasRole == true {
                                        denyReason = helpers.GetText("plugins.bias.add-role-already")
                                        continue TryRoleLoop
                                    }
                                    if requestIsAddRole == false && memberHasRole == false {
                                        denyReason = helpers.GetText("plugins.bias.remove-role-not-found")
                                        continue TryRoleLoop
                                    }
                                    categoryRolesAssigned := m.CategoryRolesAssigned(member, guildRoles, category)
                                    if requestIsAddRole == true && (category.Limit >= 0 && len(categoryRolesAssigned) >= category.Limit) {
                                        denyReason = helpers.GetText("plugins.bias.role-limit-reached")
                                        continue TryRoleLoop
                                    }
                                    if requestIsAddRole == true && category.Pool != "" {
                                        for _, poolCategories := range biasChannel.Categories {
                                            if poolCategories.Pool == category.Pool {
                                                for _, poolRole := range poolCategories.Roles {
                                                    if poolRole.Print == role.Print {
                                                        poolDiscordRole := m.GetDiscordRole(poolRole, guild)
                                                        if poolDiscordRole != nil && poolDiscordRole.ID != "" && m.MemberHasRole(member, poolDiscordRole) {
                                                            denyReason = helpers.GetText("plugins.bias.add-role-already")
                                                            continue TryRoleLoop
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }

                                    roleToAddOrDelete = Role_Information{Role: role, DiscordRole: discordRole}

                                    break FindRoleLoop
                                }

                            }
                        }
                    }
                }
                if roleToAddOrDelete.Role.Name != "" && roleToAddOrDelete.DiscordRole != nil {
                    if requestIsAddRole == true {
                        err := session.GuildMemberRoleAdd(guild.ID, msg.Author.ID, roleToAddOrDelete.DiscordRole.ID)
                        if err != nil {
                            newMessage, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.bias.generic-error"))
                            helpers.Relax(err)
                            messagesToDelete = append(messagesToDelete, newMessage)
                        } else {
                            newMessage, err := session.ChannelMessageSend(msg.ChannelID, fmt.Sprintf("<@%s> %s", msg.Author.ID, helpers.GetText("plugins.bias.role-added")))
                            helpers.Relax(err)
                            messagesToDelete = append(messagesToDelete, newMessage)
                        }
                    } else {
                        err := session.GuildMemberRoleRemove(guild.ID, msg.Author.ID, roleToAddOrDelete.DiscordRole.ID)
                        if err != nil {
                            newMessage, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.bias.generic-error"))
                            helpers.Relax(err)
                            messagesToDelete = append(messagesToDelete, newMessage)
                        } else {
                            newMessage, err := session.ChannelMessageSend(msg.ChannelID, fmt.Sprintf("<@%s> %s", msg.Author.ID, helpers.GetText("plugins.bias.role-removed")))
                            helpers.Relax(err)
                            messagesToDelete = append(messagesToDelete, newMessage)
                        }
                    }
                } else if denyReason != "" {
                    newMessage, err := session.ChannelMessageSend(msg.ChannelID, fmt.Sprintf("<@%s> %s", msg.Author.ID, denyReason))
                    helpers.Relax(err)
                    messagesToDelete = append(messagesToDelete, newMessage)
                } else {
                    newMessage, err := session.ChannelMessageSend(msg.ChannelID, fmt.Sprintf("<@%s> %s", msg.Author.ID, helpers.GetText("plugins.bias.role-not-found")))
                    helpers.Relax(err)
                    messagesToDelete = append(messagesToDelete, newMessage)
                }
            }
            // Delete messages after ten seconds
            time.Sleep(10 * time.Second)
            for _, messagsToDelete := range messagesToDelete {
                session.ChannelMessageDelete(msg.ChannelID, messagsToDelete.ID)
            }
        }
    }
}

func (m *Bias) GetBiasChannels() []AssignableRole_Channel {
    var entryBucket []AssignableRole_Channel
    cursor, err := rethink.Table("bias").Run(helpers.GetDB())
    helpers.Relax(err)

    err = cursor.All(&entryBucket)
    helpers.Relax(err)

    return entryBucket
}

func (m *Bias) CategoryRolesAssigned(member *discordgo.Member, guildRoles []*discordgo.Role, category AssignableRole_Category) []AssignableRole_Role {
    var rolesAssigned []AssignableRole_Role
    for _, discordRoleId := range member.Roles {
        for _, discordGuildRole := range guildRoles {
            if discordRoleId == discordGuildRole.ID {
                for _, assignableRole := range category.Roles {
                    if strings.ToLower(assignableRole.Name) == strings.ToLower(discordGuildRole.Name) {
                        rolesAssigned = append(rolesAssigned, assignableRole)
                    }
                }
            }
        }
    }

    return rolesAssigned
}

func (m *Bias) GetDiscordRole(role AssignableRole_Role, guild *discordgo.Guild) *discordgo.Role {
    for _, discordRole := range guild.Roles {
        if strings.ToLower(role.Name) == strings.ToLower(discordRole.Name) {
            return discordRole
        }
    }
    var discordRole *discordgo.Role
    return discordRole
}

func (m *Bias) MemberHasRole(member *discordgo.Member, role *discordgo.Role) bool {
    for _, memberRole := range member.Roles {
        if memberRole == role.ID {
            return true
        }
    }
    return false
}

func (m *Bias) CleanUpRoleName(inputName string) string {
    inputName = strings.TrimPrefix(inputName, "+")
    inputName = strings.TrimPrefix(inputName, "-")
    inputName = strings.TrimSpace(inputName)
    inputName = strings.TrimPrefix(inputName, "name")
    inputName = strings.TrimSpace(inputName)
    inputName = strings.ToLower(inputName)
    return inputName
}

func (m *Bias) OnGuildMemberAdd(member *discordgo.Member, session *discordgo.Session) {

}
func (m *Bias) OnGuildMemberRemove(member *discordgo.Member, session *discordgo.Session) {

}
func (m *Bias) OnReactionAdd(reaction *discordgo.MessageReactionAdd, session *discordgo.Session) {

}
func (m *Bias) OnReactionRemove(reaction *discordgo.MessageReactionRemove, session *discordgo.Session) {

}
func (m *Bias) OnGuildBanAdd(user *discordgo.GuildBanAdd, session *discordgo.Session) {

}
func (m *Bias) OnGuildBanRemove(user *discordgo.GuildBanRemove, session *discordgo.Session) {

}
<commit_msg>[bias] sort [p]bias stats by member amount<commit_after>package plugins

import (
    "fmt"
    "github.com/Seklfreak/Robyul2/helpers"
    "github.com/bwmarrin/discordgo"
    "strings"
    "time"
    rethink "github.com/gorethink/gorethink"
    "sort"
)

type Bias struct{}

type AssignableRole_Channel struct {
    ID         string  `gorethink:"id,omitempty"`
    ServerID   string  `gorethink:"serverid"`
    ChannelID  string  `gorethink:"channelid"`
    Categories []AssignableRole_Category  `gorethink:"categories"`
}

type AssignableRole_Category struct {
    Label   string
    Pool    string
    Hidden  bool
    Limit   int
    Roles   []AssignableRole_Role
    Message string
}

type AssignableRole_Role struct {
    Name    string
    Print   string
    Aliases []string
}

func (m *Bias) Commands() []string {
    return []string{
        "bias",
    }
}

var (
    biasChannels []AssignableRole_Channel
)

func (m *Bias) Init(session *discordgo.Session) {
    biasChannels = m.GetBiasChannels()
}

func (m *Bias) Action(command string, content string, msg *discordgo.Message, session *discordgo.Session) {
    args := strings.Fields(content)
    if len(args) >= 1 {
        switch args[0] {
        case "help":
            helpers.RequireAdmin(msg, func() {
                session.ChannelTyping(msg.ChannelID)
                for _, biasChannel := range biasChannels {
                    if msg.ChannelID == biasChannel.ChannelID {
                        exampleRoleName := ""
                        biasListText := ""
                        for _, biasCategory := range biasChannel.Categories {
                            if biasCategory.Hidden == true {
                                continue
                            }
                            if biasCategory.Message != "" {
                                biasListText += "\n" + biasCategory.Message
                            }
                            biasListText += fmt.Sprintf("\n%s: ", biasCategory.Label)
                            for i, biasRole := range biasCategory.Roles {
                                if exampleRoleName == "" {
                                    exampleRoleName = biasRole.Print
                                }
                                if i != 0 {
                                    if i+1 < len(biasCategory.Roles) {
                                        biasListText += ", "
                                    } else {
                                        biasListText += " and "
                                    }
                                }
                                biasListText += fmt.Sprintf("**`%s`**", biasRole.Print)
                            }
                            calculatedLimit := biasCategory.Limit
                            if biasCategory.Pool != "" {
                                calculatedLimit = 0
                                for _, poolCategorie := range biasChannel.Categories {
                                    if poolCategorie.Pool == biasCategory.Pool {
                                        calculatedLimit += poolCategorie.Limit
                                    }
                                }
                            }
                            if calculatedLimit == 1 {
                                biasListText += " (**`One Role`** Max)"
                            } else if calculatedLimit > 1 {
                                biasListText += fmt.Sprintf(" (**`%s Roles`** Max)", strings.Title(helpers.HumanizeNumber(calculatedLimit)))
                            }
                        }
                        for _, page := range helpers.Pagify(helpers.GetTextF("plugins.bias.bias-help-message",
                            biasListText, exampleRoleName, exampleRoleName), ",") {
                            session.ChannelMessageSend(msg.ChannelID, page)
                        }
                        return
                    }
                }

                _, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.bias.no-bias-config"))
                helpers.Relax(err)
            })
        case "refresh":
            helpers.RequireAdmin(msg, func() {
                session.ChannelTyping(msg.ChannelID)
                biasChannels = m.GetBiasChannels()
                _, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.bias.refreshed-config"))
                helpers.Relax(err)
            })
        case "new-config":
            helpers.RequireBotAdmin(msg, func() {
                session.ChannelTyping(msg.ChannelID)

                insert := rethink.Table("bias").Insert(AssignableRole_Channel{})
                _, err := insert.RunWrite(helpers.GetDB())
                helpers.Relax(err)

                _, err = session.ChannelMessageSend(msg.ChannelID, "Created a new entry in the Database. Please fill it manually.")
                helpers.Relax(err)
            })
        case "stats":
            session.ChannelTyping(msg.ChannelID)

            channel, err := session.State.Channel(msg.ChannelID)
            helpers.Relax(err)
            guild, err := session.State.Guild(channel.GuildID)
            helpers.Relax(err)

            members := make([]*discordgo.Member, 0)
            lastAfterMemberId := ""
            for {
                additionalMembers, err := session.GuildMembers(guild.ID, lastAfterMemberId, 1000)
                if len(additionalMembers) <= 0 {
                    break
                }
                lastAfterMemberId = additionalMembers[len(additionalMembers)-1].User.ID
                helpers.Relax(err)
                for _, member := range additionalMembers {
                    members = append(members, member)
                }
            }

            statsText := ""

            statsPrinted := 0
            for _, biasChannel := range biasChannels {
                if biasChannel.ServerID == channel.GuildID {
                    for _, biasCategory := range biasChannel.Categories {
                        categoryNumbers := make(BiasRoleStatList, 0)
                        if biasCategory.Hidden == true && biasCategory.Pool == "" {
                            continue
                        }
                        for _, biasRole := range biasCategory.Roles {
                            discordRole := m.GetDiscordRole(biasRole, guild)
                            if discordRole != nil {
                                categoryNumbers = append(categoryNumbers, BiasRoleStat{
                                    RoleName: discordRole.Name, Members: 0,
                                })
                                for _, member := range members {
                                    for _, memberRole := range member.Roles {
                                        if memberRole == discordRole.ID {
                                            // user has the role
                                            for i, biasRoleStat := range categoryNumbers {
                                                if biasRoleStat.RoleName == discordRole.Name {
                                                    categoryNumbers[i].Members++
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        sort.Sort(categoryNumbers)
                        if len(categoryNumbers) > 0 {
                            statsText += fmt.Sprintf("__**%s:**__\n", biasCategory.Label)
                            for _, biasRoleStat := range categoryNumbers {
                                statsText += fmt.Sprintf("**%s**: %d Members\n",
                                    biasRoleStat.RoleName, biasRoleStat.Members )
                            }
                        }
                    }
                    statsPrinted++
                }
            }

            if statsPrinted <= 0 {
                _, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.bias.no-stats"))
                helpers.Relax(err)
            } else {
                for _, page := range helpers.Pagify(statsText, "\n") {
                    _, err := session.ChannelMessageSend(msg.ChannelID, page)
                    helpers.Relax(err)
                }
            }
        }
    }
}

func (m *Bias) OnMessage(content string, msg *discordgo.Message, session *discordgo.Session) {
    for _, biasChannel := range biasChannels {
        if msg.ChannelID == biasChannel.ChannelID {
            channel, err := session.Channel(msg.ChannelID)
            helpers.Relax(err)
            guild, err := session.Guild(channel.GuildID)
            helpers.Relax(err)
            member, err := session.GuildMember(guild.ID, msg.Author.ID)
            helpers.Relax(err)
            guildRoles, err := session.GuildRoles(guild.ID)
            if err != nil {
                if err, ok := err.(*discordgo.RESTError); ok && err.Message.Code == 50013 {
                    newMessage, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.bias.generic-error"))
                    helpers.Relax(err)
                    // Delete messages after ten seconds
                    time.Sleep(10 * time.Second)
                    session.ChannelMessageDelete(newMessage.ChannelID, newMessage.ID)
                    session.ChannelMessageDelete(msg.ChannelID, msg.ID)
                    return
                } else {
                    helpers.Relax(err)
                }
            }
            helpers.Relax(err)
            var messagesToDelete []*discordgo.Message
            messagesToDelete = append(messagesToDelete, msg)
            var requestIsAddRole bool
            isRequest := false
            if strings.HasPrefix(content, "+") {
                requestIsAddRole = true
                isRequest = true
            } else if strings.HasPrefix(content, "-") {
                requestIsAddRole = false
                isRequest = true
            }
            if isRequest == true {
                requestedRoleName := m.CleanUpRoleName(content)
                denyReason := ""
                type Role_Information struct {
                    Role        AssignableRole_Role
                    DiscordRole *discordgo.Role
                }
                var roleToAddOrDelete Role_Information
            FindRoleLoop:
                for _, category := range biasChannel.Categories {
                TryRoleLoop:
                    for _, role := range category.Roles {
                        for _, label := range role.Aliases {
                            if strings.ToLower(label) == requestedRoleName {
                                discordRole := m.GetDiscordRole(role, guild)
                                if discordRole != nil && discordRole.ID != "" {
                                    memberHasRole := m.MemberHasRole(member, discordRole)
                                    if requestIsAddRole == true && memberHasRole == true {
                                        denyReason = helpers.GetText("plugins.bias.add-role-already")
                                        continue TryRoleLoop
                                    }
                                    if requestIsAddRole == false && memberHasRole == false {
                                        denyReason = helpers.GetText("plugins.bias.remove-role-not-found")
                                        continue TryRoleLoop
                                    }
                                    categoryRolesAssigned := m.CategoryRolesAssigned(member, guildRoles, category)
                                    if requestIsAddRole == true && (category.Limit >= 0 && len(categoryRolesAssigned) >= category.Limit) {
                                        denyReason = helpers.GetText("plugins.bias.role-limit-reached")
                                        continue TryRoleLoop
                                    }
                                    if requestIsAddRole == true && category.Pool != "" {
                                        for _, poolCategories := range biasChannel.Categories {
                                            if poolCategories.Pool == category.Pool {
                                                for _, poolRole := range poolCategories.Roles {
                                                    if poolRole.Print == role.Print {
                                                        poolDiscordRole := m.GetDiscordRole(poolRole, guild)
                                                        if poolDiscordRole != nil && poolDiscordRole.ID != "" && m.MemberHasRole(member, poolDiscordRole) {
                                                            denyReason = helpers.GetText("plugins.bias.add-role-already")
                                                            continue TryRoleLoop
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }

                                    roleToAddOrDelete = Role_Information{Role: role, DiscordRole: discordRole}

                                    break FindRoleLoop
                                }

                            }
                        }
                    }
                }
                if roleToAddOrDelete.Role.Name != "" && roleToAddOrDelete.DiscordRole != nil {
                    if requestIsAddRole == true {
                        err := session.GuildMemberRoleAdd(guild.ID, msg.Author.ID, roleToAddOrDelete.DiscordRole.ID)
                        if err != nil {
                            newMessage, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.bias.generic-error"))
                            helpers.Relax(err)
                            messagesToDelete = append(messagesToDelete, newMessage)
                        } else {
                            newMessage, err := session.ChannelMessageSend(msg.ChannelID, fmt.Sprintf("<@%s> %s", msg.Author.ID, helpers.GetText("plugins.bias.role-added")))
                            helpers.Relax(err)
                            messagesToDelete = append(messagesToDelete, newMessage)
                        }
                    } else {
                        err := session.GuildMemberRoleRemove(guild.ID, msg.Author.ID, roleToAddOrDelete.DiscordRole.ID)
                        if err != nil {
                            newMessage, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.bias.generic-error"))
                            helpers.Relax(err)
                            messagesToDelete = append(messagesToDelete, newMessage)
                        } else {
                            newMessage, err := session.ChannelMessageSend(msg.ChannelID, fmt.Sprintf("<@%s> %s", msg.Author.ID, helpers.GetText("plugins.bias.role-removed")))
                            helpers.Relax(err)
                            messagesToDelete = append(messagesToDelete, newMessage)
                        }
                    }
                } else if denyReason != "" {
                    newMessage, err := session.ChannelMessageSend(msg.ChannelID, fmt.Sprintf("<@%s> %s", msg.Author.ID, denyReason))
                    helpers.Relax(err)
                    messagesToDelete = append(messagesToDelete, newMessage)
                } else {
                    newMessage, err := session.ChannelMessageSend(msg.ChannelID, fmt.Sprintf("<@%s> %s", msg.Author.ID, helpers.GetText("plugins.bias.role-not-found")))
                    helpers.Relax(err)
                    messagesToDelete = append(messagesToDelete, newMessage)
                }
            }
            // Delete messages after ten seconds
            time.Sleep(10 * time.Second)
            for _, messagsToDelete := range messagesToDelete {
                session.ChannelMessageDelete(msg.ChannelID, messagsToDelete.ID)
            }
        }
    }
}

func (m *Bias) GetBiasChannels() []AssignableRole_Channel {
    var entryBucket []AssignableRole_Channel
    cursor, err := rethink.Table("bias").Run(helpers.GetDB())
    helpers.Relax(err)

    err = cursor.All(&entryBucket)
    helpers.Relax(err)

    return entryBucket
}

func (m *Bias) CategoryRolesAssigned(member *discordgo.Member, guildRoles []*discordgo.Role, category AssignableRole_Category) []AssignableRole_Role {
    var rolesAssigned []AssignableRole_Role
    for _, discordRoleId := range member.Roles {
        for _, discordGuildRole := range guildRoles {
            if discordRoleId == discordGuildRole.ID {
                for _, assignableRole := range category.Roles {
                    if strings.ToLower(assignableRole.Name) == strings.ToLower(discordGuildRole.Name) {
                        rolesAssigned = append(rolesAssigned, assignableRole)
                    }
                }
            }
        }
    }

    return rolesAssigned
}

func (m *Bias) GetDiscordRole(role AssignableRole_Role, guild *discordgo.Guild) *discordgo.Role {
    for _, discordRole := range guild.Roles {
        if strings.ToLower(role.Name) == strings.ToLower(discordRole.Name) {
            return discordRole
        }
    }
    var discordRole *discordgo.Role
    return discordRole
}

func (m *Bias) MemberHasRole(member *discordgo.Member, role *discordgo.Role) bool {
    for _, memberRole := range member.Roles {
        if memberRole == role.ID {
            return true
        }
    }
    return false
}

func (m *Bias) CleanUpRoleName(inputName string) string {
    inputName = strings.TrimPrefix(inputName, "+")
    inputName = strings.TrimPrefix(inputName, "-")
    inputName = strings.TrimSpace(inputName)
    inputName = strings.TrimPrefix(inputName, "name")
    inputName = strings.TrimSpace(inputName)
    inputName = strings.ToLower(inputName)
    return inputName
}

func (m *Bias) OnGuildMemberAdd(member *discordgo.Member, session *discordgo.Session) {

}
func (m *Bias) OnGuildMemberRemove(member *discordgo.Member, session *discordgo.Session) {

}
func (m *Bias) OnReactionAdd(reaction *discordgo.MessageReactionAdd, session *discordgo.Session) {

}
func (m *Bias) OnReactionRemove(reaction *discordgo.MessageReactionRemove, session *discordgo.Session) {

}
func (m *Bias) OnGuildBanAdd(user *discordgo.GuildBanAdd, session *discordgo.Session) {

}
func (m *Bias) OnGuildBanRemove(user *discordgo.GuildBanRemove, session *discordgo.Session) {

}

type BiasRoleStat struct {
    RoleName string
    Members int
}

type BiasRoleStatList []BiasRoleStat

func (p BiasRoleStatList) Len() int { return len(p) }
func (p BiasRoleStatList) Less(i, j int) bool { return p[i].Members > p[j].Members }
func (p BiasRoleStatList) Swap(i, j int){ p[i], p[j] = p[j], p[i] }
<|endoftext|>


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

// +build darwin freebsd linux netbsd openbsd windows

// TCP sockets

package net

import (
	"io"
	"os"
	"syscall"
	"time"
)

// BUG(rsc): On OpenBSD, listening on the "tcp" network does not listen for
// both IPv4 and IPv6 connections. This is due to the fact that IPv4 traffic
// will not be routed to an IPv6 socket - two separate sockets are required
// if both AFs are to be supported. See inet6(4) on OpenBSD for details.

func sockaddrToTCP(sa syscall.Sockaddr) Addr {
	switch sa := sa.(type) {
	case *syscall.SockaddrInet4:
		return &TCPAddr{sa.Addr[0:], sa.Port}
	case *syscall.SockaddrInet6:
		return &TCPAddr{sa.Addr[0:], sa.Port}
	}
	return nil
}

func (a *TCPAddr) family() int {
	if a == nil || len(a.IP) <= IPv4len {
		return syscall.AF_INET
	}
	if a.IP.To4() != nil {
		return syscall.AF_INET
	}
	return syscall.AF_INET6
}

func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
	return ipToSockaddr(family, a.IP, a.Port)
}

func (a *TCPAddr) toAddr() sockaddr {
	if a == nil { // nil *TCPAddr
		return nil // nil interface
	}
	return a
}

// TCPConn is an implementation of the Conn interface
// for TCP network connections.
type TCPConn struct {
	fd *netFD
}

func newTCPConn(fd *netFD) *TCPConn {
	c := &TCPConn{fd}
	c.SetNoDelay(true)
	return c
}

func (c *TCPConn) ok() bool { return c != nil && c.fd != nil }

// Implementation of the Conn interface - see Conn for documentation.

// Read implements the Conn Read method.
func (c *TCPConn) Read(b []byte) (n int, err error) {
	if !c.ok() {
		return 0, syscall.EINVAL
	}
	return c.fd.Read(b)
}

// ReadFrom implements the io.ReaderFrom ReadFrom method.
func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
	if n, err, handled := sendFile(c.fd, r); handled {
		return n, err
	}
	return genericReadFrom(c, r)
}

// Write implements the Conn Write method.
func (c *TCPConn) Write(b []byte) (n int, err error) {
	if !c.ok() {
		return 0, syscall.EINVAL
	}
	return c.fd.Write(b)
}

// Close closes the TCP connection.
func (c *TCPConn) Close() error {
	if !c.ok() {
		return syscall.EINVAL
	}
	err := c.fd.Close()
	c.fd = nil
	return err
}

// CloseRead shuts down the reading side of the TCP connection.
// Most callers should just use Close.
func (c *TCPConn) CloseRead() error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return c.fd.CloseRead()
}

// CloseWrite shuts down the writing side of the TCP connection.
// Most callers should just use Close.
func (c *TCPConn) CloseWrite() error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return c.fd.CloseWrite()
}

// LocalAddr returns the local network address, a *TCPAddr.
func (c *TCPConn) LocalAddr() Addr {
	if !c.ok() {
		return nil
	}
	return c.fd.laddr
}

// RemoteAddr returns the remote network address, a *TCPAddr.
func (c *TCPConn) RemoteAddr() Addr {
	if !c.ok() {
		return nil
	}
	return c.fd.raddr
}

// SetDeadline implements the Conn SetDeadline method.
func (c *TCPConn) SetDeadline(t time.Time) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setDeadline(c.fd, t)
}

// SetReadDeadline implements the Conn SetReadDeadline method.
func (c *TCPConn) SetReadDeadline(t time.Time) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setReadDeadline(c.fd, t)
}

// SetWriteDeadline implements the Conn SetWriteDeadline method.
func (c *TCPConn) SetWriteDeadline(t time.Time) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setWriteDeadline(c.fd, t)
}

// SetReadBuffer sets the size of the operating system's
// receive buffer associated with the connection.
func (c *TCPConn) SetReadBuffer(bytes int) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setReadBuffer(c.fd, bytes)
}

// SetWriteBuffer sets the size of the operating system's
// transmit buffer associated with the connection.
func (c *TCPConn) SetWriteBuffer(bytes int) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setWriteBuffer(c.fd, bytes)
}

// SetLinger sets the behavior of Close() on a connection
// which still has data waiting to be sent or to be acknowledged.
//
// If sec < 0 (the default), Close returns immediately and
// the operating system finishes sending the data in the background.
//
// If sec == 0, Close returns immediately and the operating system
// discards any unsent or unacknowledged data.
//
// If sec > 0, Close blocks for at most sec seconds waiting for
// data to be sent and acknowledged.
func (c *TCPConn) SetLinger(sec int) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setLinger(c.fd, sec)
}

// SetKeepAlive sets whether the operating system should send
// keepalive messages on the connection.
func (c *TCPConn) SetKeepAlive(keepalive bool) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setKeepAlive(c.fd, keepalive)
}

// SetNoDelay controls whether the operating system should delay
// packet transmission in hopes of sending fewer packets
// (Nagle's algorithm).  The default is true (no delay), meaning
// that data is sent as soon as possible after a Write.
func (c *TCPConn) SetNoDelay(noDelay bool) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setNoDelay(c.fd, noDelay)
}

// File returns a copy of the underlying os.File, set to blocking mode.
// It is the caller's responsibility to close f when finished.
// Closing c does not affect f, and closing f does not affect c.
func (c *TCPConn) File() (f *os.File, err error) { return c.fd.dup() }

// DialTCP connects to the remote address raddr on the network net,
// which must be "tcp", "tcp4", or "tcp6".  If laddr is not nil, it is used
// as the local address for the connection.
func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
	if raddr == nil {
		return nil, &OpError{"dial", net, nil, errMissingAddress}
	}

	fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)

	// TCP has a rarely used mechanism called a 'simultaneous connection' in
	// which Dial("tcp", addr1, addr2) run on the machine at addr1 can
	// connect to a simultaneous Dial("tcp", addr2, addr1) run on the machine
	// at addr2, without either machine executing Listen.  If laddr == nil,
	// it means we want the kernel to pick an appropriate originating local
	// address.  Some Linux kernels cycle blindly through a fixed range of
	// local ports, regardless of destination port.  If a kernel happens to
	// pick local port 50001 as the source for a Dial("tcp", "", "localhost:50001"),
	// then the Dial will succeed, having simultaneously connected to itself.
	// This can only happen when we are letting the kernel pick a port (laddr == nil)
	// and when there is no listener for the destination address.
	// It's hard to argue this is anything other than a kernel bug.  If we
	// see this happen, rather than expose the buggy effect to users, we
	// close the fd and try again.  If it happens twice more, we relent and
	// use the result.  See also:
	//	http://golang.org/issue/2690
	//	http://stackoverflow.com/questions/4949858/
	for i := 0; i < 2 && err == nil && laddr == nil && selfConnect(fd); i++ {
		fd.Close()
		fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
	}

	if err != nil {
		return nil, err
	}
	return newTCPConn(fd), nil
}

func selfConnect(fd *netFD) bool {
	l := fd.laddr.(*TCPAddr)
	r := fd.raddr.(*TCPAddr)
	return l.Port == r.Port && l.IP.Equal(r.IP)
}

// TCPListener is a TCP network listener.
// Clients should typically use variables of type Listener
// instead of assuming TCP.
type TCPListener struct {
	fd *netFD
}

// ListenTCP announces on the TCP address laddr and returns a TCP listener.
// Net must be "tcp", "tcp4", or "tcp6".
// If laddr has a port of 0, it means to listen on some available port.
// The caller can use l.Addr() to retrieve the chosen address.
func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
	fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP)
	if err != nil {
		return nil, err
	}
	err = syscall.Listen(fd.sysfd, listenerBacklog)
	if err != nil {
		closesocket(fd.sysfd)
		return nil, &OpError{"listen", net, laddr, err}
	}
	l := new(TCPListener)
	l.fd = fd
	return l, nil
}

// AcceptTCP accepts the next incoming call and returns the new connection
// and the remote address.
func (l *TCPListener) AcceptTCP() (c *TCPConn, err error) {
	if l == nil || l.fd == nil || l.fd.sysfd < 0 {
		return nil, syscall.EINVAL
	}
	fd, err := l.fd.accept(sockaddrToTCP)
	if err != nil {
		return nil, err
	}
	return newTCPConn(fd), nil
}

// Accept implements the Accept method in the Listener interface;
// it waits for the next call and returns a generic Conn.
func (l *TCPListener) Accept() (c Conn, err error) {
	c1, err := l.AcceptTCP()
	if err != nil {
		return nil, err
	}
	return c1, nil
}

// Close stops listening on the TCP address.
// Already Accepted connections are not closed.
func (l *TCPListener) Close() error {
	if l == nil || l.fd == nil {
		return syscall.EINVAL
	}
	return l.fd.Close()
}

// Addr returns the listener's network address, a *TCPAddr.
func (l *TCPListener) Addr() Addr { return l.fd.laddr }

// SetDeadline sets the deadline associated with the listener.
// A zero time value disables the deadline.
func (l *TCPListener) SetDeadline(t time.Time) error {
	if l == nil || l.fd == nil {
		return syscall.EINVAL
	}
	return setDeadline(l.fd, t)
}

// File returns a copy of the underlying os.File, set to blocking mode.
// It is the caller's responsibility to close f when finished.
// Closing c does not affect f, and closing f does not affect c.
func (l *TCPListener) File() (f *os.File, err error) { return l.fd.dup() }
<commit_msg>net: add diagnostic to try to catch selfConnect bug earlier TBR=dsymonds<commit_after>// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build darwin freebsd linux netbsd openbsd windows

// TCP sockets

package net

import (
	"io"
	"os"
	"syscall"
	"time"
)

// BUG(rsc): On OpenBSD, listening on the "tcp" network does not listen for
// both IPv4 and IPv6 connections. This is due to the fact that IPv4 traffic
// will not be routed to an IPv6 socket - two separate sockets are required
// if both AFs are to be supported. See inet6(4) on OpenBSD for details.

func sockaddrToTCP(sa syscall.Sockaddr) Addr {
	switch sa := sa.(type) {
	case *syscall.SockaddrInet4:
		return &TCPAddr{sa.Addr[0:], sa.Port}
	case *syscall.SockaddrInet6:
		return &TCPAddr{sa.Addr[0:], sa.Port}
	}
	return nil
}

func (a *TCPAddr) family() int {
	if a == nil || len(a.IP) <= IPv4len {
		return syscall.AF_INET
	}
	if a.IP.To4() != nil {
		return syscall.AF_INET
	}
	return syscall.AF_INET6
}

func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
	return ipToSockaddr(family, a.IP, a.Port)
}

func (a *TCPAddr) toAddr() sockaddr {
	if a == nil { // nil *TCPAddr
		return nil // nil interface
	}
	return a
}

// TCPConn is an implementation of the Conn interface
// for TCP network connections.
type TCPConn struct {
	fd *netFD
}

func newTCPConn(fd *netFD) *TCPConn {
	c := &TCPConn{fd}
	c.SetNoDelay(true)
	return c
}

func (c *TCPConn) ok() bool { return c != nil && c.fd != nil }

// Implementation of the Conn interface - see Conn for documentation.

// Read implements the Conn Read method.
func (c *TCPConn) Read(b []byte) (n int, err error) {
	if !c.ok() {
		return 0, syscall.EINVAL
	}
	return c.fd.Read(b)
}

// ReadFrom implements the io.ReaderFrom ReadFrom method.
func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
	if n, err, handled := sendFile(c.fd, r); handled {
		return n, err
	}
	return genericReadFrom(c, r)
}

// Write implements the Conn Write method.
func (c *TCPConn) Write(b []byte) (n int, err error) {
	if !c.ok() {
		return 0, syscall.EINVAL
	}
	return c.fd.Write(b)
}

// Close closes the TCP connection.
func (c *TCPConn) Close() error {
	if !c.ok() {
		return syscall.EINVAL
	}
	err := c.fd.Close()
	c.fd = nil
	return err
}

// CloseRead shuts down the reading side of the TCP connection.
// Most callers should just use Close.
func (c *TCPConn) CloseRead() error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return c.fd.CloseRead()
}

// CloseWrite shuts down the writing side of the TCP connection.
// Most callers should just use Close.
func (c *TCPConn) CloseWrite() error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return c.fd.CloseWrite()
}

// LocalAddr returns the local network address, a *TCPAddr.
func (c *TCPConn) LocalAddr() Addr {
	if !c.ok() {
		return nil
	}
	return c.fd.laddr
}

// RemoteAddr returns the remote network address, a *TCPAddr.
func (c *TCPConn) RemoteAddr() Addr {
	if !c.ok() {
		return nil
	}
	return c.fd.raddr
}

// SetDeadline implements the Conn SetDeadline method.
func (c *TCPConn) SetDeadline(t time.Time) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setDeadline(c.fd, t)
}

// SetReadDeadline implements the Conn SetReadDeadline method.
func (c *TCPConn) SetReadDeadline(t time.Time) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setReadDeadline(c.fd, t)
}

// SetWriteDeadline implements the Conn SetWriteDeadline method.
func (c *TCPConn) SetWriteDeadline(t time.Time) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setWriteDeadline(c.fd, t)
}

// SetReadBuffer sets the size of the operating system's
// receive buffer associated with the connection.
func (c *TCPConn) SetReadBuffer(bytes int) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setReadBuffer(c.fd, bytes)
}

// SetWriteBuffer sets the size of the operating system's
// transmit buffer associated with the connection.
func (c *TCPConn) SetWriteBuffer(bytes int) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setWriteBuffer(c.fd, bytes)
}

// SetLinger sets the behavior of Close() on a connection
// which still has data waiting to be sent or to be acknowledged.
//
// If sec < 0 (the default), Close returns immediately and
// the operating system finishes sending the data in the background.
//
// If sec == 0, Close returns immediately and the operating system
// discards any unsent or unacknowledged data.
//
// If sec > 0, Close blocks for at most sec seconds waiting for
// data to be sent and acknowledged.
func (c *TCPConn) SetLinger(sec int) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setLinger(c.fd, sec)
}

// SetKeepAlive sets whether the operating system should send
// keepalive messages on the connection.
func (c *TCPConn) SetKeepAlive(keepalive bool) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setKeepAlive(c.fd, keepalive)
}

// SetNoDelay controls whether the operating system should delay
// packet transmission in hopes of sending fewer packets
// (Nagle's algorithm).  The default is true (no delay), meaning
// that data is sent as soon as possible after a Write.
func (c *TCPConn) SetNoDelay(noDelay bool) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setNoDelay(c.fd, noDelay)
}

// File returns a copy of the underlying os.File, set to blocking mode.
// It is the caller's responsibility to close f when finished.
// Closing c does not affect f, and closing f does not affect c.
func (c *TCPConn) File() (f *os.File, err error) { return c.fd.dup() }

// DialTCP connects to the remote address raddr on the network net,
// which must be "tcp", "tcp4", or "tcp6".  If laddr is not nil, it is used
// as the local address for the connection.
func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
	if raddr == nil {
		return nil, &OpError{"dial", net, nil, errMissingAddress}
	}

	fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)

	checkRaddr := func(s string) {
		if err == nil && fd.raddr == nil {
			panic("nil raddr in DialTCP: " + s)
		}
	}
	checkRaddr("early")

	// TCP has a rarely used mechanism called a 'simultaneous connection' in
	// which Dial("tcp", addr1, addr2) run on the machine at addr1 can
	// connect to a simultaneous Dial("tcp", addr2, addr1) run on the machine
	// at addr2, without either machine executing Listen.  If laddr == nil,
	// it means we want the kernel to pick an appropriate originating local
	// address.  Some Linux kernels cycle blindly through a fixed range of
	// local ports, regardless of destination port.  If a kernel happens to
	// pick local port 50001 as the source for a Dial("tcp", "", "localhost:50001"),
	// then the Dial will succeed, having simultaneously connected to itself.
	// This can only happen when we are letting the kernel pick a port (laddr == nil)
	// and when there is no listener for the destination address.
	// It's hard to argue this is anything other than a kernel bug.  If we
	// see this happen, rather than expose the buggy effect to users, we
	// close the fd and try again.  If it happens twice more, we relent and
	// use the result.  See also:
	//	http://golang.org/issue/2690
	//	http://stackoverflow.com/questions/4949858/
	for i := 0; i < 2 && err == nil && laddr == nil && selfConnect(fd); i++ {
		fd.Close()
		fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
		checkRaddr("after close")
	}

	if err != nil {
		return nil, err
	}
	return newTCPConn(fd), nil
}

func selfConnect(fd *netFD) bool {
	l := fd.laddr.(*TCPAddr)
	r := fd.raddr.(*TCPAddr)
	return l.Port == r.Port && l.IP.Equal(r.IP)
}

// TCPListener is a TCP network listener.
// Clients should typically use variables of type Listener
// instead of assuming TCP.
type TCPListener struct {
	fd *netFD
}

// ListenTCP announces on the TCP address laddr and returns a TCP listener.
// Net must be "tcp", "tcp4", or "tcp6".
// If laddr has a port of 0, it means to listen on some available port.
// The caller can use l.Addr() to retrieve the chosen address.
func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
	fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP)
	if err != nil {
		return nil, err
	}
	err = syscall.Listen(fd.sysfd, listenerBacklog)
	if err != nil {
		closesocket(fd.sysfd)
		return nil, &OpError{"listen", net, laddr, err}
	}
	l := new(TCPListener)
	l.fd = fd
	return l, nil
}

// AcceptTCP accepts the next incoming call and returns the new connection
// and the remote address.
func (l *TCPListener) AcceptTCP() (c *TCPConn, err error) {
	if l == nil || l.fd == nil || l.fd.sysfd < 0 {
		return nil, syscall.EINVAL
	}
	fd, err := l.fd.accept(sockaddrToTCP)
	if err != nil {
		return nil, err
	}
	return newTCPConn(fd), nil
}

// Accept implements the Accept method in the Listener interface;
// it waits for the next call and returns a generic Conn.
func (l *TCPListener) Accept() (c Conn, err error) {
	c1, err := l.AcceptTCP()
	if err != nil {
		return nil, err
	}
	return c1, nil
}

// Close stops listening on the TCP address.
// Already Accepted connections are not closed.
func (l *TCPListener) Close() error {
	if l == nil || l.fd == nil {
		return syscall.EINVAL
	}
	return l.fd.Close()
}

// Addr returns the listener's network address, a *TCPAddr.
func (l *TCPListener) Addr() Addr { return l.fd.laddr }

// SetDeadline sets the deadline associated with the listener.
// A zero time value disables the deadline.
func (l *TCPListener) SetDeadline(t time.Time) error {
	if l == nil || l.fd == nil {
		return syscall.EINVAL
	}
	return setDeadline(l.fd, t)
}

// File returns a copy of the underlying os.File, set to blocking mode.
// It is the caller's responsibility to close f when finished.
// Closing c does not affect f, and closing f does not affect c.
func (l *TCPListener) File() (f *os.File, err error) { return l.fd.dup() }
<|endoftext|>


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

package testing

import (
	"flag"
	"fmt"
	"os"
	"runtime"
	"time"
)

var matchBenchmarks = flag.String("test.bench", "", "regular expression to select benchmarks to run")
var benchTime = flag.Float64("test.benchtime", 1, "approximate run time for each benchmark, in seconds")

// An internal type but exported because it is cross-package; part of the implementation
// of gotest.
type InternalBenchmark struct {
	Name string
	F    func(b *B)
}

// B is a type passed to Benchmark functions to manage benchmark
// timing and to specify the number of iterations to run.
type B struct {
	N         int
	benchmark InternalBenchmark
	ns        int64
	bytes     int64
	start     int64
}

// StartTimer starts timing a test.  This function is called automatically
// before a benchmark starts, but it can also used to resume timing after
// a call to StopTimer.
func (b *B) StartTimer() { b.start = time.Nanoseconds() }

// StopTimer stops timing a test.  This can be used to pause the timer
// while performing complex initialization that you don't
// want to measure.
func (b *B) StopTimer() {
	if b.start > 0 {
		b.ns += time.Nanoseconds() - b.start
	}
	b.start = 0
}

// ResetTimer stops the timer and sets the elapsed benchmark time to zero.
func (b *B) ResetTimer() {
	b.start = 0
	b.ns = 0
}

// SetBytes records the number of bytes processed in a single operation.
// If this is called, the benchmark will report ns/op and MB/s.
func (b *B) SetBytes(n int64) { b.bytes = n }

func (b *B) nsPerOp() int64 {
	if b.N <= 0 {
		return 0
	}
	return b.ns / int64(b.N)
}

// runN runs a single benchmark for the specified number of iterations.
func (b *B) runN(n int) {
	// Try to get a comparable environment for each run
	// by clearing garbage from previous runs.
	runtime.GC()
	b.N = n
	b.ResetTimer()
	b.StartTimer()
	b.benchmark.F(b)
	b.StopTimer()
}

func min(x, y int) int {
	if x > y {
		return y
	}
	return x
}

func max(x, y int) int {
	if x < y {
		return y
	}
	return x
}

// roundDown10 rounds a number down to the nearest power of 10.
func roundDown10(n int) int {
	var tens = 0
	// tens = floor(log_10(n))
	for n > 10 {
		n = n / 10
		tens++
	}
	// result = 10^tens
	result := 1
	for i := 0; i < tens; i++ {
		result *= 10
	}
	return result
}

// roundUp rounds x up to a number of the form [1eX, 2eX, 5eX].
func roundUp(n int) int {
	base := roundDown10(n)
	if n < (2 * base) {
		return 2 * base
	}
	if n < (5 * base) {
		return 5 * base
	}
	return 10 * base
}

// run times the benchmark function.  It gradually increases the number
// of benchmark iterations until the benchmark runs for a second in order
// to get a reasonable measurement.  It prints timing information in this form
//		testing.BenchmarkHello	100000		19 ns/op
func (b *B) run() BenchmarkResult {
	// Run the benchmark for a single iteration in case it's expensive.
	n := 1
	b.runN(n)
	// Run the benchmark for at least the specified amount of time.
	time := int64(*benchTime * 1e9)
	for b.ns < time && n < 1e9 {
		last := n
		// Predict iterations/sec.
		if b.nsPerOp() == 0 {
			n = 1e9
		} else {
			n = int(time / b.nsPerOp())
		}
		// Run more iterations than we think we'll need for a second (1.5x).
		// Don't grow too fast in case we had timing errors previously.
		// Be sure to run at least one more than last time.
		n = max(min(n+n/2, 100*last), last+1)
		// Round up to something easy to read.
		n = roundUp(n)
		b.runN(n)
	}
	return BenchmarkResult{b.N, b.ns, b.bytes}
}

// The results of a benchmark run.
type BenchmarkResult struct {
	N     int   // The number of iterations.
	Ns    int64 // The total time taken.
	Bytes int64 // Bytes processed in one iteration.
}

func (r BenchmarkResult) NsPerOp() int64 {
	if r.N <= 0 {
		return 0
	}
	return r.Ns / int64(r.N)
}

func (r BenchmarkResult) mbPerSec() float64 {
	if r.Bytes <= 0 || r.Ns <= 0 || r.N <= 0 {
		return 0
	}
	return float64(r.Bytes) * float64(r.N) / float64(r.Ns) * 1e3
}

func (r BenchmarkResult) String() string {
	mbs := r.mbPerSec()
	mb := ""
	if mbs != 0 {
		mb = fmt.Sprintf("\t%7.2f MB/s", mbs)
	}
	nsop := r.NsPerOp()
	ns := fmt.Sprintf("%10d ns/op", nsop)
	if r.N > 0 && nsop < 100 {
		// The format specifiers here make sure that
		// the ones digits line up for all three possible formats.
		if nsop < 10 {
			ns = fmt.Sprintf("%13.2f ns/op", float64(r.Ns)/float64(r.N))
		} else {
			ns = fmt.Sprintf("%12.1f ns/op", float64(r.Ns)/float64(r.N))
		}
	}
	return fmt.Sprintf("%8d\t%s%s", r.N, ns, mb)
}

// An internal function but exported because it is cross-package; part of the implementation
// of gotest.
func RunBenchmarks(matchString func(pat, str string) (bool, os.Error), benchmarks []InternalBenchmark) {
	// If no flag was specified, don't run benchmarks.
	if len(*matchBenchmarks) == 0 {
		return
	}
	for _, Benchmark := range benchmarks {
		matched, err := matchString(*matchBenchmarks, Benchmark.Name)
		if err != nil {
			println("invalid regexp for -test.bench:", err.String())
			os.Exit(1)
		}
		if !matched {
			continue
		}
		for _, procs := range cpuList {
			runtime.GOMAXPROCS(procs)
			b := &B{benchmark: Benchmark}
			r := b.run()
			benchName := Benchmark.Name
			if procs != 1 {
				benchName = fmt.Sprintf("%s-%d", Benchmark.Name, procs)
			}
			print(fmt.Sprintf("%s\t%v\n", benchName, r))
			if p := runtime.GOMAXPROCS(-1); p != procs {
				print(fmt.Sprintf("%s left GOMAXPROCS set to %d\n", benchName, p))
			}
		}
	}
}

// Benchmark benchmarks a single function. Useful for creating
// custom benchmarks that do not use gotest.
func Benchmark(f func(b *B)) BenchmarkResult {
	b := &B{benchmark: InternalBenchmark{"", f}}
	return b.run()
}
<commit_msg>testing: make ResetTimer not start/stop the timer<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package testing

import (
	"flag"
	"fmt"
	"os"
	"runtime"
	"time"
)

var matchBenchmarks = flag.String("test.bench", "", "regular expression to select benchmarks to run")
var benchTime = flag.Float64("test.benchtime", 1, "approximate run time for each benchmark, in seconds")

// An internal type but exported because it is cross-package; part of the implementation
// of gotest.
type InternalBenchmark struct {
	Name string
	F    func(b *B)
}

// B is a type passed to Benchmark functions to manage benchmark
// timing and to specify the number of iterations to run.
type B struct {
	N         int
	benchmark InternalBenchmark
	ns        int64
	bytes     int64
	start     int64
}

// StartTimer starts timing a test.  This function is called automatically
// before a benchmark starts, but it can also used to resume timing after
// a call to StopTimer.
func (b *B) StartTimer() {
	if b.start == 0 {
		b.start = time.Nanoseconds()
	}
}

// StopTimer stops timing a test.  This can be used to pause the timer
// while performing complex initialization that you don't
// want to measure.
func (b *B) StopTimer() {
	if b.start > 0 {
		b.ns += time.Nanoseconds() - b.start
	}
	b.start = 0
}

// ResetTimer sets the elapsed benchmark time to zero.
// It does not affect whether the timer is running.
func (b *B) ResetTimer() {
	if b.start > 0 {
		b.start = time.Nanoseconds()
	}
	b.ns = 0
}

// SetBytes records the number of bytes processed in a single operation.
// If this is called, the benchmark will report ns/op and MB/s.
func (b *B) SetBytes(n int64) { b.bytes = n }

func (b *B) nsPerOp() int64 {
	if b.N <= 0 {
		return 0
	}
	return b.ns / int64(b.N)
}

// runN runs a single benchmark for the specified number of iterations.
func (b *B) runN(n int) {
	// Try to get a comparable environment for each run
	// by clearing garbage from previous runs.
	runtime.GC()
	b.N = n
	b.ResetTimer()
	b.StartTimer()
	b.benchmark.F(b)
	b.StopTimer()
}

func min(x, y int) int {
	if x > y {
		return y
	}
	return x
}

func max(x, y int) int {
	if x < y {
		return y
	}
	return x
}

// roundDown10 rounds a number down to the nearest power of 10.
func roundDown10(n int) int {
	var tens = 0
	// tens = floor(log_10(n))
	for n > 10 {
		n = n / 10
		tens++
	}
	// result = 10^tens
	result := 1
	for i := 0; i < tens; i++ {
		result *= 10
	}
	return result
}

// roundUp rounds x up to a number of the form [1eX, 2eX, 5eX].
func roundUp(n int) int {
	base := roundDown10(n)
	if n < (2 * base) {
		return 2 * base
	}
	if n < (5 * base) {
		return 5 * base
	}
	return 10 * base
}

// run times the benchmark function.  It gradually increases the number
// of benchmark iterations until the benchmark runs for a second in order
// to get a reasonable measurement.  It prints timing information in this form
//		testing.BenchmarkHello	100000		19 ns/op
func (b *B) run() BenchmarkResult {
	// Run the benchmark for a single iteration in case it's expensive.
	n := 1
	b.runN(n)
	// Run the benchmark for at least the specified amount of time.
	time := int64(*benchTime * 1e9)
	for b.ns < time && n < 1e9 {
		last := n
		// Predict iterations/sec.
		if b.nsPerOp() == 0 {
			n = 1e9
		} else {
			n = int(time / b.nsPerOp())
		}
		// Run more iterations than we think we'll need for a second (1.5x).
		// Don't grow too fast in case we had timing errors previously.
		// Be sure to run at least one more than last time.
		n = max(min(n+n/2, 100*last), last+1)
		// Round up to something easy to read.
		n = roundUp(n)
		b.runN(n)
	}
	return BenchmarkResult{b.N, b.ns, b.bytes}
}

// The results of a benchmark run.
type BenchmarkResult struct {
	N     int   // The number of iterations.
	Ns    int64 // The total time taken.
	Bytes int64 // Bytes processed in one iteration.
}

func (r BenchmarkResult) NsPerOp() int64 {
	if r.N <= 0 {
		return 0
	}
	return r.Ns / int64(r.N)
}

func (r BenchmarkResult) mbPerSec() float64 {
	if r.Bytes <= 0 || r.Ns <= 0 || r.N <= 0 {
		return 0
	}
	return float64(r.Bytes) * float64(r.N) / float64(r.Ns) * 1e3
}

func (r BenchmarkResult) String() string {
	mbs := r.mbPerSec()
	mb := ""
	if mbs != 0 {
		mb = fmt.Sprintf("\t%7.2f MB/s", mbs)
	}
	nsop := r.NsPerOp()
	ns := fmt.Sprintf("%10d ns/op", nsop)
	if r.N > 0 && nsop < 100 {
		// The format specifiers here make sure that
		// the ones digits line up for all three possible formats.
		if nsop < 10 {
			ns = fmt.Sprintf("%13.2f ns/op", float64(r.Ns)/float64(r.N))
		} else {
			ns = fmt.Sprintf("%12.1f ns/op", float64(r.Ns)/float64(r.N))
		}
	}
	return fmt.Sprintf("%8d\t%s%s", r.N, ns, mb)
}

// An internal function but exported because it is cross-package; part of the implementation
// of gotest.
func RunBenchmarks(matchString func(pat, str string) (bool, os.Error), benchmarks []InternalBenchmark) {
	// If no flag was specified, don't run benchmarks.
	if len(*matchBenchmarks) == 0 {
		return
	}
	for _, Benchmark := range benchmarks {
		matched, err := matchString(*matchBenchmarks, Benchmark.Name)
		if err != nil {
			println("invalid regexp for -test.bench:", err.String())
			os.Exit(1)
		}
		if !matched {
			continue
		}
		for _, procs := range cpuList {
			runtime.GOMAXPROCS(procs)
			b := &B{benchmark: Benchmark}
			r := b.run()
			benchName := Benchmark.Name
			if procs != 1 {
				benchName = fmt.Sprintf("%s-%d", Benchmark.Name, procs)
			}
			print(fmt.Sprintf("%s\t%v\n", benchName, r))
			if p := runtime.GOMAXPROCS(-1); p != procs {
				print(fmt.Sprintf("%s left GOMAXPROCS set to %d\n", benchName, p))
			}
		}
	}
}

// Benchmark benchmarks a single function. Useful for creating
// custom benchmarks that do not use gotest.
func Benchmark(f func(b *B)) BenchmarkResult {
	b := &B{benchmark: InternalBenchmark{"", f}}
	return b.run()
}
<|endoftext|>


<commit_before>package workload

import (
	"fmt"
	"io"
	"math/rand"

	"github.com/pachyderm/pachyderm/src/pfs"
	"github.com/pachyderm/pachyderm/src/pfs/pfsutil"
	"github.com/pachyderm/pachyderm/src/pps"
	"github.com/pachyderm/pachyderm/src/pps/ppsutil"
)

func RunWorkload(
	pfsClient pfs.APIClient,
	ppsClient pps.APIClient,
	rand *rand.Rand,
	size int,
) error {
	worker := newWorker(rand)
	for i := 0; i < size; i++ {
		if err := worker.work(pfsClient, ppsClient); err != nil {
			return err
		}
	}
	return nil
}

type worker struct {
	repos     []*pfs.Repo
	finished  []*pfs.Commit
	started   []*pfs.Commit
	files     []*pfs.File
	jobs      []*pps.Job
	pipelines []*pps.Pipeline
	rand      *rand.Rand
}

func newWorker(rand *rand.Rand) *worker {
	return &worker{
		rand: rand,
	}
}

const (
	repo     float64 = .1
	commit           = .3
	file             = .6
	job              = .9
	pipeline         = 1.0
)

const maxStartedCommits = 6

func (w *worker) work(pfsClient pfs.APIClient, ppsClient pps.APIClient) error {
	opt := w.rand.Float64()
	switch {
	case opt < repo:
		repoName := w.name()
		if err := pfsutil.CreateRepo(pfsClient, repoName); err != nil {
			return err
		}
		w.repos = append(w.repos, &pfs.Repo{Name: repoName})
		commit, err := pfsutil.StartCommit(pfsClient, repoName, "")
		if err != nil {
			return err
		}
		w.started = append(w.started, commit)
	case opt < commit:
		if len(w.started) >= maxStartedCommits {
			i := w.rand.Intn(len(w.started))
			commit := w.started[i]
			if err := pfsutil.FinishCommit(pfsClient, commit.Repo.Name, commit.Id); err != nil {
				return err
			}
			w.started = append(w.started[:i], w.started[i+1:]...)
			w.finished = append(w.finished, commit)
		} else {
			commit := w.finished[w.rand.Intn(len(w.finished))]
			commit, err := pfsutil.StartCommit(pfsClient, commit.Repo.Name, commit.Id)
			if err != nil {
				return err
			}
			w.started = append(w.started, commit)
		}
	case opt < file:
		commit := w.started[w.rand.Intn(len(w.started))]
		if _, err := pfsutil.PutFile(pfsClient, commit.Repo.Name, commit.Id, w.name(), 0, w.reader()); err != nil {
			return err
		}
	case opt < job:
		inputs := [5]string{}
		var inputCommits []*pfs.Commit
		for i := range inputs {
			randI := w.rand.Intn(len(w.finished))
			inputs[i] = w.finished[randI].Repo.Name
			inputCommits = append(inputCommits, w.finished[randI])
		}
		var parentJobID string
		if len(w.jobs) > 0 {
			parentJobID = w.jobs[w.rand.Intn(len(w.jobs))].Id
		}
		outFilename := w.name()
		job, err := ppsutil.CreateJob(
			ppsClient,
			"",
			[]string{"sh"},
			w.grepCmd(inputs, outFilename),
			1,
			inputCommits,
			parentJobID,
		)
		if err != nil {
			return err
		}
		w.jobs = append(w.jobs, job)
	case opt < pipeline:
		inputs := [5]string{}
		var inputRepos []*pfs.Repo
		for i := range inputs {
			randI := w.rand.Intn(len(w.repos))
			inputs[i] = w.repos[randI].Name
			inputRepos = append(inputRepos, w.repos[randI])
		}
		pipelineName := w.name()
		outFilename := w.name()
		if err := ppsutil.CreatePipeline(
			ppsClient,
			pipelineName,
			"",
			[]string{"sh"},
			w.grepCmd(inputs, outFilename),
			1,
			inputRepos,
		); err != nil {
			return err
		}
		w.pipelines = append(w.pipelines, ppsutil.NewPipeline(pipelineName))
	}
	return nil
}

const letters = "abcdefghijklmnopqrstuvwxyz"
const lettersAndSpaces = "abcdefghijklmnopqrstuvwxyz      \n\n"

func (w *worker) name() string {
	b := make([]byte, 20)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}

type reader struct {
	rand *rand.Rand
}

func (r *reader) Read(p []byte) (int, error) {
	for i := range p {
		p[i] = lettersAndSpaces[r.rand.Intn(len(lettersAndSpaces))]
	}
	if rand.Intn(10) == 0 {
		return len(p), io.EOF
	}
	return len(p), nil
}

func (w *worker) reader() io.Reader {
	return &reader{w.rand}
}

func (w *worker) grepCmd(inputs [5]string, outFilename string) string {
	pattern := make([]byte, 5)
	_, _ = w.reader().Read(pattern)
	return fmt.Sprintf(
		"grep %s /pfs/{%s,%s,%s,%s,%s}/* >/pfs/out/%s",
		pattern,
		inputs[0],
		inputs[1],
		inputs[2],
		inputs[3],
		inputs[4],
		outFilename,
	)
}
<commit_msg>Fixes panics. Adds newlines, they seem defective.<commit_after>package workload

import (
	"fmt"
	"io"
	"math/rand"

	"github.com/pachyderm/pachyderm/src/pfs"
	"github.com/pachyderm/pachyderm/src/pfs/pfsutil"
	"github.com/pachyderm/pachyderm/src/pps"
	"github.com/pachyderm/pachyderm/src/pps/ppsutil"
)

func RunWorkload(
	pfsClient pfs.APIClient,
	ppsClient pps.APIClient,
	rand *rand.Rand,
	size int,
) error {
	worker := newWorker(rand)
	for i := 0; i < size; i++ {
		if err := worker.work(pfsClient, ppsClient); err != nil {
			return err
		}
	}
	return nil
}

type worker struct {
	repos     []*pfs.Repo
	finished  []*pfs.Commit
	started   []*pfs.Commit
	files     []*pfs.File
	jobs      []*pps.Job
	pipelines []*pps.Pipeline
	rand      *rand.Rand
}

func newWorker(rand *rand.Rand) *worker {
	return &worker{
		rand: rand,
	}
}

const (
	repo     float64 = .1
	commit           = .3
	file             = .6
	job              = .9
	pipeline         = 1.0
)

const maxStartedCommits = 6

func (w *worker) work(pfsClient pfs.APIClient, ppsClient pps.APIClient) error {
	opt := w.rand.Float64()
	switch {
	case opt < repo:
		repoName := w.name()
		if err := pfsutil.CreateRepo(pfsClient, repoName); err != nil {
			return err
		}
		w.repos = append(w.repos, &pfs.Repo{Name: repoName})
		commit, err := pfsutil.StartCommit(pfsClient, repoName, "")
		if err != nil {
			return err
		}
		w.started = append(w.started, commit)
	case opt < commit:
		if len(w.started) >= maxStartedCommits || len(w.finished) == 0 {
			if len(w.started) == 0 {
				return nil
			}
			i := w.rand.Intn(len(w.started))
			commit := w.started[i]
			if err := pfsutil.FinishCommit(pfsClient, commit.Repo.Name, commit.Id); err != nil {
				return err
			}
			w.started = append(w.started[:i], w.started[i+1:]...)
			w.finished = append(w.finished, commit)
		} else {
			if len(w.finished) == 0 {
				return nil
			}
			commit := w.finished[w.rand.Intn(len(w.finished))]
			commit, err := pfsutil.StartCommit(pfsClient, commit.Repo.Name, commit.Id)
			if err != nil {
				return err
			}
			w.started = append(w.started, commit)
		}
	case opt < file:
		if len(w.started) == 0 {
			return nil
		}
		commit := w.started[w.rand.Intn(len(w.started))]
		if _, err := pfsutil.PutFile(pfsClient, commit.Repo.Name, commit.Id, w.name(), 0, w.reader()); err != nil {
			return err
		}
	case opt < job:
		if len(w.finished) == 0 {
			return nil
		}
		inputs := [5]string{}
		var inputCommits []*pfs.Commit
		for i := range inputs {
			randI := w.rand.Intn(len(w.finished))
			inputs[i] = w.finished[randI].Repo.Name
			inputCommits = append(inputCommits, w.finished[randI])
		}
		var parentJobID string
		if len(w.jobs) > 0 {
			parentJobID = w.jobs[w.rand.Intn(len(w.jobs))].Id
		}
		outFilename := w.name()
		job, err := ppsutil.CreateJob(
			ppsClient,
			"",
			[]string{"sh"},
			w.grepCmd(inputs, outFilename),
			1,
			inputCommits,
			parentJobID,
		)
		if err != nil {
			return err
		}
		w.jobs = append(w.jobs, job)
	case opt < pipeline:
		if len(w.repos) == 0 {
			return nil
		}
		inputs := [5]string{}
		var inputRepos []*pfs.Repo
		for i := range inputs {
			randI := w.rand.Intn(len(w.repos))
			inputs[i] = w.repos[randI].Name
			inputRepos = append(inputRepos, w.repos[randI])
		}
		pipelineName := w.name()
		outFilename := w.name()
		if err := ppsutil.CreatePipeline(
			ppsClient,
			pipelineName,
			"",
			[]string{"sh"},
			w.grepCmd(inputs, outFilename),
			1,
			inputRepos,
		); err != nil {
			return err
		}
		w.pipelines = append(w.pipelines, ppsutil.NewPipeline(pipelineName))
	}
	return nil
}

const letters = "abcdefghijklmnopqrstuvwxyz"
const lettersAndSpaces = "abcdefghijklmnopqrstuvwxyz      "

func (w *worker) name() string {
	b := make([]byte, 20)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}

type reader struct {
	rand *rand.Rand
}

func (r *reader) Read(p []byte) (int, error) {
	for i := range p {
		if i%128 == 0 {
			p[i] = '\n'
		} else {
			p[i] = lettersAndSpaces[r.rand.Intn(len(lettersAndSpaces))]
		}
	}
	if rand.Intn(10) == 0 {
		return len(p), io.EOF
	}
	return len(p), nil
}

func (w *worker) reader() io.Reader {
	return &reader{w.rand}
}

func (w *worker) grepCmd(inputs [5]string, outFilename string) string {
	pattern := make([]byte, 5)
	_, _ = w.reader().Read(pattern)
	return fmt.Sprintf(
		"grep %s /pfs/{%s,%s,%s,%s,%s}/* >/pfs/out/%s",
		pattern,
		inputs[0],
		inputs[1],
		inputs[2],
		inputs[3],
		inputs[4],
		outFilename,
	)
}
<|endoftext|>


<commit_before>/* Copyright (c) 2014-2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"encoding/json"
	"flag"
	"html/template"
	"log"
	"net/http"
	"path/filepath"
	"strconv"
	"sync"

	"github.com/gorilla/mux"
)

var (
	listen = flag.String("l", ":8080", "Host and port to listen to")
)

type handler struct {
	tmpl   *template.Template
	places []Place
	users  []User
	activs []Activity
	m      sync.RWMutex
}

func newHttpHandler() *handler {
	h := &handler{
		users:  getUsers(),
		activs: getActivities(),
	}
	h.addPlaces(getClubs())
	h.addPlaces(getSchools())
	return h
}

func (h *handler) addPlaces(places []Place) {
	for _, p := range places {
		p.Id = len(h.places) + 1
		h.places = append(h.places, p)
	}
}

func (h *handler) index(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "README.md")
}

func filterPlaces(places []Place, search string) []Place {
	if search == "" {
		return places
	}
	found := []Place{}
	for _, p := range places {
		if !p.Matches(search) {
			continue
		}
		found = append(found, p)
	}
	return found
}

func (h *handler) searchplaces(w http.ResponseWriter, r *http.Request) {
	search := mux.Vars(r)["search"]
	places := filterPlaces(h.places, search)
	doMarshal(w, r, &places)
}

func filterActivs(activs []Activity, search string) []Activity {
	if search == "" {
		return activs
	}
	found := []Activity{}
	for _, p := range activs {
		if !p.Matches(search) {
			continue
		}
		found = append(found, p)
	}
	return found
}

func (h *handler) searchactivs(w http.ResponseWriter, r *http.Request) {
	h.m.RLock()
	defer h.m.RUnlock()
	search := mux.Vars(r)["search"]
	activs := filterActivs(h.activs, search)
	doMarshal(w, r, &activs)
}

func (h *handler) getplace(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]
	n, err := strconv.Atoi(id)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if n < 1 || n > len(h.places) {
		http.Error(w, "not found", http.StatusNotFound)
		return
	}
	doMarshal(w, r, &h.places[n-1])
}

func (h *handler) getuser(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]
	n, err := strconv.Atoi(id)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if n < 1 || n > len(h.users) {
		http.Error(w, "not found", http.StatusNotFound)
		return
	}
	doMarshal(w, r, &h.users[n-1])
}

func (h *handler) postactivity(w http.ResponseWriter, r *http.Request) {
	h.m.Lock()
	defer h.m.Unlock()
	decoder := json.NewDecoder(r.Body)
	var a Activity
	if err := decoder.Decode(&a); err != nil {
		log.Print(err)
	}
	a.Id = len(h.activs) + 1
	a.Parts = []int{}
	if a.Cap < 1 {
		a.Cap = 1
	}
	if a.Cap > 50 {
		a.Cap = 50
	}
	if a.Owner < 1 {
		a.Owner = 1
	}
	if a.Owner > len(h.users) {
		a.Cap = len(h.users)
	}
	h.activs = append(h.activs, a)
}

func (h *handler) getactivity(w http.ResponseWriter, r *http.Request) {
	h.m.RLock()
	defer h.m.RUnlock()
	id := mux.Vars(r)["id"]
	n, err := strconv.Atoi(id)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if n < 1 || n > len(h.activs) {
		http.Error(w, "not found", http.StatusNotFound)
		return
	}
	doMarshal(w, r, &h.activs[n-1])
}

func (h *handler) getusers(w http.ResponseWriter, r *http.Request) {
	doMarshal(w, r, &h.users)
}

func (h *handler) getidimg(kind, name string) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		id := mux.Vars(r)["id"]
		doFile(w, r, filepath.Join(kind, "img", name, id+".jpg"))
	}
}

func addHeaders(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
}

func doFile(w http.ResponseWriter, r *http.Request, path string) {
	addHeaders(w, r)
	http.ServeFile(w, r, path)
}

func doMarshal(w http.ResponseWriter, r *http.Request, v interface{}) {
	addHeaders(w, r)
	//json.NewEncoder(w).Encode(v)
	b, err := json.MarshalIndent(v, "", "\t")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Write(b)
}

func main() {
	flag.Parse()
	h := newHttpHandler()
	r := mux.NewRouter()
	r.HandleFunc("/", h.index).Methods("GET")
	r.HandleFunc("/place/{id}", h.getplace).Methods("GET")
	r.HandleFunc("/places", h.searchplaces).Methods("GET")
	r.HandleFunc("/places/", h.searchplaces).Methods("GET")
	r.HandleFunc("/places/{search}", h.searchplaces).Methods("GET")
	r.HandleFunc("/user/{id}", h.getuser).Methods("GET")
	r.HandleFunc("/users", h.getusers).Methods("GET")
	r.HandleFunc("/users/", h.getusers).Methods("GET")
	r.HandleFunc("/activity", h.postactivity).Methods("POST")
	r.HandleFunc("/activity/{id}", h.getactivity).Methods("GET")
	r.HandleFunc("/activities", h.searchactivs).Methods("GET")
	r.HandleFunc("/activities/", h.searchactivs).Methods("GET")
	r.HandleFunc("/activities/{search}", h.searchactivs).Methods("GET")
	r.HandleFunc("/img/place/{id}", h.getidimg("const", "place")).Methods("GET")
	r.HandleFunc("/img/user/{id}", h.getidimg("const", "user")).Methods("GET")
	r.HandleFunc("/img/activity/{id}", h.getidimg("var", "activity")).Methods("GET")
	log.Printf("listen = %s", *listen)
	http.Handle("/", r)
	log.Println("Up and running!")
	log.Fatal(http.ListenAndServe(*listen, nil))
}
<commit_msg>Fix a few crashers, report post issues<commit_after>/* Copyright (c) 2014-2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"encoding/json"
	"errors"
	"flag"
	"html/template"
	"log"
	"net/http"
	"path/filepath"
	"strconv"
	"sync"

	"github.com/gorilla/mux"
)

var (
	listen = flag.String("l", ":8080", "Host and port to listen to")
)

type handler struct {
	tmpl   *template.Template
	places []Place
	users  []User
	activs []Activity
	m      sync.RWMutex
}

func newHttpHandler() *handler {
	h := &handler{
		users:  getUsers(),
		activs: getActivities(),
	}
	h.addPlaces(getClubs())
	h.addPlaces(getSchools())
	return h
}

func (h *handler) addPlaces(places []Place) {
	for _, p := range places {
		p.Id = len(h.places) + 1
		h.places = append(h.places, p)
	}
}

func (h *handler) index(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "README.md")
}

func filterPlaces(places []Place, search string) []Place {
	if search == "" {
		return places
	}
	found := []Place{}
	for _, p := range places {
		if !p.Matches(search) {
			continue
		}
		found = append(found, p)
	}
	return found
}

func (h *handler) searchplaces(w http.ResponseWriter, r *http.Request) {
	search := mux.Vars(r)["search"]
	places := filterPlaces(h.places, search)
	doMarshal(w, r, &places)
}

func filterActivs(activs []Activity, search string) []Activity {
	if search == "" {
		return activs
	}
	found := []Activity{}
	for _, p := range activs {
		if !p.Matches(search) {
			continue
		}
		found = append(found, p)
	}
	return found
}

func (h *handler) searchactivs(w http.ResponseWriter, r *http.Request) {
	h.m.RLock()
	defer h.m.RUnlock()
	search := mux.Vars(r)["search"]
	activs := filterActivs(h.activs, search)
	doMarshal(w, r, &activs)
}

func (h *handler) getplacebyid(id string) (*Place, error) {
	n, err := strconv.Atoi(id)
	if err != nil {
		return nil, err
	}
	if n < 1 || n > len(h.places) {
		return nil, errors.New("not found")
	}
	return &h.places[n-1], nil
}

func (h *handler) getplace(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]
	p, err := h.getplacebyid(id)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	doMarshal(w, r, p)
}

func (h *handler) getuserbyid(id string) (*User, error) {
	n, err := strconv.Atoi(id)
	if err != nil {
		return nil, err
	}
	if n < 1 || n > len(h.users) {
		return nil, errors.New("not found")
	}
	return &h.users[n-1], nil
}

func (h *handler) getuser(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]
	u, err := h.getuserbyid(id)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	doMarshal(w, r, u)
}

func (h *handler) postactivity(w http.ResponseWriter, r *http.Request) {
	h.m.Lock()
	defer h.m.Unlock()
	decoder := json.NewDecoder(r.Body)
	var a Activity
	if err := decoder.Decode(&a); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	a.Id = len(h.activs) + 1
	a.Parts = []int{}
	if a.Cap < 1 {
		a.Cap = 1
	}
	if a.Cap > 50 {
		a.Cap = 50
	}
	if a.Owner < 1 {
		a.Owner = 1
	}
	if a.Owner > len(h.users) {
		a.Cap = len(h.users)
	}
	h.activs = append(h.activs, a)
}

func (h *handler) getactivitybyid(id string) (*Activity, error) {
	n, err := strconv.Atoi(id)
	if err != nil {
		return nil, err
	}
	if n < 1 || n > len(h.activs) {
		return nil, errors.New("not found")
	}
	return &h.activs[n-1], nil
}

func (h *handler) getactivity(w http.ResponseWriter, r *http.Request) {
	h.m.RLock()
	defer h.m.RUnlock()
	id := mux.Vars(r)["id"]
	a, err := h.getactivitybyid(id)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	doMarshal(w, r, a)
}

func (h *handler) joinactivity(w http.ResponseWriter, r *http.Request) {
}

func (h *handler) leaveactivity(w http.ResponseWriter, r *http.Request) {
}

func (h *handler) getusers(w http.ResponseWriter, r *http.Request) {
	doMarshal(w, r, &h.users)
}

func (h *handler) getidimg(kind, name string) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		id := mux.Vars(r)["id"]
		doFile(w, r, filepath.Join(kind, "img", name, id+".jpg"))
	}
}

func addHeaders(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
}

func doFile(w http.ResponseWriter, r *http.Request, path string) {
	addHeaders(w, r)
	http.ServeFile(w, r, path)
}

func doMarshal(w http.ResponseWriter, r *http.Request, v interface{}) {
	addHeaders(w, r)
	//json.NewEncoder(w).Encode(v)
	b, err := json.MarshalIndent(v, "", "\t")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Write(b)
}

func main() {
	flag.Parse()
	h := newHttpHandler()
	r := mux.NewRouter()
	r.HandleFunc("/", h.index).Methods("GET")
	r.HandleFunc("/place/{id}", h.getplace).Methods("GET")
	r.HandleFunc("/places", h.searchplaces).Methods("GET")
	r.HandleFunc("/places/", h.searchplaces).Methods("GET")
	r.HandleFunc("/places/{search}", h.searchplaces).Methods("GET")
	r.HandleFunc("/user/{id}", h.getuser).Methods("GET")
	r.HandleFunc("/user/{id}/join/{activityid}", h.joinactivity).Methods("GET")
	r.HandleFunc("/user/{id}/leave/{activityid}", h.leaveactivity).Methods("GET")
	r.HandleFunc("/users", h.getusers).Methods("GET")
	r.HandleFunc("/users/", h.getusers).Methods("GET")
	r.HandleFunc("/activity", h.postactivity).Methods("POST")
	r.HandleFunc("/activity/{id}", h.getactivity).Methods("GET")
	r.HandleFunc("/activities", h.searchactivs).Methods("GET")
	r.HandleFunc("/activities/", h.searchactivs).Methods("GET")
	r.HandleFunc("/leave-activity", h.joinactivity).Methods("POST")
	r.HandleFunc("/img/place/{id}", h.getidimg("const", "place")).Methods("GET")
	r.HandleFunc("/img/user/{id}", h.getidimg("const", "user")).Methods("GET")
	r.HandleFunc("/img/activity/{id}", h.getidimg("var", "activity")).Methods("GET")
	log.Printf("listen = %s", *listen)
	http.Handle("/", r)
	log.Println("Up and running!")
	log.Fatal(http.ListenAndServe(*listen, nil))
}
<|endoftext|>