<commit_before>package main

import (
	"fmt"
	"os"
    "./cli"
)

const Name = "gdrive"
const Version = "2.0.0"

const DefaultMaxFiles = 30
const DefaultNameWidth = 40
const DefaultUploadChunkSize = 8 * 1024 * 1024
const DefaultQuery = "trashed = false and 'me' in owners"
const DefaultShareRole = "reader"
const DefaultShareType = "anyone"
var DefaultConfigDir = GetDefaultConfigDir()

func main() {
    globalFlags := []cli.Flag{
        cli.StringFlag{
            Name: "configDir",
            Patterns: []string{"-c", "--config"},
            Description: fmt.Sprintf("Application path, default: %s", DefaultConfigDir),
            DefaultValue: DefaultConfigDir,
        },
    }

    handlers := []*cli.Handler{
        &cli.Handler{
            Pattern: "[global options] list [options]",
            Description: "List files",
            Callback: listHandler,
            Flags: cli.Flags{
                "global options": globalFlags,
                "options": []cli.Flag{
                    cli.IntFlag{
                        Name: "maxFiles",
                        Patterns: []string{"-m", "--max"},
                        Description: fmt.Sprintf("Max files to list, default: %d", DefaultMaxFiles),
                        DefaultValue: DefaultMaxFiles,
                    },
                    cli.StringFlag{
                        Name: "query",
                        Patterns: []string{"-q", "--query"},
                        Description: fmt.Sprintf(`Default query: "%s". See https://developers.google.com/drive/search-parameters`, DefaultQuery),
                        DefaultValue: DefaultQuery,
                    },
                    cli.IntFlag{
                        Name: "nameWidth",
                        Patterns: []string{"--name-width"},
                        Description: fmt.Sprintf("Width of name column, default: %d, minimum: 9, use 0 for full width", DefaultNameWidth),
                        DefaultValue: DefaultNameWidth,
                    },
                    cli.BoolFlag{
                        Name: "skipHeader",
                        Patterns: []string{"--no-header"},
                        Description: "Dont print the header",
                        OmitValue: true,
                    },
                    cli.BoolFlag{
                        Name: "sizeInBytes",
                        Patterns: []string{"--bytes"},
                        Description: "Size in bytes",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global options] list revisions [options] <id>",
            Description: "List file revisions",
            Callback: listRevisionsHandler,
            Flags: cli.Flags{
                "global options": globalFlags,
                "options": []cli.Flag{
                    cli.IntFlag{
                        Name: "nameWidth",
                        Patterns: []string{"--name-width"},
                        Description: fmt.Sprintf("Width of name column, default: %d, minimum: 9, use 0 for full width", DefaultNameWidth),
                        DefaultValue: DefaultNameWidth,
                    },
                    cli.BoolFlag{
                        Name: "skipHeader",
                        Patterns: []string{"--no-header"},
                        Description: "Dont print the header",
                        OmitValue: true,
                    },
                    cli.BoolFlag{
                        Name: "sizeInBytes",
                        Patterns: []string{"--bytes"},
                        Description: "Size in bytes",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global options] download [options] <id>",
            Description: "Download file or directory",
            Callback: downloadHandler,
            Flags: cli.Flags{
                "global options": globalFlags,
                "options": []cli.Flag{
                    cli.BoolFlag{
                        Name: "force",
                        Patterns: []string{"-f", "--force"},
                        Description: "Overwrite existing file",
                        OmitValue: true,
                    },
                    cli.BoolFlag{
                        Name: "noProgress",
                        Patterns: []string{"--no-progress"},
                        Description: "Hide progress",
                        OmitValue: true,
                    },
                    cli.BoolFlag{
                        Name: "stdout",
                        Patterns: []string{"--stdout"},
                        Description: "Write file content to stdout",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global options] download revision [options] <fileId> <revisionId>",
            Description: "Download revision",
            Callback: downloadRevisionHandler,
            Flags: cli.Flags{
                "global options": globalFlags,
                "options": []cli.Flag{
                    cli.BoolFlag{
                        Name: "force",
                        Patterns: []string{"-f", "--force"},
                        Description: "Overwrite existing file",
                        OmitValue: true,
                    },
                    cli.BoolFlag{
                        Name: "noProgress",
                        Patterns: []string{"--no-progress"},
                        Description: "Hide progress",
                        OmitValue: true,
                    },
                    cli.BoolFlag{
                        Name: "stdout",
                        Patterns: []string{"--stdout"},
                        Description: "Write file content to stdout",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global options] upload [options] <path>",
            Description: "Upload file or directory",
            Callback: uploadHandler,
            Flags: cli.Flags{
                "global options": globalFlags,
                "options": []cli.Flag{
                    cli.BoolFlag{
                        Name: "recursive",
                        Patterns: []string{"-r", "--recursive"},
                        Description: "Upload directory recursively",
                        OmitValue: true,
                    },
                    cli.StringSliceFlag{
                        Name: "parent",
                        Patterns: []string{"-p", "--parent"},
                        Description: "Parent id, used to upload file to a specific directory, can be specified multiple times to give many parents",
                    },
                    cli.StringFlag{
                        Name: "name",
                        Patterns: []string{"--name"},
                        Description: "Filename",
                    },
                    cli.BoolFlag{
                        Name: "noProgress",
                        Patterns: []string{"--no-progress"},
                        Description: "Hide progress",
                        OmitValue: true,
                    },
                    cli.StringFlag{
                        Name: "mime",
                        Patterns: []string{"--mime"},
                        Description: "Force mime type",
                    },
                    cli.BoolFlag{
                        Name: "share",
                        Patterns: []string{"--share"},
                        Description: "Share file",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global options] upload stdin [options] <name>",
            Description: "Upload file from stdin",
            Callback: uploadStdinHandler,
            Flags: cli.Flags{
                "global options": globalFlags,
                "options": []cli.Flag{
                    cli.StringSliceFlag{
                        Name: "parent",
                        Patterns: []string{"-p", "--parent"},
                        Description: "Parent id, used to upload file to a specific directory, can be specified multiple times to give many parents",
                    },
                    cli.IntFlag{
                        Name: "chunksize",
                        Patterns: []string{"--chunksize"},
                        Description: fmt.Sprintf("Set chunk size in bytes, default: %d", DefaultUploadChunkSize),
                        DefaultValue: DefaultUploadChunkSize,
                    },
                    cli.StringFlag{
                        Name: "mime",
                        Patterns: []string{"--mime"},
                        Description: "Force mime type",
                    },
                    cli.BoolFlag{
                        Name: "share",
                        Patterns: []string{"--share"},
                        Description: "Share file",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global options] update [options] <id> <path>",
            Description: "Update file, this creates a new revision of the file",
            Callback: updateHandler,
            Flags: cli.Flags{
                "global options": globalFlags,
                "options": []cli.Flag{
                    cli.StringSliceFlag{
                        Name: "parent",
                        Patterns: []string{"-p", "--parent"},
                        Description: "Parent id, used to upload file to a specific directory, can be specified multiple times to give many parents",
                    },
                    cli.StringFlag{
                        Name: "name",
                        Patterns: []string{"--name"},
                        Description: "Filename",
                    },
                    cli.BoolFlag{
                        Name: "noProgress",
                        Patterns: []string{"--no-progress"},
                        Description: "Hide progress",
                        OmitValue: true,
                    },
                    cli.BoolFlag{
                        Name: "stdin",
                        Patterns: []string{"--stdin"},
                        Description: "Use stdin as file content",
                        OmitValue: true,
                    },
                    cli.StringFlag{
                        Name: "mime",
                        Patterns: []string{"--mime"},
                        Description: "Force mime type",
                    },
                    cli.BoolFlag{
                        Name: "share",
                        Patterns: []string{"--share"},
                        Description: "Share file",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global options] info [options] <id>",
            Description: "Show file info",
            Callback: infoHandler,
            Flags: cli.Flags{
                "global options": globalFlags,
                "options": []cli.Flag{
                    cli.BoolFlag{
                        Name: "sizeInBytes",
                        Patterns: []string{"--bytes"},
                        Description: "Show size in bytes",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global options] export [options] <id>",
            Description: "Export a google document",
            Callback: exportHandler,
            Flags: cli.Flags{
                "global options": globalFlags,
                "options": []cli.Flag{
                    cli.BoolFlag{
                        Name: "force",
                        Patterns: []string{"-f", "--force"},
                        Description: "Overwrite existing file",
                        OmitValue: true,
                    },
                    cli.StringFlag{
                        Name: "mime",
                        Patterns: []string{"--mime"},
                        Description: "Mime type of exported file",
                    },
                    cli.BoolFlag{
                        Name: "printMimes",
                        Patterns: []string{"--print-mimes"},
                        Description: "Print available mime types for given file",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global options] mkdir [options] <name>",
            Description: "Create directory",
            Callback: mkdirHandler,
            Flags: cli.Flags{
                "global options": globalFlags,
                "options": []cli.Flag{
                    cli.StringSliceFlag{
                        Name: "parent",
                        Patterns: []string{"-p", "--parent"},
                        Description: "Parent id of created directory, can be specified multiple times to give many parents",
                    },
                    cli.BoolFlag{
                        Name: "share",
                        Patterns: []string{"--share"},
                        Description: "Share created directory",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global options] share [options] <id>",
            Description: "Share file or directory",
            Callback: shareHandler,
            Flags: cli.Flags{
                "global options": globalFlags,
                "options": []cli.Flag{
                    cli.BoolFlag{
                        Name: "discoverable",
                        Patterns: []string{"--discoverable"},
                        Description: "Make file discoverable by search engines",
                        OmitValue: true,
                    },
                    cli.StringFlag{
                        Name: "role",
                        Patterns: []string{"--role"},
                        Description: fmt.Sprintf("Share role. Default: %s", DefaultShareRole),
                        DefaultValue: DefaultShareRole,
                    },
                    cli.StringFlag{
                        Name: "type",
                        Patterns: []string{"--type"},
                        Description: fmt.Sprintf("Share type. Default: %s", DefaultShareType),
                        DefaultValue: DefaultShareType,
                    },
                    cli.StringFlag{
                        Name: "email",
                        Patterns: []string{"--email"},
                        Description: "The email address of the user or group to share the file with. Requires 'user' or 'group' as type",
                    },
                    cli.BoolFlag{
                        Name: "revoke",
                        Patterns: []string{"--revoke"},
                        Description: "Delete all sharing permissions",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global options] delete <id>",
            Description: "Delete file or directory",
            Callback: deleteHandler,
            Flags: cli.Flags{
                "global options": globalFlags,
            },
        },
        &cli.Handler{
            Pattern: "[global options] delete revision <fileId> <revisionId>",
            Description: "Delete file revision",
            Callback: deleteRevisionHandler,
            Flags: cli.Flags{
                "global options": globalFlags,
            },
        },
        &cli.Handler{
            Pattern: "[global options] about [options]",
            Description: "Google drive metadata, quota usage",
            Callback: aboutHandler,
            Flags: cli.Flags{
                "global options": globalFlags,
                "options": []cli.Flag{
                    cli.BoolFlag{
                        Name: "sizeInBytes",
                        Patterns: []string{"--bytes"},
                        Description: "Show size in bytes",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global options] about import",
            Description: "Show supported import formats",
            Callback: aboutImportHandler,
            Flags: cli.Flags{
                "global options": globalFlags,
            },
        },
        &cli.Handler{
            Pattern: "[global options] about export",
            Description: "Show supported export formats",
            Callback: aboutExportHandler,
            Flags: cli.Flags{
                "global options": globalFlags,
            },
        },
        &cli.Handler{
            Pattern: "version",
            Description: "Print application version",
            Callback: printVersion,
        },
        &cli.Handler{
            Pattern: "help",
            Description: "Print help",
            Callback: printHelp,
        },
        &cli.Handler{
            Pattern: "help <command>",
            Description: "Print command help",
            Callback: printCommandHelp,
        },
        &cli.Handler{
            Pattern: "help <command> <subcommand>",
            Description: "Print subcommand help",
            Callback: printSubCommandHelp,
        },
    }

    cli.SetHandlers(handlers)

    if ok := cli.Handle(os.Args[1:]); !ok {
        ExitF("No valid arguments given, use '%s help' to see available commands", Name)
    }
}
<commit_msg>s/global options/global/<commit_after>package main

import (
	"fmt"
	"os"
    "./cli"
)

const Name = "gdrive"
const Version = "2.0.0"

const DefaultMaxFiles = 30
const DefaultNameWidth = 40
const DefaultUploadChunkSize = 8 * 1024 * 1024
const DefaultQuery = "trashed = false and 'me' in owners"
const DefaultShareRole = "reader"
const DefaultShareType = "anyone"
var DefaultConfigDir = GetDefaultConfigDir()

func main() {
    globalFlags := []cli.Flag{
        cli.StringFlag{
            Name: "configDir",
            Patterns: []string{"-c", "--config"},
            Description: fmt.Sprintf("Application path, default: %s", DefaultConfigDir),
            DefaultValue: DefaultConfigDir,
        },
    }

    handlers := []*cli.Handler{
        &cli.Handler{
            Pattern: "[global] list [options]",
            Description: "List files",
            Callback: listHandler,
            Flags: cli.Flags{
                "global": globalFlags,
                "options": []cli.Flag{
                    cli.IntFlag{
                        Name: "maxFiles",
                        Patterns: []string{"-m", "--max"},
                        Description: fmt.Sprintf("Max files to list, default: %d", DefaultMaxFiles),
                        DefaultValue: DefaultMaxFiles,
                    },
                    cli.StringFlag{
                        Name: "query",
                        Patterns: []string{"-q", "--query"},
                        Description: fmt.Sprintf(`Default query: "%s". See https://developers.google.com/drive/search-parameters`, DefaultQuery),
                        DefaultValue: DefaultQuery,
                    },
                    cli.IntFlag{
                        Name: "nameWidth",
                        Patterns: []string{"--name-width"},
                        Description: fmt.Sprintf("Width of name column, default: %d, minimum: 9, use 0 for full width", DefaultNameWidth),
                        DefaultValue: DefaultNameWidth,
                    },
                    cli.BoolFlag{
                        Name: "skipHeader",
                        Patterns: []string{"--no-header"},
                        Description: "Dont print the header",
                        OmitValue: true,
                    },
                    cli.BoolFlag{
                        Name: "sizeInBytes",
                        Patterns: []string{"--bytes"},
                        Description: "Size in bytes",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global] list revisions [options] <id>",
            Description: "List file revisions",
            Callback: listRevisionsHandler,
            Flags: cli.Flags{
                "global": globalFlags,
                "options": []cli.Flag{
                    cli.IntFlag{
                        Name: "nameWidth",
                        Patterns: []string{"--name-width"},
                        Description: fmt.Sprintf("Width of name column, default: %d, minimum: 9, use 0 for full width", DefaultNameWidth),
                        DefaultValue: DefaultNameWidth,
                    },
                    cli.BoolFlag{
                        Name: "skipHeader",
                        Patterns: []string{"--no-header"},
                        Description: "Dont print the header",
                        OmitValue: true,
                    },
                    cli.BoolFlag{
                        Name: "sizeInBytes",
                        Patterns: []string{"--bytes"},
                        Description: "Size in bytes",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global] download [options] <id>",
            Description: "Download file or directory",
            Callback: downloadHandler,
            Flags: cli.Flags{
                "global": globalFlags,
                "options": []cli.Flag{
                    cli.BoolFlag{
                        Name: "force",
                        Patterns: []string{"-f", "--force"},
                        Description: "Overwrite existing file",
                        OmitValue: true,
                    },
                    cli.BoolFlag{
                        Name: "noProgress",
                        Patterns: []string{"--no-progress"},
                        Description: "Hide progress",
                        OmitValue: true,
                    },
                    cli.BoolFlag{
                        Name: "stdout",
                        Patterns: []string{"--stdout"},
                        Description: "Write file content to stdout",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global] download revision [options] <fileId> <revisionId>",
            Description: "Download revision",
            Callback: downloadRevisionHandler,
            Flags: cli.Flags{
                "global": globalFlags,
                "options": []cli.Flag{
                    cli.BoolFlag{
                        Name: "force",
                        Patterns: []string{"-f", "--force"},
                        Description: "Overwrite existing file",
                        OmitValue: true,
                    },
                    cli.BoolFlag{
                        Name: "noProgress",
                        Patterns: []string{"--no-progress"},
                        Description: "Hide progress",
                        OmitValue: true,
                    },
                    cli.BoolFlag{
                        Name: "stdout",
                        Patterns: []string{"--stdout"},
                        Description: "Write file content to stdout",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global] upload [options] <path>",
            Description: "Upload file or directory",
            Callback: uploadHandler,
            Flags: cli.Flags{
                "global": globalFlags,
                "options": []cli.Flag{
                    cli.BoolFlag{
                        Name: "recursive",
                        Patterns: []string{"-r", "--recursive"},
                        Description: "Upload directory recursively",
                        OmitValue: true,
                    },
                    cli.StringSliceFlag{
                        Name: "parent",
                        Patterns: []string{"-p", "--parent"},
                        Description: "Parent id, used to upload file to a specific directory, can be specified multiple times to give many parents",
                    },
                    cli.StringFlag{
                        Name: "name",
                        Patterns: []string{"--name"},
                        Description: "Filename",
                    },
                    cli.BoolFlag{
                        Name: "noProgress",
                        Patterns: []string{"--no-progress"},
                        Description: "Hide progress",
                        OmitValue: true,
                    },
                    cli.StringFlag{
                        Name: "mime",
                        Patterns: []string{"--mime"},
                        Description: "Force mime type",
                    },
                    cli.BoolFlag{
                        Name: "share",
                        Patterns: []string{"--share"},
                        Description: "Share file",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global] upload stdin [options] <name>",
            Description: "Upload file from stdin",
            Callback: uploadStdinHandler,
            Flags: cli.Flags{
                "global": globalFlags,
                "options": []cli.Flag{
                    cli.StringSliceFlag{
                        Name: "parent",
                        Patterns: []string{"-p", "--parent"},
                        Description: "Parent id, used to upload file to a specific directory, can be specified multiple times to give many parents",
                    },
                    cli.IntFlag{
                        Name: "chunksize",
                        Patterns: []string{"--chunksize"},
                        Description: fmt.Sprintf("Set chunk size in bytes, default: %d", DefaultUploadChunkSize),
                        DefaultValue: DefaultUploadChunkSize,
                    },
                    cli.StringFlag{
                        Name: "mime",
                        Patterns: []string{"--mime"},
                        Description: "Force mime type",
                    },
                    cli.BoolFlag{
                        Name: "share",
                        Patterns: []string{"--share"},
                        Description: "Share file",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global] update [options] <id> <path>",
            Description: "Update file, this creates a new revision of the file",
            Callback: updateHandler,
            Flags: cli.Flags{
                "global": globalFlags,
                "options": []cli.Flag{
                    cli.StringSliceFlag{
                        Name: "parent",
                        Patterns: []string{"-p", "--parent"},
                        Description: "Parent id, used to upload file to a specific directory, can be specified multiple times to give many parents",
                    },
                    cli.StringFlag{
                        Name: "name",
                        Patterns: []string{"--name"},
                        Description: "Filename",
                    },
                    cli.BoolFlag{
                        Name: "noProgress",
                        Patterns: []string{"--no-progress"},
                        Description: "Hide progress",
                        OmitValue: true,
                    },
                    cli.BoolFlag{
                        Name: "stdin",
                        Patterns: []string{"--stdin"},
                        Description: "Use stdin as file content",
                        OmitValue: true,
                    },
                    cli.StringFlag{
                        Name: "mime",
                        Patterns: []string{"--mime"},
                        Description: "Force mime type",
                    },
                    cli.BoolFlag{
                        Name: "share",
                        Patterns: []string{"--share"},
                        Description: "Share file",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global] info [options] <id>",
            Description: "Show file info",
            Callback: infoHandler,
            Flags: cli.Flags{
                "global": globalFlags,
                "options": []cli.Flag{
                    cli.BoolFlag{
                        Name: "sizeInBytes",
                        Patterns: []string{"--bytes"},
                        Description: "Show size in bytes",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global] export [options] <id>",
            Description: "Export a google document",
            Callback: exportHandler,
            Flags: cli.Flags{
                "global": globalFlags,
                "options": []cli.Flag{
                    cli.BoolFlag{
                        Name: "force",
                        Patterns: []string{"-f", "--force"},
                        Description: "Overwrite existing file",
                        OmitValue: true,
                    },
                    cli.StringFlag{
                        Name: "mime",
                        Patterns: []string{"--mime"},
                        Description: "Mime type of exported file",
                    },
                    cli.BoolFlag{
                        Name: "printMimes",
                        Patterns: []string{"--print-mimes"},
                        Description: "Print available mime types for given file",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global] mkdir [options] <name>",
            Description: "Create directory",
            Callback: mkdirHandler,
            Flags: cli.Flags{
                "global": globalFlags,
                "options": []cli.Flag{
                    cli.StringSliceFlag{
                        Name: "parent",
                        Patterns: []string{"-p", "--parent"},
                        Description: "Parent id of created directory, can be specified multiple times to give many parents",
                    },
                    cli.BoolFlag{
                        Name: "share",
                        Patterns: []string{"--share"},
                        Description: "Share created directory",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global] share [options] <id>",
            Description: "Share file or directory",
            Callback: shareHandler,
            Flags: cli.Flags{
                "global": globalFlags,
                "options": []cli.Flag{
                    cli.BoolFlag{
                        Name: "discoverable",
                        Patterns: []string{"--discoverable"},
                        Description: "Make file discoverable by search engines",
                        OmitValue: true,
                    },
                    cli.StringFlag{
                        Name: "role",
                        Patterns: []string{"--role"},
                        Description: fmt.Sprintf("Share role. Default: %s", DefaultShareRole),
                        DefaultValue: DefaultShareRole,
                    },
                    cli.StringFlag{
                        Name: "type",
                        Patterns: []string{"--type"},
                        Description: fmt.Sprintf("Share type. Default: %s", DefaultShareType),
                        DefaultValue: DefaultShareType,
                    },
                    cli.StringFlag{
                        Name: "email",
                        Patterns: []string{"--email"},
                        Description: "The email address of the user or group to share the file with. Requires 'user' or 'group' as type",
                    },
                    cli.BoolFlag{
                        Name: "revoke",
                        Patterns: []string{"--revoke"},
                        Description: "Delete all sharing permissions",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global] delete <id>",
            Description: "Delete file or directory",
            Callback: deleteHandler,
            Flags: cli.Flags{
                "global": globalFlags,
            },
        },
        &cli.Handler{
            Pattern: "[global] delete revision <fileId> <revisionId>",
            Description: "Delete file revision",
            Callback: deleteRevisionHandler,
            Flags: cli.Flags{
                "global": globalFlags,
            },
        },
        &cli.Handler{
            Pattern: "[global] about [options]",
            Description: "Google drive metadata, quota usage",
            Callback: aboutHandler,
            Flags: cli.Flags{
                "global": globalFlags,
                "options": []cli.Flag{
                    cli.BoolFlag{
                        Name: "sizeInBytes",
                        Patterns: []string{"--bytes"},
                        Description: "Show size in bytes",
                        OmitValue: true,
                    },
                },
            },
        },
        &cli.Handler{
            Pattern: "[global] about import",
            Description: "Show supported import formats",
            Callback: aboutImportHandler,
            Flags: cli.Flags{
                "global": globalFlags,
            },
        },
        &cli.Handler{
            Pattern: "[global] about export",
            Description: "Show supported export formats",
            Callback: aboutExportHandler,
            Flags: cli.Flags{
                "global": globalFlags,
            },
        },
        &cli.Handler{
            Pattern: "version",
            Description: "Print application version",
            Callback: printVersion,
        },
        &cli.Handler{
            Pattern: "help",
            Description: "Print help",
            Callback: printHelp,
        },
        &cli.Handler{
            Pattern: "help <command>",
            Description: "Print command help",
            Callback: printCommandHelp,
        },
        &cli.Handler{
            Pattern: "help <command> <subcommand>",
            Description: "Print subcommand help",
            Callback: printSubCommandHelp,
        },
    }

    cli.SetHandlers(handlers)

    if ok := cli.Handle(os.Args[1:]); !ok {
        ExitF("No valid arguments given, use '%s help' to see available commands", Name)
    }
}
<|endoftext|>


<commit_before>package template

// Functions available to gondola templates

import (
	"bytes"
	"encoding/json"
	"fmt"
	"gnd.la/app/serialize"
	"gnd.la/html"
	"gnd.la/template/assets"
	"gnd.la/util/types"
	"html/template"
	"math"
	"reflect"
	"strconv"
	"strings"
	"time"
)

func eq(args ...interface{}) bool {
	if len(args) == 0 {
		return false
	}
	x := args[0]
	switch x := x.(type) {
	case string, int, int64, byte, float32, float64:
		for _, y := range args[1:] {
			if x == y {
				return true
			}
		}
		return false
	}

	for _, y := range args[1:] {
		if reflect.DeepEqual(x, y) {
			return true
		}
	}
	return false
}

func neq(args ...interface{}) bool {
	return !eq(args...)
}

func lt(arg1, arg2 interface{}) (bool, error) {
	v1 := reflect.ValueOf(arg1)
	v2 := reflect.ValueOf(arg2)
	t1 := v1.Type()
	t2 := v2.Type()
	switch {
	case types.IsInt(t1) && types.IsInt(t2):
		return v1.Int() < v2.Int(), nil
	case types.IsUint(t1) && types.IsUint(t2):
		return v1.Uint() < v2.Uint(), nil
	case types.IsFloat(t1) && types.IsFloat(t2):
		return v1.Float() < v2.Float(), nil
	}
	return false, fmt.Errorf("can't compare %T with %T", arg1, arg2)
}

func lte(arg1, arg2 interface{}) (bool, error) {
	lessThan, err := lt(arg1, arg2)
	if lessThan || err != nil {
		return lessThan, err
	}
	return eq(arg1, arg2), nil
}

func gt(arg1, arg2 interface{}) (bool, error) {
	v1 := reflect.ValueOf(arg1)
	v2 := reflect.ValueOf(arg2)
	t1 := v1.Type()
	t2 := v2.Type()
	switch {
	case types.IsInt(t1) && types.IsInt(t2):
		return v1.Int() > v2.Int(), nil
	case types.IsUint(t1) && types.IsUint(t2):
		return v1.Uint() > v2.Uint(), nil
	case types.IsFloat(t1) && types.IsFloat(t2):
		return v1.Float() > v2.Float(), nil
	}
	return false, fmt.Errorf("can't compare %T with %T", arg1, arg2)
}

func gte(arg1, arg2 interface{}) (bool, error) {
	greaterThan, err := gt(arg1, arg2)
	if greaterThan || err != nil {
		return greaterThan, err
	}
	return eq(arg1, arg2), nil
}

func jsons(arg interface{}) (string, error) {
	if jw, ok := arg.(serialize.JSONWriter); ok {
		var buf bytes.Buffer
		_, err := jw.WriteJSON(&buf)
		return buf.String(), err
	}
	b, err := json.Marshal(arg)
	return string(b), err
}

func _json(arg interface{}) (template.JS, error) {
	s, err := jsons(arg)
	return template.JS(s), err
}

func nz(x interface{}) bool {
	switch x := x.(type) {
	case int, uint, int64, uint64, byte, float32, float64:
		return x != 0
	case string:
		return len(x) > 0
	}
	return false
}

func lower(x string) string {
	return strings.ToLower(x)
}

func join(x []string, sep string) string {
	return strings.Join(x, sep)
}

func _map(args ...interface{}) (map[string]interface{}, error) {
	var key string
	m := make(map[string]interface{})
	for ii, v := range args {
		if ii%2 == 0 {
			if s, ok := v.(string); ok {
				key = s
			} else if s, ok := v.(*string); ok {
				key = *s
			} else {
				return nil, fmt.Errorf("Invalid argument to map at index %d, %t instead of string", ii, v)
			}
		} else {
			m[key] = v
		}
	}
	return m, nil
}

// this returns *[]interface{} so append works on
// slices declared in templates
func _slice(args ...interface{}) *[]interface{} {
	return &args
}

func _append(items interface{}, args ...interface{}) (string, error) {
	val := reflect.ValueOf(items)
	if !val.IsValid() || val.Kind() != reflect.Ptr || val.Elem().Kind() != reflect.Slice {
		return "", fmt.Errorf("first argument to append must be pointer to slice, it's %T", items)
	}
	sl := val.Elem()
	for _, v := range args {
		vval := reflect.ValueOf(v)
		if !vval.Type().AssignableTo(sl.Type().Elem()) {
			return "", fmt.Errorf("can't append %s to %s", vval.Type(), sl.Type())
		}
		sl = reflect.Append(sl, vval)
	}
	val.Elem().Set(sl)
	return "", nil
}

func mult(args ...interface{}) (interface{}, error) {
	val := 1.0
	for ii, v := range args {
		value := reflect.ValueOf(v)
		switch value.Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			val *= float64(value.Int())
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			val *= float64(value.Uint())
		case reflect.Float32, reflect.Float64:
			val *= value.Float()
		case reflect.String:
			v, err := strconv.ParseFloat(value.String(), 64)
			if err != nil {
				return 0, fmt.Errorf("Error parsing string passed to mult at index %d: %s", ii, err)
			}
			val *= v
		default:
			return 0, fmt.Errorf("Invalid argument of type %T passed to mult at index %d", v, ii)
		}
	}
	if math.Floor(val) == val {
		return int(val), nil
	}
	return val, nil

}

func add(args ...interface{}) (interface{}, error) {
	val := 0.0
	for ii, v := range args {
		value := reflect.ValueOf(v)
		switch value.Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			val += float64(value.Int())
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			val += float64(value.Uint())
		case reflect.Float32, reflect.Float64:
			val += value.Float()
		case reflect.String:
			v, err := strconv.ParseFloat(value.String(), 64)
			if err != nil {
				return 0, fmt.Errorf("error parsing string passed to add() at index %d: %s", ii, err)
			}
			val += v
		default:
			return 0, fmt.Errorf("invalid argument of type %T passed to add() at index %d", v, ii)
		}
	}
	if math.Floor(val) == val {
		return int(val), nil
	}
	return val, nil

}

func concat(args ...interface{}) string {
	s := make([]string, len(args))
	for ii, v := range args {
		s[ii] = types.ToString(v)
	}
	return strings.Join(s, "")
}

func and(args ...interface{}) bool {
	for _, v := range args {
		t, _ := types.IsTrue(v)
		if !t {
			return false
		}
	}
	return true
}

func or(args ...interface{}) interface{} {
	for _, v := range args {
		t, _ := types.IsTrue(v)
		if t {
			return v
		}
	}
	return nil
}

func not(arg interface{}) bool {
	t, _ := types.IsTrue(arg)
	return !t
}

func divisible(n interface{}, d interface{}) (bool, error) {
	ni, err := types.ToInt(n)
	if err != nil {
		return false, fmt.Errorf("divisible() invalid number %v: %s", n, err)
	}
	di, err := types.ToInt(d)
	if err != nil {
		return false, fmt.Errorf("divisible() invalid divisor %v: %s", d, err)
	}
	return ni%di == 0, nil
}

func even(arg interface{}) (bool, error) {
	return divisible(arg, 2)
}

func odd(arg interface{}) (bool, error) {
	res, err := divisible(arg, 2)
	if err != nil {
		return false, err
	}
	return !res, nil
}

func now() time.Time {
	return time.Now()
}

func toHtml(s string) template.HTML {
	return template.HTML(strings.Replace(html.Escape(s), "\n", "
", -1))
}

var templateFuncs template.FuncMap = template.FuncMap{
	"eq":        eq,
	"neq":       neq,
	"lt":        lt,
	"lte":       lte,
	"gt":        gt,
	"gte":       gte,
	"json":      _json,
	"jsons":     jsons,
	"nz":        nz,
	"lower":     lower,
	"join":      join,
	"map":       _map,
	"slice":     _slice,
	"append":    _append,
	"mult":      mult,
	"divisible": divisible,
	"add":       add,
	"even":      even,
	"odd":       odd,
	"render":    assets.Render,
	"concat":    concat,
	"and":       and,
	"or":        or,
	"not":       not,
	"now":       now,
	"to_lower":  strings.ToLower,
	"to_title":  strings.ToTitle,
	"to_upper":  strings.ToUpper,
	"to_html":   toHtml,

	// Go builtins
	"call":     call,
	"html":     template.HTMLEscaper,
	"index":    index,
	"js":       template.JSEscaper,
	"len":      length,
	"print":    fmt.Sprint,
	"printf":   fmt.Sprintf,
	"println":  fmt.Sprintln,
	"urlquery": template.URLQueryEscaper,
}
<commit_msg>Add deref template function<commit_after>package template

// Functions available to gondola templates

import (
	"bytes"
	"encoding/json"
	"fmt"
	"gnd.la/app/serialize"
	"gnd.la/html"
	"gnd.la/template/assets"
	"gnd.la/util/types"
	"html/template"
	"math"
	"reflect"
	"strconv"
	"strings"
	"time"
)

func eq(args ...interface{}) bool {
	if len(args) == 0 {
		return false
	}
	x := args[0]
	switch x := x.(type) {
	case string, int, int64, byte, float32, float64:
		for _, y := range args[1:] {
			if x == y {
				return true
			}
		}
		return false
	}

	for _, y := range args[1:] {
		if reflect.DeepEqual(x, y) {
			return true
		}
	}
	return false
}

func neq(args ...interface{}) bool {
	return !eq(args...)
}

func lt(arg1, arg2 interface{}) (bool, error) {
	v1 := reflect.ValueOf(arg1)
	v2 := reflect.ValueOf(arg2)
	t1 := v1.Type()
	t2 := v2.Type()
	switch {
	case types.IsInt(t1) && types.IsInt(t2):
		return v1.Int() < v2.Int(), nil
	case types.IsUint(t1) && types.IsUint(t2):
		return v1.Uint() < v2.Uint(), nil
	case types.IsFloat(t1) && types.IsFloat(t2):
		return v1.Float() < v2.Float(), nil
	}
	return false, fmt.Errorf("can't compare %T with %T", arg1, arg2)
}

func lte(arg1, arg2 interface{}) (bool, error) {
	lessThan, err := lt(arg1, arg2)
	if lessThan || err != nil {
		return lessThan, err
	}
	return eq(arg1, arg2), nil
}

func gt(arg1, arg2 interface{}) (bool, error) {
	v1 := reflect.ValueOf(arg1)
	v2 := reflect.ValueOf(arg2)
	t1 := v1.Type()
	t2 := v2.Type()
	switch {
	case types.IsInt(t1) && types.IsInt(t2):
		return v1.Int() > v2.Int(), nil
	case types.IsUint(t1) && types.IsUint(t2):
		return v1.Uint() > v2.Uint(), nil
	case types.IsFloat(t1) && types.IsFloat(t2):
		return v1.Float() > v2.Float(), nil
	}
	return false, fmt.Errorf("can't compare %T with %T", arg1, arg2)
}

func gte(arg1, arg2 interface{}) (bool, error) {
	greaterThan, err := gt(arg1, arg2)
	if greaterThan || err != nil {
		return greaterThan, err
	}
	return eq(arg1, arg2), nil
}

func jsons(arg interface{}) (string, error) {
	if jw, ok := arg.(serialize.JSONWriter); ok {
		var buf bytes.Buffer
		_, err := jw.WriteJSON(&buf)
		return buf.String(), err
	}
	b, err := json.Marshal(arg)
	return string(b), err
}

func _json(arg interface{}) (template.JS, error) {
	s, err := jsons(arg)
	return template.JS(s), err
}

func nz(x interface{}) bool {
	switch x := x.(type) {
	case int, uint, int64, uint64, byte, float32, float64:
		return x != 0
	case string:
		return len(x) > 0
	}
	return false
}

func lower(x string) string {
	return strings.ToLower(x)
}

func join(x []string, sep string) string {
	return strings.Join(x, sep)
}

func _map(args ...interface{}) (map[string]interface{}, error) {
	var key string
	m := make(map[string]interface{})
	for ii, v := range args {
		if ii%2 == 0 {
			if s, ok := v.(string); ok {
				key = s
			} else if s, ok := v.(*string); ok {
				key = *s
			} else {
				return nil, fmt.Errorf("Invalid argument to map at index %d, %t instead of string", ii, v)
			}
		} else {
			m[key] = v
		}
	}
	return m, nil
}

// this returns *[]interface{} so append works on
// slices declared in templates
func _slice(args ...interface{}) *[]interface{} {
	return &args
}

func _append(items interface{}, args ...interface{}) (string, error) {
	val := reflect.ValueOf(items)
	if !val.IsValid() || val.Kind() != reflect.Ptr || val.Elem().Kind() != reflect.Slice {
		return "", fmt.Errorf("first argument to append must be pointer to slice, it's %T", items)
	}
	sl := val.Elem()
	for _, v := range args {
		vval := reflect.ValueOf(v)
		if !vval.Type().AssignableTo(sl.Type().Elem()) {
			return "", fmt.Errorf("can't append %s to %s", vval.Type(), sl.Type())
		}
		sl = reflect.Append(sl, vval)
	}
	val.Elem().Set(sl)
	return "", nil
}

func deref(arg interface{}) (interface{}, error) {
	v := reflect.ValueOf(arg)
	if v.Kind() != reflect.Ptr {
		return nil, fmt.Errorf("argument to deref must be pointer, not %T", arg)
	}
	return v.Elem().Interface(), nil
}

func mult(args ...interface{}) (interface{}, error) {
	val := 1.0
	for ii, v := range args {
		value := reflect.ValueOf(v)
		switch value.Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			val *= float64(value.Int())
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			val *= float64(value.Uint())
		case reflect.Float32, reflect.Float64:
			val *= value.Float()
		case reflect.String:
			v, err := strconv.ParseFloat(value.String(), 64)
			if err != nil {
				return 0, fmt.Errorf("Error parsing string passed to mult at index %d: %s", ii, err)
			}
			val *= v
		default:
			return 0, fmt.Errorf("Invalid argument of type %T passed to mult at index %d", v, ii)
		}
	}
	if math.Floor(val) == val {
		return int(val), nil
	}
	return val, nil

}

func add(args ...interface{}) (interface{}, error) {
	val := 0.0
	for ii, v := range args {
		value := reflect.ValueOf(v)
		switch value.Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			val += float64(value.Int())
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			val += float64(value.Uint())
		case reflect.Float32, reflect.Float64:
			val += value.Float()
		case reflect.String:
			v, err := strconv.ParseFloat(value.String(), 64)
			if err != nil {
				return 0, fmt.Errorf("error parsing string passed to add() at index %d: %s", ii, err)
			}
			val += v
		default:
			return 0, fmt.Errorf("invalid argument of type %T passed to add() at index %d", v, ii)
		}
	}
	if math.Floor(val) == val {
		return int(val), nil
	}
	return val, nil

}

func concat(args ...interface{}) string {
	s := make([]string, len(args))
	for ii, v := range args {
		s[ii] = types.ToString(v)
	}
	return strings.Join(s, "")
}

func and(args ...interface{}) bool {
	for _, v := range args {
		t, _ := types.IsTrue(v)
		if !t {
			return false
		}
	}
	return true
}

func or(args ...interface{}) interface{} {
	for _, v := range args {
		t, _ := types.IsTrue(v)
		if t {
			return v
		}
	}
	return nil
}

func not(arg interface{}) bool {
	t, _ := types.IsTrue(arg)
	return !t
}

func divisible(n interface{}, d interface{}) (bool, error) {
	ni, err := types.ToInt(n)
	if err != nil {
		return false, fmt.Errorf("divisible() invalid number %v: %s", n, err)
	}
	di, err := types.ToInt(d)
	if err != nil {
		return false, fmt.Errorf("divisible() invalid divisor %v: %s", d, err)
	}
	return ni%di == 0, nil
}

func even(arg interface{}) (bool, error) {
	return divisible(arg, 2)
}

func odd(arg interface{}) (bool, error) {
	res, err := divisible(arg, 2)
	if err != nil {
		return false, err
	}
	return !res, nil
}

func now() time.Time {
	return time.Now()
}

func toHtml(s string) template.HTML {
	return template.HTML(strings.Replace(html.Escape(s), "\n", "
", -1))
}

var templateFuncs template.FuncMap = template.FuncMap{
	"eq":        eq,
	"neq":       neq,
	"lt":        lt,
	"lte":       lte,
	"gt":        gt,
	"gte":       gte,
	"json":      _json,
	"jsons":     jsons,
	"nz":        nz,
	"lower":     lower,
	"join":      join,
	"map":       _map,
	"slice":     _slice,
	"append":    _append,
	"deref":     deref,
	"mult":      mult,
	"divisible": divisible,
	"add":       add,
	"even":      even,
	"odd":       odd,
	"render":    assets.Render,
	"concat":    concat,
	"and":       and,
	"or":        or,
	"not":       not,
	"now":       now,
	"to_lower":  strings.ToLower,
	"to_title":  strings.ToTitle,
	"to_upper":  strings.ToUpper,
	"to_html":   toHtml,

	// Go builtins
	"call":     call,
	"html":     template.HTMLEscaper,
	"index":    index,
	"js":       template.JSEscaper,
	"len":      length,
	"print":    fmt.Sprint,
	"printf":   fmt.Sprintf,
	"println":  fmt.Sprintln,
	"urlquery": template.URLQueryEscaper,
}
<|endoftext|>


<commit_before>package main

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

	"os"

	"github.com/graphql-go/handler"
	"github.com/mleonard87/merknera/gameworker"
	"github.com/mleonard87/merknera/graphql"
	"github.com/mleonard87/merknera/repository"
	"github.com/mleonard87/merknera/schema"
	"github.com/mleonard87/merknera/security"
	"github.com/mleonard87/merknera/services"
	"github.com/mleonard87/rpc"
	"github.com/mleonard87/rpc/json"
)

func registerRPCHandler() {
	s := rpc.NewServer()
	s.RegisterCodec(json.NewCodec(), "application/json")
	s.RegisterService(new(services.RegistrationService), "")
	http.Handle("/rpc", s)
}

func registerGraphQLHandler() {
	schema := schema.MerkneraSchema()

	h := graphql.NewCORSHandler(&handler.Config{
		Schema: schema,
		Pretty: true,
	})

	http.Handle("/graphql", h)
}

func registerStaticFileServerHandler() {
	fs := http.FileServer(http.Dir("static"))
	http.Handle("/", fs)
}

func registerAboutHandler() {
	http.HandleFunc("/about", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "
Welcome to Merknera
")
	})
}

func registerLoginHandler() {
	lih := security.LoginHandler{}
	http.Handle("/login", lih)
	loh := security.LogoutHandler{}
	http.Handle("/logout", loh)
}

func verifyBotsAndQueueMoves() {
	botList, err := repository.ListBots()
	if err != nil {
		log.Fatal(err)
	}
	for _, b := range botList {
		b.Ping()
	}

	// Find all moves that are currently awaiting play and queue them.
	awaitingMoves, err := repository.ListAwaitingMoves()
	if err != nil {
		log.Fatal(err)
	}
	for _, gm := range awaitingMoves {
		log.Printf("Re-queuing: %d\n", gm.Id)
		gameworker.QueueGameMove(gm)
	}
}

func main() {
	registerRPCHandler()
	registerGraphQLHandler()
	graphiql := os.Getenv("MERKNERA_GRAPHIQL")
	if graphiql == "TRUE" {
		registerStaticFileServerHandler()
	}
	registerAboutHandler()
	registerLoginHandler()

	gameworker.StartGameMoveDispatcher(4)

	go verifyBotsAndQueueMoves()

	fmt.Println("Merknera is now listening on localhost:8080")
	http.ListenAndServe(":8080", nil)
}
<commit_msg>Don't log the re-queuing of moves.<commit_after>package main

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

	"os"

	"github.com/graphql-go/handler"
	"github.com/mleonard87/merknera/gameworker"
	"github.com/mleonard87/merknera/graphql"
	"github.com/mleonard87/merknera/repository"
	"github.com/mleonard87/merknera/schema"
	"github.com/mleonard87/merknera/security"
	"github.com/mleonard87/merknera/services"
	"github.com/mleonard87/rpc"
	"github.com/mleonard87/rpc/json"
)

func registerRPCHandler() {
	s := rpc.NewServer()
	s.RegisterCodec(json.NewCodec(), "application/json")
	s.RegisterService(new(services.RegistrationService), "")
	http.Handle("/rpc", s)
}

func registerGraphQLHandler() {
	schema := schema.MerkneraSchema()

	h := graphql.NewCORSHandler(&handler.Config{
		Schema: schema,
		Pretty: true,
	})

	http.Handle("/graphql", h)
}

func registerStaticFileServerHandler() {
	fs := http.FileServer(http.Dir("static"))
	http.Handle("/", fs)
}

func registerAboutHandler() {
	http.HandleFunc("/about", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "
Welcome to Merknera
")
	})
}

func registerLoginHandler() {
	lih := security.LoginHandler{}
	http.Handle("/login", lih)
	loh := security.LogoutHandler{}
	http.Handle("/logout", loh)
}

func verifyBotsAndQueueMoves() {
	botList, err := repository.ListBots()
	if err != nil {
		log.Fatal(err)
	}
	for _, b := range botList {
		b.Ping()
	}

	// Find all moves that are currently awaiting play and queue them.
	awaitingMoves, err := repository.ListAwaitingMoves()
	if err != nil {
		log.Fatal(err)
	}
	for _, gm := range awaitingMoves {
		gameworker.QueueGameMove(gm)
	}
}

func main() {
	registerRPCHandler()
	registerGraphQLHandler()
	graphiql := os.Getenv("MERKNERA_GRAPHIQL")
	if graphiql == "TRUE" {
		registerStaticFileServerHandler()
	}
	registerAboutHandler()
	registerLoginHandler()

	gameworker.StartGameMoveDispatcher(4)

	go verifyBotsAndQueueMoves()

	fmt.Println("Merknera is now listening on localhost:8080")
	http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before>// Copyright 2014 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License. See the AUTHORS file
// for names of contributors.
//
// Author: Spencer Kimball (spencer.kimball@gmail.com)

package storage

import (
	"container/heap"
	"sync"
	"time"

	"github.com/cockroachdb/cockroach/proto"
	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/hlc"
	"github.com/cockroachdb/cockroach/util/log"
)

// A rangeItem holds a range and its priority for use with a priority queue.
type rangeItem struct {
	value    *Range
	priority float64
	// The index is needed by update and is maintained by the heap.Interface methods.
	index int // The index of the item in the heap.
}

// A priorityQueue implements heap.Interface and holds rangeItems.
type priorityQueue []*rangeItem

func (pq priorityQueue) Len() int { return len(pq) }

func (pq priorityQueue) Less(i, j int) bool {
	// We want Pop to give us the highest, not lowest, priority so we use greater than here.
	return pq[i].priority > pq[j].priority
}

func (pq priorityQueue) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
	pq[i].index, pq[j].index = i, j
}

func (pq *priorityQueue) Push(x interface{}) {
	n := len(*pq)
	item := x.(*rangeItem)
	item.index = n
	*pq = append(*pq, item)
}

func (pq *priorityQueue) Pop() interface{} {
	old := *pq
	n := len(old)
	item := old[n-1]
	item.index = -1 // for safety
	*pq = old[0 : n-1]
	return item
}

// update modifies the priority of a rangeItem in the queue.
func (pq *priorityQueue) update(item *rangeItem, priority float64) {
	item.priority = priority
	heap.Fix(pq, item.index)
}

// shouldQueueFn accepts current time and a Range and returns whether
// it should be queued and if so, at what priority.
type shouldQueueFn func(proto.Timestamp, *Range) (shouldQueue bool, priority float64)

// processFn accepts current time and a range and executes
// queue-specific work on it.
type processFn func(proto.Timestamp, *Range) error

// timerFn returns a duration to wait between processing the next item
// from the queue.
type timerFn func() time.Duration

// baseQueue is the base implementation of the rangeQueue interface.
// Queue implementations should embed a baseQueue and provide it
// with shouldQueueFn.
//
// baseQueue is not thread safe and is intended for usage only from
// the scanner's goroutine.
type baseQueue struct {
	name       string
	shouldQ    shouldQueueFn        // Should a range be queued?
	process    processFn            // Executes queue-specific work on range
	timer      timerFn              // Returns duration for queue processing
	maxSize    int                  // Maximum number of ranges to queue
	incoming   chan *Range          // Channel for ranges to be queued
	sync.Mutex                      // Mutex protects priorityQ and ranges
	priorityQ  priorityQueue        // The priority queue
	ranges     map[int64]*rangeItem // Map from RaftID to rangeItem (for updating priority)
}

// newBaseQueue returns a new instance of baseQueue with the
// specified shouldQ function to determine which ranges to queue
// and maxSize to limit the growth of the queue. Note that
// maxSize doesn't prevent new ranges from being added, it just
// limits the total size. Higher priority ranges can still be
// added; their addition simply removes the lowest priority range.
func newBaseQueue(name string, shouldQ shouldQueueFn, process processFn, timer timerFn, maxSize int) *baseQueue {
	return &baseQueue{
		name:     name,
		shouldQ:  shouldQ,
		process:  process,
		timer:    timer,
		maxSize:  maxSize,
		incoming: make(chan *Range, 10),
		ranges:   map[int64]*rangeItem{},
	}
}

// Length returns the current size of the queue.
func (bq *baseQueue) Length() int {
	bq.Lock()
	defer bq.Unlock()
	return bq.priorityQ.Len()
}

// Start launches a goroutine to process entries in the queue. The
// provided stopper is used to finish processing.
func (bq *baseQueue) Start(clock *hlc.Clock, stopper *util.Stopper) {
	stopper.Add(1)
	go bq.processLoop(clock, stopper)
}

// MaybeAdd adds the specified range if bq.shouldQ specifies it should
// be queued. Ranges are added to the queue using the priority
// returned by bq.shouldQ. If the queue is too full, an already-queued
// range with the lowest priority may be dropped.
func (bq *baseQueue) MaybeAdd(rng *Range, now proto.Timestamp) {
	bq.Lock()
	defer bq.Unlock()
	should, priority := bq.shouldQ(now, rng)
	item, ok := bq.ranges[rng.Desc().RaftID]
	if !should {
		if ok {
			bq.remove(item.index)
		}
		return
	} else if ok {
		// Range has already been added; update priority.
		bq.priorityQ.update(item, priority)
		return
	}

	log.Infof("adding range %s to %s queue", rng, bq.name)
	item = &rangeItem{value: rng, priority: priority}
	heap.Push(&bq.priorityQ, item)
	bq.ranges[rng.Desc().RaftID] = item

	// If adding this range has pushed the queue past its maximum size,
	// remove the lowest priority element.
	if pqLen := bq.priorityQ.Len(); pqLen > bq.maxSize {
		bq.remove(pqLen - 1)
	}
	// Signal the processLoop that a range has been added.
	bq.incoming <- rng
}

// MaybeRemove removes the specified range from the queue if enqueued.
func (bq *baseQueue) MaybeRemove(rng *Range) {
	bq.Lock()
	defer bq.Unlock()
	if item, ok := bq.ranges[rng.Desc().RaftID]; ok {
		log.Infof("removing range %s from %s queue", item.value, bq.name)
		bq.remove(item.index)
	}
}

// process processes the entries in the queue until the provided
// stopper signals exit.
//
// TODO(spencer): current load should factor into range processing timer.
func (bq *baseQueue) processLoop(clock *hlc.Clock, stopper *util.Stopper) {
	// nextTime is set arbitrarily far into the future so that we don't
	// unecessarily check for a range to dequeue if the timer function
	// returns a short duration but the priority queue is empty.
	emptyQueue := true
	nextTime := time.Now().Add(24 * time.Hour)

	for {
		select {
		// Incoming ranges set the next time to process in the event that
		// there were previously no ranges in the queue.
		case <-bq.incoming:
			if emptyQueue {
				emptyQueue = false
				nextTime = time.Now().Add(bq.timer())
			}
		// Process ranges as the timer expires.
		case <-time.After(nextTime.Sub(time.Now())):
			start := time.Now()
			nextTime = start.Add(bq.timer())
			bq.Lock()
			rng := bq.pop()
			bq.Unlock()
			if rng != nil {
				log.Infof("processing range %s from %s queue...", rng, bq.name)
				if err := bq.process(clock.Now(), rng); err != nil {
					log.Errorf("failure processing range %s from %s queue: %s", rng, bq.name, err)
				}
				log.Infof("processed range %s from %s queue in %s", rng, bq.name, time.Now().Sub(start))
			}
			if bq.Length() == 0 {
				emptyQueue = true
				nextTime = time.Now().Add(24 * time.Hour)
			}
		// Exit on stopper.
		case <-stopper.ShouldStop():
			stopper.SetStopped()
			bq.Lock()
			bq.Unlock()
			bq.ranges = map[int64]*rangeItem{}
			bq.priorityQ = nil
			return
		}
	}
}

// pop dequeues the highest priority range in the queue. Returns the
// range if not empty; otherwise, returns nil. Expects mutex to be
// locked.
func (bq *baseQueue) pop() *Range {
	if bq.priorityQ.Len() == 0 {
		return nil
	}
	item := heap.Pop(&bq.priorityQ).(*rangeItem)
	delete(bq.ranges, item.value.Desc().RaftID)
	return item.value
}

// remove removes an element from the priority queue by index. Expects
// mutex to be locked.
func (bq *baseQueue) remove(index int) {
	item := heap.Remove(&bq.priorityQ, index).(*rangeItem)
	delete(bq.ranges, item.value.Desc().RaftID)
}
<commit_msg>Move a non-deferred unlock to the right place.<commit_after>// Copyright 2014 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License. See the AUTHORS file
// for names of contributors.
//
// Author: Spencer Kimball (spencer.kimball@gmail.com)

package storage

import (
	"container/heap"
	"sync"
	"time"

	"github.com/cockroachdb/cockroach/proto"
	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/hlc"
	"github.com/cockroachdb/cockroach/util/log"
)

// A rangeItem holds a range and its priority for use with a priority queue.
type rangeItem struct {
	value    *Range
	priority float64
	// The index is needed by update and is maintained by the heap.Interface methods.
	index int // The index of the item in the heap.
}

// A priorityQueue implements heap.Interface and holds rangeItems.
type priorityQueue []*rangeItem

func (pq priorityQueue) Len() int { return len(pq) }

func (pq priorityQueue) Less(i, j int) bool {
	// We want Pop to give us the highest, not lowest, priority so we use greater than here.
	return pq[i].priority > pq[j].priority
}

func (pq priorityQueue) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
	pq[i].index, pq[j].index = i, j
}

func (pq *priorityQueue) Push(x interface{}) {
	n := len(*pq)
	item := x.(*rangeItem)
	item.index = n
	*pq = append(*pq, item)
}

func (pq *priorityQueue) Pop() interface{} {
	old := *pq
	n := len(old)
	item := old[n-1]
	item.index = -1 // for safety
	*pq = old[0 : n-1]
	return item
}

// update modifies the priority of a rangeItem in the queue.
func (pq *priorityQueue) update(item *rangeItem, priority float64) {
	item.priority = priority
	heap.Fix(pq, item.index)
}

// shouldQueueFn accepts current time and a Range and returns whether
// it should be queued and if so, at what priority.
type shouldQueueFn func(proto.Timestamp, *Range) (shouldQueue bool, priority float64)

// processFn accepts current time and a range and executes
// queue-specific work on it.
type processFn func(proto.Timestamp, *Range) error

// timerFn returns a duration to wait between processing the next item
// from the queue.
type timerFn func() time.Duration

// baseQueue is the base implementation of the rangeQueue interface.
// Queue implementations should embed a baseQueue and provide it
// with shouldQueueFn.
//
// baseQueue is not thread safe and is intended for usage only from
// the scanner's goroutine.
type baseQueue struct {
	name       string
	shouldQ    shouldQueueFn        // Should a range be queued?
	process    processFn            // Executes queue-specific work on range
	timer      timerFn              // Returns duration for queue processing
	maxSize    int                  // Maximum number of ranges to queue
	incoming   chan *Range          // Channel for ranges to be queued
	sync.Mutex                      // Mutex protects priorityQ and ranges
	priorityQ  priorityQueue        // The priority queue
	ranges     map[int64]*rangeItem // Map from RaftID to rangeItem (for updating priority)
}

// newBaseQueue returns a new instance of baseQueue with the
// specified shouldQ function to determine which ranges to queue
// and maxSize to limit the growth of the queue. Note that
// maxSize doesn't prevent new ranges from being added, it just
// limits the total size. Higher priority ranges can still be
// added; their addition simply removes the lowest priority range.
func newBaseQueue(name string, shouldQ shouldQueueFn, process processFn, timer timerFn, maxSize int) *baseQueue {
	return &baseQueue{
		name:     name,
		shouldQ:  shouldQ,
		process:  process,
		timer:    timer,
		maxSize:  maxSize,
		incoming: make(chan *Range, 10),
		ranges:   map[int64]*rangeItem{},
	}
}

// Length returns the current size of the queue.
func (bq *baseQueue) Length() int {
	bq.Lock()
	defer bq.Unlock()
	return bq.priorityQ.Len()
}

// Start launches a goroutine to process entries in the queue. The
// provided stopper is used to finish processing.
func (bq *baseQueue) Start(clock *hlc.Clock, stopper *util.Stopper) {
	stopper.Add(1)
	go bq.processLoop(clock, stopper)
}

// MaybeAdd adds the specified range if bq.shouldQ specifies it should
// be queued. Ranges are added to the queue using the priority
// returned by bq.shouldQ. If the queue is too full, an already-queued
// range with the lowest priority may be dropped.
func (bq *baseQueue) MaybeAdd(rng *Range, now proto.Timestamp) {
	bq.Lock()
	defer bq.Unlock()
	should, priority := bq.shouldQ(now, rng)
	item, ok := bq.ranges[rng.Desc().RaftID]
	if !should {
		if ok {
			bq.remove(item.index)
		}
		return
	} else if ok {
		// Range has already been added; update priority.
		bq.priorityQ.update(item, priority)
		return
	}

	log.Infof("adding range %s to %s queue", rng, bq.name)
	item = &rangeItem{value: rng, priority: priority}
	heap.Push(&bq.priorityQ, item)
	bq.ranges[rng.Desc().RaftID] = item

	// If adding this range has pushed the queue past its maximum size,
	// remove the lowest priority element.
	if pqLen := bq.priorityQ.Len(); pqLen > bq.maxSize {
		bq.remove(pqLen - 1)
	}
	// Signal the processLoop that a range has been added.
	bq.incoming <- rng
}

// MaybeRemove removes the specified range from the queue if enqueued.
func (bq *baseQueue) MaybeRemove(rng *Range) {
	bq.Lock()
	defer bq.Unlock()
	if item, ok := bq.ranges[rng.Desc().RaftID]; ok {
		log.Infof("removing range %s from %s queue", item.value, bq.name)
		bq.remove(item.index)
	}
}

// process processes the entries in the queue until the provided
// stopper signals exit.
//
// TODO(spencer): current load should factor into range processing timer.
func (bq *baseQueue) processLoop(clock *hlc.Clock, stopper *util.Stopper) {
	// nextTime is set arbitrarily far into the future so that we don't
	// unecessarily check for a range to dequeue if the timer function
	// returns a short duration but the priority queue is empty.
	emptyQueue := true
	nextTime := time.Now().Add(24 * time.Hour)

	for {
		select {
		// Incoming ranges set the next time to process in the event that
		// there were previously no ranges in the queue.
		case <-bq.incoming:
			if emptyQueue {
				emptyQueue = false
				nextTime = time.Now().Add(bq.timer())
			}
		// Process ranges as the timer expires.
		case <-time.After(nextTime.Sub(time.Now())):
			start := time.Now()
			nextTime = start.Add(bq.timer())
			bq.Lock()
			rng := bq.pop()
			bq.Unlock()
			if rng != nil {
				log.Infof("processing range %s from %s queue...", rng, bq.name)
				if err := bq.process(clock.Now(), rng); err != nil {
					log.Errorf("failure processing range %s from %s queue: %s", rng, bq.name, err)
				}
				log.Infof("processed range %s from %s queue in %s", rng, bq.name, time.Now().Sub(start))
			}
			if bq.Length() == 0 {
				emptyQueue = true
				nextTime = time.Now().Add(24 * time.Hour)
			}
		// Exit on stopper.
		case <-stopper.ShouldStop():
			stopper.SetStopped()
			bq.Lock()
			bq.ranges = map[int64]*rangeItem{}
			bq.priorityQ = nil
			bq.Unlock()
			return
		}
	}
}

// pop dequeues the highest priority range in the queue. Returns the
// range if not empty; otherwise, returns nil. Expects mutex to be
// locked.
func (bq *baseQueue) pop() *Range {
	if bq.priorityQ.Len() == 0 {
		return nil
	}
	item := heap.Pop(&bq.priorityQ).(*rangeItem)
	delete(bq.ranges, item.value.Desc().RaftID)
	return item.value
}

// remove removes an element from the priority queue by index. Expects
// mutex to be locked.
func (bq *baseQueue) remove(index int) {
	item := heap.Remove(&bq.priorityQ, index).(*rangeItem)
	delete(bq.ranges, item.value.Desc().RaftID)
}
<|endoftext|>


<commit_before>// Copyright 2013 Bobby Powers. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

// FIXME: these definitions are from kernel headers.  I either need to
// revise them, or relicense under the GPL2.

package spi

import (
	"fmt"
	"os"
	"unsafe"
)

const (
	SPI_CPHA = 0x01
	SPI_CPOL = 0x02

	SPI_MODE_0 = (0 | 0)
	SPI_MODE_1 = (0 | SPI_CPHA)
	SPI_MODE_2 = (SPI_CPOL | 0)
	SPI_MODE_3 = (SPI_CPOL | SPI_CPHA)

	SPI_CS_HIGH   = 0x04
	SPI_LSB_FIRST = 0x08
	SPI_3WIRE     = 0x10
	SPI_LOOP      = 0x20
	SPI_NO_CS     = 0x40
	SPI_READY     = 0x80

	SPI_IOC_MAGIC = 'k'
)

type SPIIOTransaction struct {
	TXBuf       uint64
	RXBuf       uint64
	Len         uint32
	SpeedHz     uint32
	DelayUsecs  uint16
	BitsPerWord uint8
	CSChange    uint8
	Pad         uint32
}

const sizeof_SPIIOTransaction = 32

var (
	SPI_IOC_RD_MODE = IOR(SPI_IOC_MAGIC, 1, 1)
	SPI_IOC_WR_MODE = IOW(SPI_IOC_MAGIC, 1, 1)

	// Read / Write SPI bit justification
	SPI_IOC_RD_LSB_FIRST = IOR(SPI_IOC_MAGIC, 2, 1)
	SPI_IOC_WR_LSB_FIRST = IOW(SPI_IOC_MAGIC, 2, 1)

	// Read / Write SPI device word length (1..N)
	SPI_IOC_RD_BITS_PER_WORD = IOR(SPI_IOC_MAGIC, 3, 1)
	SPI_IOC_WR_BITS_PER_WORD = IOW(SPI_IOC_MAGIC, 3, 1)

	// Read / Write SPI device default max speed hz
	SPI_IOC_RD_MAX_SPEED_HZ = IOR(SPI_IOC_MAGIC, 4, 4)
	SPI_IOC_WR_MAX_SPEED_HZ = IOW(SPI_IOC_MAGIC, 4, 4)
)

func SPI_IOC_MESSAGE(count int) int32 {
	return IOW(SPI_IOC_MAGIC, 0, count*sizeof_SPIIOTransaction)
}

func Transaction(f *os.File, write, read []byte) error {
	if write != nil && read != nil {
		if len(write) != len(read) {
			return fmt.Errorf("write and read size mismatch (%d vs %d)",
				len(write), len(read))
		}
	}
	var length uint32
	if write != nil {
		length = uint32(len(write))
	} else {
		length = uint32(len(read))
	}
	trx := SPIIOTransaction{
		TXBuf: uint64(uintptr(unsafe.Pointer(&write[0]))),
		RXBuf: uint64(uintptr(unsafe.Pointer(&write[0]))),
		Len:   length,
	}
	return ioctl(f.Fd(), SPI_IOC_MESSAGE(1), unsafe.Pointer(&trx))
}
<commit_msg>spi: fix unsafe getting of addr<commit_after>// Copyright 2013 Bobby Powers. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

// FIXME: these definitions are from kernel headers.  I either need to
// revise them, or relicense under the GPL2.

package spi

import (
	"fmt"
	"os"
	"unsafe"
)

const (
	SPI_CPHA = 0x01
	SPI_CPOL = 0x02

	SPI_MODE_0 = (0 | 0)
	SPI_MODE_1 = (0 | SPI_CPHA)
	SPI_MODE_2 = (SPI_CPOL | 0)
	SPI_MODE_3 = (SPI_CPOL | SPI_CPHA)

	SPI_CS_HIGH   = 0x04
	SPI_LSB_FIRST = 0x08
	SPI_3WIRE     = 0x10
	SPI_LOOP      = 0x20
	SPI_NO_CS     = 0x40
	SPI_READY     = 0x80

	SPI_IOC_MAGIC = 'k'
)

type SPIIOTransaction struct {
	TXBuf       uint64
	RXBuf       uint64
	Len         uint32
	SpeedHz     uint32
	DelayUsecs  uint16
	BitsPerWord uint8
	CSChange    uint8
	Pad         uint32
}

const sizeof_SPIIOTransaction = 32

var (
	SPI_IOC_RD_MODE = IOR(SPI_IOC_MAGIC, 1, 1)
	SPI_IOC_WR_MODE = IOW(SPI_IOC_MAGIC, 1, 1)

	// Read / Write SPI bit justification
	SPI_IOC_RD_LSB_FIRST = IOR(SPI_IOC_MAGIC, 2, 1)
	SPI_IOC_WR_LSB_FIRST = IOW(SPI_IOC_MAGIC, 2, 1)

	// Read / Write SPI device word length (1..N)
	SPI_IOC_RD_BITS_PER_WORD = IOR(SPI_IOC_MAGIC, 3, 1)
	SPI_IOC_WR_BITS_PER_WORD = IOW(SPI_IOC_MAGIC, 3, 1)

	// Read / Write SPI device default max speed hz
	SPI_IOC_RD_MAX_SPEED_HZ = IOR(SPI_IOC_MAGIC, 4, 4)
	SPI_IOC_WR_MAX_SPEED_HZ = IOW(SPI_IOC_MAGIC, 4, 4)
)

func SPI_IOC_MESSAGE(count int) int32 {
	return IOW(SPI_IOC_MAGIC, 0, count*sizeof_SPIIOTransaction)
}

func Transaction(f *os.File, write, read []byte) error {
	if write != nil && read != nil {
		if len(write) != len(read) {
			return fmt.Errorf("write and read size mismatch (%d vs %d)",
				len(write), len(read))
		}
	}
	var length uint32
	var wp, rp unsafe.Pointer
	if write != nil {
		wp = unsafe.Pointer(&write[0])
		length = uint32(len(write))
	}
	if read != nil {
		rp = unsafe.Pointer(&read[0])
		length = uint32(len(read))
	}
	trx := SPIIOTransaction{
		TXBuf: uint64(uintptr(wp)),
		RXBuf: uint64(uintptr(rp)),
		Len:   length,
	}
	return ioctl(f.Fd(), SPI_IOC_MESSAGE(1), unsafe.Pointer(&trx))
}
<|endoftext|>


<commit_before>package deeprefactoringbot

import (
	"fmt"
	"math/rand"
	"strings"
	"time"
)

var GreetingMessages = []string{
	"{username}, деплоить докером не бросим",
	"Питон лучше руби",
	"Руби лучше питон",
	"Элексир лучше питона и руби",
	"Вечер в коворкинг, {username}, деплой в радость, ролбек в сладость",
	"Привет, {username}",
	"{username}, опиши свой проект версией дебиана",
	"{username}, сколько ты принес прибыли компании сегодня?",
	"raise Exception('hello {username}')",
	"Лучший воронежский чат о рефакторинге привествует тебя, {username}",
	"1 1111 11111111 11111111",
	"Какой JS фреймворк ты предпочитаешь, {username}?",
}

var CurseMessages = []string{
	"{username}, мы не будем скучать",
	"наверное {username} фронтэндер",
	"Press F to Pay Respects for {username}",
	"Мы тебя тоже не любим",
}

var RollMessages = []string{
	"Живые форки php4",
	"Переходные процессы в проектах однодневках",
	"Статическая типизация аналогов баша",
	"Перспективы перехода на subservion в рамках энтерпрайз проекта",
	"Логин от рута, преимущества и руткиты",
	"Санный или ссаный .net",
	"Шифрование методом редупликации, секретики-хуетики",
	"Самый быстрый способ уйти в swap",
	"Сколько в вашей команде женщин за 40 без кошек?",
	"Жать ли руку руби программистам?",
	"Преимущество использования widows для хостинга статичных файлов",
	"Почему питон делится на 6?",
	"Проектный менеджер, что ты несешь?",
	"Лучший шаблон шаблона на с++ 2017",
	"Ранний уход на пенсию по причине создания js фреймворка",
	"Сколько раз ты ронял базу в этом месяце (опрос)",
	"Монитора два, а продакшен сервер один, за и против",
	"Как лучше посылать логи в /dev/null",
	"Использование mutex в языках с GIL",
	"Веб 1.0 на перл, перекличка работников Яндекса",
	"Когда будет новый пост от Чистякова в его канале?",
	"Дженерики в баше, хватит это терпеть",
	"Документация проекта очевидными коментариями",
	"Безключевой доступ в AWS",
	"Jira и другие способы почувстовать себя плохо",
	"Выход из vim народными средствами",
	"Рациональны предложения менеджеру, которые ты никогда не озвучишь",
	"Расчет 6%% на Spring Boot или патент?",
	"Че, пацаны, фронтенд?"
}

const nextMeetupMessage = "Анонс следующего митапа: http://deeprefactoring.ru/meetup/next/"
const hammertimeMessage = "https://ci.memecdn.com/2501287.gif"

var randomiser = rand.New(rand.NewSource(time.Now().Unix()))

func randomMessageFromSlice(slice []string) string {
	return slice[randomiser.Intn(len(slice))]
}

func ReplaceUsername(text, username string) string {
	return strings.Replace(text, "{username}", fmt.Sprintf("@%s", username), -1)
}

func RandomGreeting(username string) string {
	text := randomMessageFromSlice(GreetingMessages)
	return ReplaceUsername(text, username)
}

func RandomCurse(username string) string {
	text := randomMessageFromSlice(CurseMessages)
	return ReplaceUsername(text, username)
}

func NextMeetupInfo() string {
	return nextMeetupMessage
}

func HammertimeInfo() string {
	return hammertimeMessage
}

func RollMessage() string {
	return randomMessageFromSlice(RollMessages)
}
<commit_msg>fix missing commas<commit_after>package deeprefactoringbot

import (
	"fmt"
	"math/rand"
	"strings"
	"time"
)

var GreetingMessages = []string{
	"{username}, деплоить докером не бросим",
	"Питон лучше руби",
	"Руби лучше питон",
	"Элексир лучше питона и руби",
	"Вечер в коворкинг, {username}, деплой в радость, ролбек в сладость",
	"Привет, {username}",
	"{username}, опиши свой проект версией дебиана",
	"{username}, сколько ты принес прибыли компании сегодня?",
	"raise Exception('hello {username}')",
	"Лучший воронежский чат о рефакторинге привествует тебя, {username}",
	"1 1111 11111111 11111111",
	"Какой JS фреймворк ты предпочитаешь, {username}?",
}

var CurseMessages = []string{
	"{username}, мы не будем скучать",
	"наверное {username} фронтэндер",
	"Press F to Pay Respects for {username}",
	"Мы тебя тоже не любим",
}

var RollMessages = []string{
	"Живые форки php4",
	"Переходные процессы в проектах однодневках",
	"Статическая типизация аналогов баша",
	"Перспективы перехода на subservion в рамках энтерпрайз проекта",
	"Логин от рута, преимущества и руткиты",
	"Санный или ссаный .net",
	"Шифрование методом редупликации, секретики-хуетики",
	"Самый быстрый способ уйти в swap",
	"Сколько в вашей команде женщин за 40 без кошек?",
	"Жать ли руку руби программистам?",
	"Преимущество использования widows для хостинга статичных файлов",
	"Почему питон делится на 6?",
	"Проектный менеджер, что ты несешь?",
	"Лучший шаблон шаблона на с++ 2017",
	"Ранний уход на пенсию по причине создания js фреймворка",
	"Сколько раз ты ронял базу в этом месяце (опрос)",
	"Монитора два, а продакшен сервер один, за и против",
	"Как лучше посылать логи в /dev/null",
	"Использование mutex в языках с GIL",
	"Веб 1.0 на перл, перекличка работников Яндекса",
	"Когда будет новый пост от Чистякова в его канале?",
	"Дженерики в баше, хватит это терпеть",
	"Документация проекта очевидными коментариями",
	"Безключевой доступ в AWS",
	"Jira и другие способы почувстовать себя плохо",
	"Выход из vim народными средствами",
	"Рациональны предложения менеджеру, которые ты никогда не озвучишь",
	"Расчет 6%% на Spring Boot или патент?",
	"Че, пацаны, фронтенд?",
}

const nextMeetupMessage = "Анонс следующего митапа: http://deeprefactoring.ru/meetup/next/"
const hammertimeMessage = "https://ci.memecdn.com/2501287.gif"

var randomiser = rand.New(rand.NewSource(time.Now().Unix()))

func randomMessageFromSlice(slice []string) string {
	return slice[randomiser.Intn(len(slice))]
}

func ReplaceUsername(text, username string) string {
	return strings.Replace(text, "{username}", fmt.Sprintf("@%s", username), -1)
}

func RandomGreeting(username string) string {
	text := randomMessageFromSlice(GreetingMessages)
	return ReplaceUsername(text, username)
}

func RandomCurse(username string) string {
	text := randomMessageFromSlice(CurseMessages)
	return ReplaceUsername(text, username)
}

func NextMeetupInfo() string {
	return nextMeetupMessage
}

func HammertimeInfo() string {
	return hammertimeMessage
}

func RollMessage() string {
	return randomMessageFromSlice(RollMessages)
}
<|endoftext|>