<commit_before>package router

import "github.com/sqs/mux"

const (
	Build            = "build"
	BuildDequeueNext = "build.dequeue-next"
	BuildUpdate      = "build.update"
	BuildLog         = "build.log"
	Builds           = "builds"
	BuildTasks       = "build.tasks"
	BuildTaskUpdate  = "build.task"
	BuildTasksCreate = "build.tasks.create"
	BuildTaskLog     = "build.task.log"

	Org               = "org"
	OrgMembers        = "org.members"
	OrgSettings       = "org.settings"
	OrgSettingsUpdate = "org.settings.update"

	Users                 = "users"
	User                  = "user"
	UserOrgs              = "user.orgs"
	UserAuthors           = "user.authors"
	UserClients           = "user.clients"
	UserEmails            = "user.emails"
	UserFromGitHub        = "user.from-github"
	UserRepoContributions = "user.repo-contributions"
	UserRepoDependencies  = "user.repo-dependencies"
	UserRepoDependents    = "user.repo-dependents"
	UserRefreshProfile    = "user.refresh-profile"
	UserSettings          = "user.settings"
	UserSettingsUpdate    = "user.settings.update"
	UserComputeStats      = "user.compute-stats"

	Person = "person"

	RepoPullRequests              = "repo.pull-requests"
	RepoPullRequest               = "repo.pull-request"
	RepoPullRequestMerge          = "repo.pull-request.merge"
	RepoPullRequestComments       = "repo.pull-request.comments"
	RepoPullRequestCommentsCreate = "repo.pull-request.comments.create"
	RepoPullRequestCommentsEdit   = "repo.pull-request.comments.edit"
	RepoPullRequestCommentsDelete = "repo.pull-request.comments.delete"

	RepoIssues              = "repo.issues"
	RepoIssue               = "repo.issue"
	RepoIssueComments       = "repo.issue.comments"
	RepoIssueCommentsCreate = "repo.issue.comments.create"
	RepoIssueCommentsEdit   = "repo.issue.comments.edit"
	RepoIssueCommentsDelete = "repo.issue.comments.delete"

	Repos              = "repos"
	ReposCreate        = "repos.create"
	ReposGetOrCreate   = "repos.get-or-create"
	Repo               = "repo"
	RepoAuthors        = "repo.authors"
	RepoClients        = "repo.clients"
	RepoDependents     = "repo.dependents"
	RepoDependencies   = "repo.dependencies"
	RepoBadge          = "repo.badge"
	RepoBadges         = "repo.badges"
	RepoCounter        = "repo.counter"
	RepoCounters       = "repo.counters"
	RepoReadme         = "repo.readme"
	RepoBuilds         = "repo.builds"
	RepoBuildsCreate   = "repo.builds.create"
	RepoBuildDataEntry = "repo.build-data.entry"
	RepoTreeEntry      = "repo.tree.entry"
	RepoRefreshProfile = "repo.refresh-profile"
	RepoRefreshVCSData = "repo.refresh-vcs-data"
	RepoComputeStats   = "repo.compute-stats"

	RepoSettings       = "repo.settings"
	RepoSettingsUpdate = "repo.settings.update"

	RepoStats          = "repo.stats"
	RepoCombinedStatus = "repo.combined-status"

	RepoBuild = "repo.build"

	RepoCommits        = "repo.commits"
	RepoCommit         = "repo.commit"
	RepoCompareCommits = "repo.compare-commits"
	RepoTags           = "repo.tags"
	RepoBranches       = "repo.branches"

	Search         = "search"
	SearchComplete = "search.complete"

	SearchSuggestions = "search.suggestions"

	Snippet = "snippet"

	Defs          = "defs"
	Def           = "def"
	DefRefs       = "def.refs"
	DefExamples   = "def.examples"
	DefAuthors    = "def.authors"
	DefClients    = "def.clients"
	DefDependents = "def.dependents"
	DefVersions   = "def.versions"

	Delta                   = "delta"
	DeltaDefs               = "delta.defs"
	DeltaDependencies       = "delta.dependencies"
	DeltaFiles              = "delta.files"
	DeltaAffectedAuthors    = "delta.affected-authors"
	DeltaAffectedClients    = "delta.affected-clients"
	DeltaAffectedDependents = "delta.affected-dependents"
	DeltaReviewers          = "delta.reviewers"
	DeltasIncoming          = "deltas.incoming"

	Unit  = "unit"
	Units = "units"

	Markdown = "markdown"

	ExtGitHubReceiveWebhook = "ext.github.receive-webhook"

	// Redirects for old routes.
	RedirectOldRepoBadgesAndCounters = "repo.redirect-old-badges-and-counters"
)

// NewAPIRouter creates a new API router with route URL pattern definitions but
// no handlers attached to the routes.
//
// It is in a separate package from app so that other packages may use it to
// generate URLs without resulting in Go import cycles (and so we can release
// the router as open-source to support our client library).
func NewAPIRouter(base *mux.Router) *mux.Router {
	if base == nil {
		base = mux.NewRouter()
	}

	base.StrictSlash(true)

	base.Path("/builds").Methods("GET").Name(Builds)
	builds := base.PathPrefix("/builds").Subrouter()
	builds.Path("/next").Methods("POST").Name(BuildDequeueNext)
	buildPath := "/{BID}"
	builds.Path(buildPath).Methods("GET").Name(Build)
	builds.Path(buildPath).Methods("PUT").Name(BuildUpdate)
	build := builds.PathPrefix(buildPath).Subrouter()
	build.Path("/log").Methods("GET").Name(BuildLog)
	build.Path("/tasks").Methods("GET").Name(BuildTasks)
	build.Path("/tasks").Methods("POST").Name(BuildTasksCreate)
	build.Path("/tasks/{TaskID}").Methods("PUT").Name(BuildTaskUpdate)
	build.Path("/tasks/{TaskID}/log").Methods("GET").Name(BuildTaskLog)

	base.Path("/repos").Methods("GET").Name(Repos)
	base.Path("/repos").Methods("POST").Name(ReposCreate)

	base.Path("/repos/github.com/{owner:[^/]+}/{repo:[^/]+}/{what:(?:badges|counters)}/{which}.png").Methods("GET").Name(RedirectOldRepoBadgesAndCounters)

	repoRev := base.PathPrefix(`/repos/` + RepoRevSpecPattern).PostMatchFunc(FixRepoRevSpecVars).BuildVarsFunc(PrepareRepoRevSpecRouteVars).Subrouter()
	repoRev.Path("/.stats").Methods("PUT").Name(RepoComputeStats)
	repoRev.Path("/.stats").Methods("GET").Name(RepoStats)
	repoRev.Path("/.status").Methods("GET").Name(RepoCombinedStatus)
	repoRev.Path("/.authors").Methods("GET").Name(RepoAuthors)
	repoRev.Path("/.readme").Methods("GET").Name(RepoReadme)
	repoRev.Path("/.build").Methods("GET").Name(RepoBuild)
	repoRev.Path("/.dependencies").Methods("GET").Name(RepoDependencies)
	repoRev.PathPrefix("/.build-data"+TreeEntryPathPattern).PostMatchFunc(FixTreeEntryVars).BuildVarsFunc(PrepareTreeEntryRouteVars).Methods("GET", "HEAD", "PUT", "DELETE").Name(RepoBuildDataEntry)
	repoRev.Path("/.badges/{Badge}.png").Methods("GET").Name(RepoBadge)

	// repo contains routes that are NOT specific to a revision. In these routes, the URL may not contain a revspec after the repo (that is, no "github.com/foo/bar@myrevspec").
	repoPath := `/repos/` + RepoSpecPathPattern
	base.Path(repoPath).Methods("GET").Name(Repo)
	base.Path(repoPath).Methods("PUT").Name(ReposGetOrCreate)
	repo := base.PathPrefix(repoPath).Subrouter()
	repo.Path("/.clients").Methods("GET").Name(RepoClients)
	repo.Path("/.dependents").Methods("GET").Name(RepoDependents)
	repo.Path("/.external-profile").Methods("PUT").Name(RepoRefreshProfile)
	repo.Path("/.vcs-data").Methods("PUT").Name(RepoRefreshVCSData)
	repo.Path("/.settings").Methods("GET").Name(RepoSettings)
	repo.Path("/.settings").Methods("PUT").Name(RepoSettingsUpdate)
	repo.Path("/.commits").Methods("GET").Name(RepoCommits)
	repo.Path("/.commits/{Rev:" + PathComponentNoLeadingDot + "}/.compare").Methods("GET").Name(RepoCompareCommits)
	repo.Path("/.commits/{Rev:" + PathComponentNoLeadingDot + "}").Methods("GET").Name(RepoCommit)
	repo.Path("/.branches").Methods("GET").Name(RepoBranches)
	repo.Path("/.tags").Methods("GET").Name(RepoTags)
	repo.Path("/.badges").Methods("GET").Name(RepoBadges)
	repo.Path("/.counters").Methods("GET").Name(RepoCounters)
	repo.Path("/.counters/{Counter}.png").Methods("GET").Name(RepoCounter)
	repo.Path("/.builds").Methods("GET").Name(RepoBuilds)
	repo.Path("/.builds").Methods("POST").Name(RepoBuildsCreate)

	repo.Path("/.pulls").Methods("GET").Name(RepoPullRequests)
	pullPath := "/.pulls/{Pull}"
	repo.Path(pullPath).Methods("GET").Name(RepoPullRequest)
	pull := repo.PathPrefix(pullPath).Subrouter()
	pull.Path("/merge").Methods("PUT").Name(RepoPullRequestMerge)
	pull.Path("/comments").Methods("GET").Name(RepoPullRequestComments)
	pull.Path("/comments").Methods("POST").Name(RepoPullRequestCommentsCreate)
	pull.Path("/comments/{CommentID}").Methods("PATCH", "PUT").Name(RepoPullRequestCommentsEdit)
	pull.Path("/comments/{CommentID}").Methods("DELETE").Name(RepoPullRequestCommentsDelete)

	repo.Path("/.issues").Methods("GET").Name(RepoIssues)
	issuePath := "/.issues/{Issue}"
	repo.Path(issuePath).Methods("GET").Name(RepoIssue)
	issue := repo.PathPrefix(issuePath).Subrouter()
	issue.Path("/comments").Methods("GET").Name(RepoIssueComments)
	issue.Path("/comments").Methods("POST").Name(RepoIssueCommentsCreate)
	issue.Path("/comments/{CommentID}").Methods("PATCH", "PUT").Name(RepoIssueCommentsEdit)
	issue.Path("/comments/{CommentID}").Methods("DELETE").Name(RepoIssueCommentsDelete)

	deltaPath := "/.deltas/{Rev:.+}..{DeltaHeadRev:" + PathComponentNoLeadingDot + "}"
	repo.Path(deltaPath).Methods("GET").Name(Delta)
	deltas := repo.PathPrefix(deltaPath).Subrouter()
	deltas.Path("/.defs").Methods("GET").Name(DeltaDefs)
	deltas.Path("/.dependencies").Methods("GET").Name(DeltaDependencies)
	deltas.Path("/.files").Methods("GET").Name(DeltaFiles)
	deltas.Path("/.affected-authors").Methods("GET").Name(DeltaAffectedAuthors)
	deltas.Path("/.affected-clients").Methods("GET").Name(DeltaAffectedClients)
	deltas.Path("/.affected-dependents").Methods("GET").Name(DeltaAffectedDependents)
	deltas.Path("/.reviewers").Methods("GET").Name(DeltaReviewers)

	repo.Path("/.deltas-incoming").Methods("GET").Name(DeltasIncoming)

	// See router_util/tree_route.go for an explanation of how we match tree
	// entry routes.
	repoRev.Path("/.tree" + TreeEntryPathPattern).PostMatchFunc(FixTreeEntryVars).BuildVarsFunc(PrepareTreeEntryRouteVars).Methods("GET").Name(RepoTreeEntry)

	base.Path(`/people/` + PersonSpecPattern).Methods("GET").Name(Person)

	base.Path("/users").Methods("GET").Name(Users)
	userPath := `/users/` + UserSpecPattern
	base.Path(userPath).Methods("GET").Name(User)
	user := base.PathPrefix(userPath).Subrouter()
	user.Path("/orgs").Methods("GET").Name(UserOrgs)
	user.Path("/clients").Methods("GET").Name(UserClients)
	user.Path("/authors").Methods("GET").Name(UserAuthors)
	user.Path("/emails").Methods("GET").Name(UserEmails)
	user.Path("/repo-contributions").Methods("GET").Name(UserRepoContributions)
	user.Path("/repo-dependencies").Methods("GET").Name(UserRepoDependencies)
	user.Path("/repo-dependents").Methods("GET").Name(UserRepoDependents)
	user.Path("/external-profile").Methods("PUT").Name(UserRefreshProfile)
	user.Path("/stats").Methods("PUT").Name(UserComputeStats)
	user.Path("/settings").Methods("GET").Name(UserSettings)
	user.Path("/settings").Methods("PUT").Name(UserSettingsUpdate)
	base.Path("/external-users/github/{GitHubUserSpec}").Methods("GET").Name(UserFromGitHub)

	orgPath := "/orgs/{OrgSpec}"
	base.Path(orgPath).Methods("GET").Name(Org)
	org := base.PathPrefix(orgPath).Subrouter()
	org.Path("/settings").Methods("GET").Name(OrgSettings)
	org.Path("/settings").Methods("PUT").Name(OrgSettingsUpdate)
	org.Path("/members").Methods("GET").Name(OrgMembers)

	base.Path("/search").Methods("GET").Name(Search)
	base.Path("/search/complete").Methods("GET").Name(SearchComplete)
	base.Path("/search/suggestions").Methods("GET").Name(SearchSuggestions)

	base.Path("/snippet").Methods("GET", "POST", "ORIGIN").Name(Snippet)

	base.Path("/.defs").Methods("GET").Name(Defs)

	// See router_util/def_route.go for an explanation of how we match def
	// routes.
	defPath := `/.defs/` + DefPathPattern
	repoRev.Path(defPath).Methods("GET").PostMatchFunc(FixDefUnitVars).BuildVarsFunc(PrepareDefRouteVars).Name(Def)
	def := repoRev.PathPrefix(defPath).PostMatchFunc(FixDefUnitVars).BuildVarsFunc(PrepareDefRouteVars).Subrouter()
	def.Path("/.refs").Methods("GET").Name(DefRefs)
	def.Path("/.examples").Methods("GET").Name(DefExamples)
	def.Path("/.authors").Methods("GET").Name(DefAuthors)
	def.Path("/.clients").Methods("GET").Name(DefClients)
	def.Path("/.dependents").Methods("GET").Name(DefDependents)
	def.Path("/.versions").Methods("GET").Name(DefVersions)

	base.Path("/.units").Methods("GET").Name(Units)
	unitPath := `/.units/{UnitType}/{Unit:.*}`
	repoRev.Path(unitPath).Methods("GET").Name(Unit)

	base.Path("/markdown").Methods("POST").Name(Markdown)

	base.Path("/ext/github/webhook").Methods("POST").Name(ExtGitHubReceiveWebhook)

	if ExtraConfig != nil {
		ExtraConfig(base, user)
	}

	return base
}

// ExtraConfig, if non-nil, is called by NewAPIRouter with the
// *mux.Router after setting up routes in this package and before
// returning it. It can be used by external packages that use this API
// router and want to add additional routes to it.
var ExtraConfig func(base, user *mux.Router)
<commit_msg>support multiple badge formats<commit_after>package router

import "github.com/sqs/mux"

const (
	Build            = "build"
	BuildDequeueNext = "build.dequeue-next"
	BuildUpdate      = "build.update"
	BuildLog         = "build.log"
	Builds           = "builds"
	BuildTasks       = "build.tasks"
	BuildTaskUpdate  = "build.task"
	BuildTasksCreate = "build.tasks.create"
	BuildTaskLog     = "build.task.log"

	Org               = "org"
	OrgMembers        = "org.members"
	OrgSettings       = "org.settings"
	OrgSettingsUpdate = "org.settings.update"

	Users                 = "users"
	User                  = "user"
	UserOrgs              = "user.orgs"
	UserAuthors           = "user.authors"
	UserClients           = "user.clients"
	UserEmails            = "user.emails"
	UserFromGitHub        = "user.from-github"
	UserRepoContributions = "user.repo-contributions"
	UserRepoDependencies  = "user.repo-dependencies"
	UserRepoDependents    = "user.repo-dependents"
	UserRefreshProfile    = "user.refresh-profile"
	UserSettings          = "user.settings"
	UserSettingsUpdate    = "user.settings.update"
	UserComputeStats      = "user.compute-stats"

	Person = "person"

	RepoPullRequests              = "repo.pull-requests"
	RepoPullRequest               = "repo.pull-request"
	RepoPullRequestMerge          = "repo.pull-request.merge"
	RepoPullRequestComments       = "repo.pull-request.comments"
	RepoPullRequestCommentsCreate = "repo.pull-request.comments.create"
	RepoPullRequestCommentsEdit   = "repo.pull-request.comments.edit"
	RepoPullRequestCommentsDelete = "repo.pull-request.comments.delete"

	RepoIssues              = "repo.issues"
	RepoIssue               = "repo.issue"
	RepoIssueComments       = "repo.issue.comments"
	RepoIssueCommentsCreate = "repo.issue.comments.create"
	RepoIssueCommentsEdit   = "repo.issue.comments.edit"
	RepoIssueCommentsDelete = "repo.issue.comments.delete"

	Repos              = "repos"
	ReposCreate        = "repos.create"
	ReposGetOrCreate   = "repos.get-or-create"
	Repo               = "repo"
	RepoAuthors        = "repo.authors"
	RepoClients        = "repo.clients"
	RepoDependents     = "repo.dependents"
	RepoDependencies   = "repo.dependencies"
	RepoBadge          = "repo.badge"
	RepoBadges         = "repo.badges"
	RepoCounter        = "repo.counter"
	RepoCounters       = "repo.counters"
	RepoReadme         = "repo.readme"
	RepoBuilds         = "repo.builds"
	RepoBuildsCreate   = "repo.builds.create"
	RepoBuildDataEntry = "repo.build-data.entry"
	RepoTreeEntry      = "repo.tree.entry"
	RepoRefreshProfile = "repo.refresh-profile"
	RepoRefreshVCSData = "repo.refresh-vcs-data"
	RepoComputeStats   = "repo.compute-stats"

	RepoSettings       = "repo.settings"
	RepoSettingsUpdate = "repo.settings.update"

	RepoStats          = "repo.stats"
	RepoCombinedStatus = "repo.combined-status"

	RepoBuild = "repo.build"

	RepoCommits        = "repo.commits"
	RepoCommit         = "repo.commit"
	RepoCompareCommits = "repo.compare-commits"
	RepoTags           = "repo.tags"
	RepoBranches       = "repo.branches"

	Search         = "search"
	SearchComplete = "search.complete"

	SearchSuggestions = "search.suggestions"

	Snippet = "snippet"

	Defs          = "defs"
	Def           = "def"
	DefRefs       = "def.refs"
	DefExamples   = "def.examples"
	DefAuthors    = "def.authors"
	DefClients    = "def.clients"
	DefDependents = "def.dependents"
	DefVersions   = "def.versions"

	Delta                   = "delta"
	DeltaDefs               = "delta.defs"
	DeltaDependencies       = "delta.dependencies"
	DeltaFiles              = "delta.files"
	DeltaAffectedAuthors    = "delta.affected-authors"
	DeltaAffectedClients    = "delta.affected-clients"
	DeltaAffectedDependents = "delta.affected-dependents"
	DeltaReviewers          = "delta.reviewers"
	DeltasIncoming          = "deltas.incoming"

	Unit  = "unit"
	Units = "units"

	Markdown = "markdown"

	ExtGitHubReceiveWebhook = "ext.github.receive-webhook"

	// Redirects for old routes.
	RedirectOldRepoBadgesAndCounters = "repo.redirect-old-badges-and-counters"
)

// NewAPIRouter creates a new API router with route URL pattern definitions but
// no handlers attached to the routes.
//
// It is in a separate package from app so that other packages may use it to
// generate URLs without resulting in Go import cycles (and so we can release
// the router as open-source to support our client library).
func NewAPIRouter(base *mux.Router) *mux.Router {
	if base == nil {
		base = mux.NewRouter()
	}

	base.StrictSlash(true)

	base.Path("/builds").Methods("GET").Name(Builds)
	builds := base.PathPrefix("/builds").Subrouter()
	builds.Path("/next").Methods("POST").Name(BuildDequeueNext)
	buildPath := "/{BID}"
	builds.Path(buildPath).Methods("GET").Name(Build)
	builds.Path(buildPath).Methods("PUT").Name(BuildUpdate)
	build := builds.PathPrefix(buildPath).Subrouter()
	build.Path("/log").Methods("GET").Name(BuildLog)
	build.Path("/tasks").Methods("GET").Name(BuildTasks)
	build.Path("/tasks").Methods("POST").Name(BuildTasksCreate)
	build.Path("/tasks/{TaskID}").Methods("PUT").Name(BuildTaskUpdate)
	build.Path("/tasks/{TaskID}/log").Methods("GET").Name(BuildTaskLog)

	base.Path("/repos").Methods("GET").Name(Repos)
	base.Path("/repos").Methods("POST").Name(ReposCreate)

	base.Path("/repos/github.com/{owner:[^/]+}/{repo:[^/]+}/{what:(?:badges|counters)}/{which}.{Format}").Methods("GET").Name(RedirectOldRepoBadgesAndCounters)

	repoRev := base.PathPrefix(`/repos/` + RepoRevSpecPattern).PostMatchFunc(FixRepoRevSpecVars).BuildVarsFunc(PrepareRepoRevSpecRouteVars).Subrouter()
	repoRev.Path("/.stats").Methods("PUT").Name(RepoComputeStats)
	repoRev.Path("/.stats").Methods("GET").Name(RepoStats)
	repoRev.Path("/.status").Methods("GET").Name(RepoCombinedStatus)
	repoRev.Path("/.authors").Methods("GET").Name(RepoAuthors)
	repoRev.Path("/.readme").Methods("GET").Name(RepoReadme)
	repoRev.Path("/.build").Methods("GET").Name(RepoBuild)
	repoRev.Path("/.dependencies").Methods("GET").Name(RepoDependencies)
	repoRev.PathPrefix("/.build-data"+TreeEntryPathPattern).PostMatchFunc(FixTreeEntryVars).BuildVarsFunc(PrepareTreeEntryRouteVars).Methods("GET", "HEAD", "PUT", "DELETE").Name(RepoBuildDataEntry)
	repoRev.Path("/.badges/{Badge}.{Format}").Methods("GET").Name(RepoBadge)

	// repo contains routes that are NOT specific to a revision. In these routes, the URL may not contain a revspec after the repo (that is, no "github.com/foo/bar@myrevspec").
	repoPath := `/repos/` + RepoSpecPathPattern
	base.Path(repoPath).Methods("GET").Name(Repo)
	base.Path(repoPath).Methods("PUT").Name(ReposGetOrCreate)
	repo := base.PathPrefix(repoPath).Subrouter()
	repo.Path("/.clients").Methods("GET").Name(RepoClients)
	repo.Path("/.dependents").Methods("GET").Name(RepoDependents)
	repo.Path("/.external-profile").Methods("PUT").Name(RepoRefreshProfile)
	repo.Path("/.vcs-data").Methods("PUT").Name(RepoRefreshVCSData)
	repo.Path("/.settings").Methods("GET").Name(RepoSettings)
	repo.Path("/.settings").Methods("PUT").Name(RepoSettingsUpdate)
	repo.Path("/.commits").Methods("GET").Name(RepoCommits)
	repo.Path("/.commits/{Rev:" + PathComponentNoLeadingDot + "}/.compare").Methods("GET").Name(RepoCompareCommits)
	repo.Path("/.commits/{Rev:" + PathComponentNoLeadingDot + "}").Methods("GET").Name(RepoCommit)
	repo.Path("/.branches").Methods("GET").Name(RepoBranches)
	repo.Path("/.tags").Methods("GET").Name(RepoTags)
	repo.Path("/.badges").Methods("GET").Name(RepoBadges)
	repo.Path("/.counters").Methods("GET").Name(RepoCounters)
	repo.Path("/.counters/{Counter}.{Format}").Methods("GET").Name(RepoCounter)
	repo.Path("/.builds").Methods("GET").Name(RepoBuilds)
	repo.Path("/.builds").Methods("POST").Name(RepoBuildsCreate)

	repo.Path("/.pulls").Methods("GET").Name(RepoPullRequests)
	pullPath := "/.pulls/{Pull}"
	repo.Path(pullPath).Methods("GET").Name(RepoPullRequest)
	pull := repo.PathPrefix(pullPath).Subrouter()
	pull.Path("/merge").Methods("PUT").Name(RepoPullRequestMerge)
	pull.Path("/comments").Methods("GET").Name(RepoPullRequestComments)
	pull.Path("/comments").Methods("POST").Name(RepoPullRequestCommentsCreate)
	pull.Path("/comments/{CommentID}").Methods("PATCH", "PUT").Name(RepoPullRequestCommentsEdit)
	pull.Path("/comments/{CommentID}").Methods("DELETE").Name(RepoPullRequestCommentsDelete)

	repo.Path("/.issues").Methods("GET").Name(RepoIssues)
	issuePath := "/.issues/{Issue}"
	repo.Path(issuePath).Methods("GET").Name(RepoIssue)
	issue := repo.PathPrefix(issuePath).Subrouter()
	issue.Path("/comments").Methods("GET").Name(RepoIssueComments)
	issue.Path("/comments").Methods("POST").Name(RepoIssueCommentsCreate)
	issue.Path("/comments/{CommentID}").Methods("PATCH", "PUT").Name(RepoIssueCommentsEdit)
	issue.Path("/comments/{CommentID}").Methods("DELETE").Name(RepoIssueCommentsDelete)

	deltaPath := "/.deltas/{Rev:.+}..{DeltaHeadRev:" + PathComponentNoLeadingDot + "}"
	repo.Path(deltaPath).Methods("GET").Name(Delta)
	deltas := repo.PathPrefix(deltaPath).Subrouter()
	deltas.Path("/.defs").Methods("GET").Name(DeltaDefs)
	deltas.Path("/.dependencies").Methods("GET").Name(DeltaDependencies)
	deltas.Path("/.files").Methods("GET").Name(DeltaFiles)
	deltas.Path("/.affected-authors").Methods("GET").Name(DeltaAffectedAuthors)
	deltas.Path("/.affected-clients").Methods("GET").Name(DeltaAffectedClients)
	deltas.Path("/.affected-dependents").Methods("GET").Name(DeltaAffectedDependents)
	deltas.Path("/.reviewers").Methods("GET").Name(DeltaReviewers)

	repo.Path("/.deltas-incoming").Methods("GET").Name(DeltasIncoming)

	// See router_util/tree_route.go for an explanation of how we match tree
	// entry routes.
	repoRev.Path("/.tree" + TreeEntryPathPattern).PostMatchFunc(FixTreeEntryVars).BuildVarsFunc(PrepareTreeEntryRouteVars).Methods("GET").Name(RepoTreeEntry)

	base.Path(`/people/` + PersonSpecPattern).Methods("GET").Name(Person)

	base.Path("/users").Methods("GET").Name(Users)
	userPath := `/users/` + UserSpecPattern
	base.Path(userPath).Methods("GET").Name(User)
	user := base.PathPrefix(userPath).Subrouter()
	user.Path("/orgs").Methods("GET").Name(UserOrgs)
	user.Path("/clients").Methods("GET").Name(UserClients)
	user.Path("/authors").Methods("GET").Name(UserAuthors)
	user.Path("/emails").Methods("GET").Name(UserEmails)
	user.Path("/repo-contributions").Methods("GET").Name(UserRepoContributions)
	user.Path("/repo-dependencies").Methods("GET").Name(UserRepoDependencies)
	user.Path("/repo-dependents").Methods("GET").Name(UserRepoDependents)
	user.Path("/external-profile").Methods("PUT").Name(UserRefreshProfile)
	user.Path("/stats").Methods("PUT").Name(UserComputeStats)
	user.Path("/settings").Methods("GET").Name(UserSettings)
	user.Path("/settings").Methods("PUT").Name(UserSettingsUpdate)
	base.Path("/external-users/github/{GitHubUserSpec}").Methods("GET").Name(UserFromGitHub)

	orgPath := "/orgs/{OrgSpec}"
	base.Path(orgPath).Methods("GET").Name(Org)
	org := base.PathPrefix(orgPath).Subrouter()
	org.Path("/settings").Methods("GET").Name(OrgSettings)
	org.Path("/settings").Methods("PUT").Name(OrgSettingsUpdate)
	org.Path("/members").Methods("GET").Name(OrgMembers)

	base.Path("/search").Methods("GET").Name(Search)
	base.Path("/search/complete").Methods("GET").Name(SearchComplete)
	base.Path("/search/suggestions").Methods("GET").Name(SearchSuggestions)

	base.Path("/snippet").Methods("GET", "POST", "ORIGIN").Name(Snippet)

	base.Path("/.defs").Methods("GET").Name(Defs)

	// See router_util/def_route.go for an explanation of how we match def
	// routes.
	defPath := `/.defs/` + DefPathPattern
	repoRev.Path(defPath).Methods("GET").PostMatchFunc(FixDefUnitVars).BuildVarsFunc(PrepareDefRouteVars).Name(Def)
	def := repoRev.PathPrefix(defPath).PostMatchFunc(FixDefUnitVars).BuildVarsFunc(PrepareDefRouteVars).Subrouter()
	def.Path("/.refs").Methods("GET").Name(DefRefs)
	def.Path("/.examples").Methods("GET").Name(DefExamples)
	def.Path("/.authors").Methods("GET").Name(DefAuthors)
	def.Path("/.clients").Methods("GET").Name(DefClients)
	def.Path("/.dependents").Methods("GET").Name(DefDependents)
	def.Path("/.versions").Methods("GET").Name(DefVersions)

	base.Path("/.units").Methods("GET").Name(Units)
	unitPath := `/.units/{UnitType}/{Unit:.*}`
	repoRev.Path(unitPath).Methods("GET").Name(Unit)

	base.Path("/markdown").Methods("POST").Name(Markdown)

	base.Path("/ext/github/webhook").Methods("POST").Name(ExtGitHubReceiveWebhook)

	if ExtraConfig != nil {
		ExtraConfig(base, user)
	}

	return base
}

// ExtraConfig, if non-nil, is called by NewAPIRouter with the
// *mux.Router after setting up routes in this package and before
// returning it. It can be used by external packages that use this API
// router and want to add additional routes to it.
var ExtraConfig func(base, user *mux.Router)
<|endoftext|>


<commit_before>package example

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"

	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
)

type GreeterHandler struct {
	srv GreeterServer
}

func NewGreeterHandler(srv GreeterServer) *GreeterHandler {
	return &GreeterHandler{
		srv: srv,
	}
}

func (h *GreeterHandler) SayHello(cb func(ctx context.Context, w http.ResponseWriter, r *http.Request, arg, ret proto.Message, err error)) http.HandlerFunc {
	if cb == nil {
		cb = func(ctx context.Context, w http.ResponseWriter, r *http.Request, arg, ret proto.Message, err error) {
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				fmt.Fprintf(w, "%v: arg = %v: ret = %v", err, arg, ret)
			}
		}
	}
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()

		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			cb(ctx, w, r, nil, nil, err)
			return
		}

		arg := &HelloRequest{}

		contentType := r.Header.Get("Content-Type")
		switch contentType {
		case "application/protobuf", "application/x-protobuf":
			if err := proto.Unmarshal(body, arg); err != nil {
				cb(ctx, w, r, nil, nil, err)
				return
			}
		case "application/json":
			if err := jsonpb.Unmarshal(bytes.NewBuffer(body), arg); err != nil {
				cb(ctx, w, r, nil, nil, err)
				return
			}
		default:
			w.WriteHeader(http.StatusUnsupportedMediaType)
			_, err := fmt.Fprintf(w, "Unsupported Content-Type: %s", contentType)
			cb(ctx, w, r, nil, nil, err)
			return
		}

		ret, err := h.srv.SayHello(ctx, arg)
		if err != nil {
			cb(ctx, w, r, arg, nil, err)
			return
		}

		switch contentType {
		case "application/protobuf", "application/x-protobuf":
			buf, err := proto.Marshal(ret)
			if err != nil {
				cb(ctx, w, r, arg, ret, err)
				return
			}
			if _, err := io.Copy(w, bytes.NewBuffer(buf)); err != nil {
				cb(ctx, w, r, arg, ret, err)
				return
			}
		case "application/json":
			if err := json.NewEncoder(w).Encode(ret); err != nil {
				cb(ctx, w, r, arg, ret, err)
				return
			}
		default:
			w.WriteHeader(http.StatusUnsupportedMediaType)
			_, err := fmt.Fprintf(w, "Unsupported Content-Type: %s", contentType)
			cb(ctx, w, r, arg, ret, err)
			return
		}
		cb(ctx, w, r, arg, ret, nil)
	})
}
<commit_msg>Update example helloworld.http.go<commit_after>package example

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"

	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
)

type GreeterHandler struct {
	srv GreeterServer
}

func NewGreeterHandler(srv GreeterServer) *GreeterHandler {
	return &GreeterHandler{
		srv: srv,
	}
}

func (h *GreeterHandler) SayHello(cb func(ctx context.Context, w http.ResponseWriter, r *http.Request, arg, ret proto.Message, err error)) http.HandlerFunc {
	if cb == nil {
		cb = func(ctx context.Context, w http.ResponseWriter, r *http.Request, arg, ret proto.Message, err error) {
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				fmt.Fprintf(w, "%v: arg = %v: ret = %v", err, arg, ret)
			}
		}
	}
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()

		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			cb(ctx, w, r, nil, nil, err)
			return
		}

		arg := &HelloRequest{}

		contentType := r.Header.Get("Content-Type")
		switch contentType {
		case "application/protobuf", "application/x-protobuf":
			if err := proto.Unmarshal(body, arg); err != nil {
				cb(ctx, w, r, nil, nil, err)
				return
			}
		case "application/json":
			if err := jsonpb.Unmarshal(bytes.NewBuffer(body), arg); err != nil {
				cb(ctx, w, r, nil, nil, err)
				return
			}
		default:
			w.WriteHeader(http.StatusUnsupportedMediaType)
			_, err := fmt.Fprintf(w, "Unsupported Content-Type: %s", contentType)
			cb(ctx, w, r, nil, nil, err)
			return
		}

		ret, err := h.srv.SayHello(ctx, arg)
		if err != nil {
			cb(ctx, w, r, arg, nil, err)
			return
		}

		switch contentType {
		case "application/protobuf", "application/x-protobuf":
			buf, err := proto.Marshal(ret)
			if err != nil {
				cb(ctx, w, r, arg, ret, err)
				return
			}
			if _, err := io.Copy(w, bytes.NewBuffer(buf)); err != nil {
				cb(ctx, w, r, arg, ret, err)
				return
			}
		case "application/json":
			if err := json.NewEncoder(w).Encode(ret); err != nil {
				cb(ctx, w, r, arg, ret, err)
				return
			}
		default:
			w.WriteHeader(http.StatusUnsupportedMediaType)
			_, err := fmt.Fprintf(w, "Unsupported Content-Type: %s", contentType)
			cb(ctx, w, r, arg, ret, err)
			return
		}
		cb(ctx, w, r, arg, ret, nil)
	})
}

func (h *GreeterHandler) SayHelloWithPath(cb func(ctx context.Context, w http.ResponseWriter, r *http.Request, arg, ret proto.Message, err error)) (string, http.HandlerFunc) {
	return "greeter/sayhello", h.SayHello(cb)
}
<|endoftext|>


<commit_before>package cli

import (
	"strings"

	"github.com/go-errors/errors"
	"github.com/spf13/cobra"

	"github.com/adrienkohlbecker/ejson-kms/model"
	"github.com/adrienkohlbecker/ejson-kms/utils"
)

const docExport = `
export: Export a secrets file in it's decrypted form.

Each secret in the file will be decrypted and output to standard out.
A number of formats are available:

  * bash:   SECRET='password'
  * dotenv: SECRET="password"
  * json:   { "secret": "password" }
  * yaml:   ??????????

Please be careful when exporting your secrets, do not save them to disk!
`
const exampleExport = `
ejson-kms export
ejson-kms export --format=json
ejson-kms export --path=secrets.json --format=dotenv
`

func exportCmd() *cobra.Command {

	cmd := &cobra.Command{
		Use:     "export",
		Short:   "export the decrypted secrets",
		Long:    strings.TrimSpace(docExport),
		Example: strings.TrimSpace(exampleExport),
	}

	var (
		storePath = ".secrets.json"
		format    = "bash"
	)

	cmd.Flags().StringVar(&storePath, "path", storePath, "path of the secrets file")
	cmd.Flags().StringVar(&format, "format", format, "format of the generated output (bash|dotenv|json|yaml)")

	cmd.RunE = func(_ *cobra.Command, args []string) error {

		err := utils.ValidSecretsPath(storePath)
		if err != nil {
			return errors.WrapPrefix(err, "Invalid path", 0)
		}

		store, err := model.Load(storePath)
		if err != nil {
			return errors.WrapPrefix(err, "Unable to load JSON", 0)
		}

		formatter, err := utils.ValidFormatter(format)
		if err != nil {
			return errors.WrapPrefix(err, "Invalid formatter", 0)
		}

		client, err := kmsDefaultClient()
		if err != nil {
			return errors.WrapPrefix(err, "Unable to initialize AWS client", 0)
		}

		items, err := store.ExportPlaintext(client)
		if err != nil {
			return errors.WrapPrefix(err, "Unable to export items", 0)
		}

		err = formatter(cmd.OutOrStdout(), items)
		if err != nil {
			return errors.WrapPrefix(err, "Unable to export items", 0)
		}

		return nil
	}

	return cmd

}
<commit_msg>Fix docstring<commit_after>package cli

import (
	"strings"

	"github.com/go-errors/errors"
	"github.com/spf13/cobra"

	"github.com/adrienkohlbecker/ejson-kms/model"
	"github.com/adrienkohlbecker/ejson-kms/utils"
)

const docExport = `
export: Export a secrets file in it's decrypted form.

Each secret in the file will be decrypted and output to standard out.
A number of formats are available:

  * bash:   SECRET='password'
  * dotenv: SECRET="password"
  * json:   { "secret": "password" }
  * yaml:   secret: password

Please be careful when exporting your secrets, do not save them to disk!
`
const exampleExport = `
ejson-kms export
ejson-kms export --format=json
ejson-kms export --path=secrets.json --format=dotenv
`

func exportCmd() *cobra.Command {

	cmd := &cobra.Command{
		Use:     "export",
		Short:   "export the decrypted secrets",
		Long:    strings.TrimSpace(docExport),
		Example: strings.TrimSpace(exampleExport),
	}

	var (
		storePath = ".secrets.json"
		format    = "bash"
	)

	cmd.Flags().StringVar(&storePath, "path", storePath, "path of the secrets file")
	cmd.Flags().StringVar(&format, "format", format, "format of the generated output (bash|dotenv|json|yaml)")

	cmd.RunE = func(_ *cobra.Command, args []string) error {

		err := utils.ValidSecretsPath(storePath)
		if err != nil {
			return errors.WrapPrefix(err, "Invalid path", 0)
		}

		store, err := model.Load(storePath)
		if err != nil {
			return errors.WrapPrefix(err, "Unable to load JSON", 0)
		}

		formatter, err := utils.ValidFormatter(format)
		if err != nil {
			return errors.WrapPrefix(err, "Invalid formatter", 0)
		}

		client, err := kmsDefaultClient()
		if err != nil {
			return errors.WrapPrefix(err, "Unable to initialize AWS client", 0)
		}

		items, err := store.ExportPlaintext(client)
		if err != nil {
			return errors.WrapPrefix(err, "Unable to export items", 0)
		}

		err = formatter(cmd.OutOrStdout(), items)
		if err != nil {
			return errors.WrapPrefix(err, "Unable to export items", 0)
		}

		return nil
	}

	return cmd

}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"encoding/hex"
	"flag"
	"fmt"
	"hash/crc32"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"

	"github.com/tarm/serial"
	"gopkg.in/readline.v1"
)

var (
	rlInstance *readline.Instance
	conn       io.ReadWriter
	serIn      = make(chan []byte)
	outBound   = make(chan string)
	progress   = make(chan bool, 1)
	incLevel   = make(chan int)

	port    = flag.String("p", "", "serial port (required: /dev/tty* or COM*)")
	exe     = flag.Bool("x", false, "executable (consumes all remaining args)")
	baud    = flag.Int("b", 115200, "baud rate")
	upload  = flag.String("u", "", "upload the specified firmware, then quit")
	expand  = flag.String("e", "", "expand specified file to stdout, then quit")
	verbose = flag.Bool("v", false, "verbose output, for debugging only")
	capture = flag.String("c", "", "a file where captured output is appended")
	timeout = flag.Duration("t", 500*time.Millisecond, "serial echo timeout")
)

func main() {
	flag.Parse()
	var err error

	// expansion does not use the serial port, it just expands include lines
	if *expand != "" {
		expandFile()
		return
	}

	if *exe && flag.NArg() > 0 {
		conn, err = launch(flag.Args())
		*port = "(" + strings.Join(flag.Args(), " ") + ")"
	} else if *port == "" || flag.NArg() > 0 {
		flag.PrintDefaults()
		os.Exit(1)
		conn, err = connect(*port)
	}
	check(err)
	//defer conn.Close()
	fmt.Println("Connected to:", *port)

	go serialInput() // feed the serIn channel

	// firmware upload uses serial in a different way, needs to quit when done
	if *upload != "" {
		firmwareUpload()
		return
	}

	rlInstance, err = readline.NewEx(&readline.Config{
		UniqueEditLine: true,
	})
	check(err)
	defer rlInstance.Close()

	go serialExchange()

	//conn.Flush()
	readWithTimeout() // get rid of partial pending data
	for {
		line, err := rlInstance.Readline()
		if err != nil { // io.EOF, readline.ErrInterrupt
			break
		}
		parseAndSend(line)
		if strings.HasPrefix(line, "include ") {
			fmt.Println("\\ done.")
		}
	}
}

// isNetPort returns true if the argument is of the form "...:<N>"
func isNetPort(s string) bool {
	if n := strings.Index(s, ":"); n > 0 {
		p, e := strconv.Atoi(s[n+1:])
		return e == nil && p > 0
	}
	return false
}

// connect to either a net port for telnet use, or to a serial device
func connect(name string) (io.ReadWriter, error) {
	if isNetPort(name) {
		return net.Dial("tcp", name)
	}

	config := serial.Config{Name: *port, Baud: *baud}
	if *upload != "" {
		config.Parity = serial.ParityEven
	}
	return serial.OpenPort(&config)
}

// launch an executable and talk to it via pipes
func launch(args []string) (io.ReadWriter, error) {
	cmd := exec.Command(args[0], args[1:]...)
	var cmdio exePipe
	var err error
	cmdio.to, err = cmd.StdinPipe()
	if err == nil {
		cmdio.from, err = cmd.StdoutPipe()
		if err == nil {
			err = cmd.Start()
		}
	}
	return &cmdio, err
}

type exePipe struct {
	from io.Reader
	to   io.Writer
}

func (e *exePipe) Read(p []byte) (n int, err error) {
	return e.from.Read(p)
}

func (e *exePipe) Write(p []byte) (n int, err error) {
	return e.to.Write(p)
}

func parseAndSend(line string) {
	if strings.HasPrefix(line, "include ") {
		for _, fname := range strings.Split(line[8:], " ") {
			doInclude(fname)
		}
	} else {
		outBound <- line
		<-progress
	}
}

func check(err error) {
	if err != nil {
		if rlInstance != nil {
			rlInstance.Close()
		}
		log.Fatal(err)
	}
}

func serialInput() {
	var f *os.File
	if *capture != "" {
		var err error
		opts := os.O_WRONLY | os.O_APPEND | os.O_CREATE
		f, err = os.OpenFile(*capture, opts, 0666)
		check(err)
		defer f.Close()
	}
	for {
		buf := make([]byte, 600)
		n, err := conn.Read(buf)
		check(err)
		if n == 0 {
			close(serIn)
			return
		}
		if f != nil {
			f.Write(buf[:n])
		}
		serIn <- buf[:n]
	}
}

func readWithTimeout() []byte {
	select {
	case data := <-serIn:
		return data
	case <-time.After(*timeout):
		return nil
	}
}

func serialExchange() {
	depth := 0
	for {
		select {

		case data := <-serIn:
			if len(data) == 0 {
				return
			}
			fmt.Print(string(data))

		case line := <-outBound:
			immediate := depth == 0
			// the task here is to omit "normal" output for included lines,
			// i.e. lines which only generate an echo, a space, and " ok.\n"
			// everything else should be echoed in full, including the input
			if len(line) > 0 {
				serialSend(line)
				prefix, matched := expectEcho(line, false, func(s string) {
					fmt.Print(s) // called to flush pending serial input lines
				})
				fmt.Print(prefix)
				if matched && immediate {
					fmt.Print(line)
					line = ""
				}
			}
			// now that the echo is done, send a CR and wait for the prompt
			serialSend("\r")
			prompt := " ok.\n"
			prefix, matched := expectEcho(prompt, immediate, func(s string) {
				fmt.Print(line + s) // show original command first
				line = ""
			})
			if !matched {
				prompt = ""
			}
			if immediate || prefix != " " || !matched {
				fmt.Print(line + prefix + prompt)
			}
			// signal to sender that this request has been processed
			progress <- matched

		case n := <-incLevel:
			depth += n
		}
	}
}

func expectEcho(match string, immed bool, flusher func(string)) (string, bool) {
	var collected []byte
	for {
		data := readWithTimeout()
		collected = append(collected, data...)
		if bytes.HasSuffix(collected, []byte(match)) {
			bytesBefore := len(collected) - len(match)
			return string(collected[:bytesBefore]), true
		}
		if immed || len(data) == 0 {
			return string(collected), false
		}
		if n := bytes.LastIndexByte(collected, '\n'); n >= 0 {
			flusher(string(collected[:n+1]))
			collected = collected[n+1:]
		}
	}
}

func serialSend(data string) {
	_, err := conn.Write([]byte(data))
	check(err)
}

func doInclude(fname string) {
	if fname == "" {
		return // silently ignore empty files
	}

	incLevel <- +1
	defer func() { incLevel <- -1 }()

	lineNum := 0
	fmt.Printf("\\       >>> include %s\n", fname)
	defer func() {
		fmt.Printf("\\       <<<<<<<<<<< %s (%d lines)\n", fname, lineNum)
	}()

	f, err := os.Open(fname)
	if err != nil {
		fmt.Println(err)
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		line := scanner.Text()
		lineNum++

		s := strings.TrimLeft(line, " ")
		if s == "" || strings.HasPrefix(s, "\\ ") {
			if len(*expand) == 0 {
				continue // don't send empty or comment-only lines
			}
		}

		parseAndSend(line)
	}
}

func expandFile() {
	go func() {
		for line := range outBound {
			fmt.Println(line)
			progress <- true
		}
	}()

	go func() {
		for range incLevel {
		}
	}()

	for _, fname := range strings.Split(*expand, ",") {
		doInclude(fname)
	}
}

func hexToBin(data []byte) []byte {
	var bin []byte
	for _, line := range strings.Split(string(data), "\n") {
		if strings.HasSuffix(line, "\r") {
			line = line[:len(line)-1]
		}
		if len(line) == 0 {
			continue
		}
		if line[0] != ':' || len(line) < 11 {
			fmt.Println("Not ihex format:", line)
			os.Exit(1)
		}
		bytes, err := hex.DecodeString(line[1:])
		check(err)
		if bytes[3] != 0x00 {
			continue
		}
		offset := (int(bytes[1]) << 8) + int(bytes[2])
		length := bytes[0]
		for offset > len(bin) {
			bin = append(bin, 0xFF)
		}
		bin = append(bin, bytes[4:4+length]...)
	}
	return bin
}

func firmwareUpload() {
	f, err := os.Open(*upload)
	if err != nil {
		fmt.Println(err)
	}
	defer f.Close()

	data, err := ioutil.ReadAll(f)
	check(err)

	// convert to binary if first bytes look like they are in hex format
	tag := ""
	if len(data) > 11 && data[0] == ':' {
		_, err = hex.DecodeString(string(data[1:11]))
		if err == nil {
			data = hexToBin(data)
			tag = " (converted from Intel HEX)"
		}
	}

	fmt.Printf("        File: %s\n", *upload)
	fmt.Printf("       Count: %d bytes%s\n", len(data), tag)
	fmt.Printf("    Checksum: %08x hex\n", crc32.ChecksumIEEE(data))

	uploadSTM32(data)
}
<commit_msg>oops, fix last change, which broke serial port use<commit_after>package main

import (
	"bufio"
	"bytes"
	"encoding/hex"
	"flag"
	"fmt"
	"hash/crc32"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"

	"github.com/tarm/serial"
	"gopkg.in/readline.v1"
)

var (
	rlInstance *readline.Instance
	conn       io.ReadWriter
	serIn      = make(chan []byte)
	outBound   = make(chan string)
	progress   = make(chan bool, 1)
	incLevel   = make(chan int)

	port    = flag.String("p", "", "serial port (required: /dev/tty* or COM*)")
	exe     = flag.Bool("x", false, "executable (consumes all remaining args)")
	baud    = flag.Int("b", 115200, "baud rate")
	upload  = flag.String("u", "", "upload the specified firmware, then quit")
	expand  = flag.String("e", "", "expand specified file to stdout, then quit")
	verbose = flag.Bool("v", false, "verbose output, for debugging only")
	capture = flag.String("c", "", "a file where captured output is appended")
	timeout = flag.Duration("t", 500*time.Millisecond, "serial echo timeout")
)

func main() {
	flag.Parse()
	var err error

	// expansion does not use the serial port, it just expands include lines
	if *expand != "" {
		expandFile()
		return
	}

	if *exe && flag.NArg() > 0 {
		conn, err = launch(flag.Args())
		*port = "(" + strings.Join(flag.Args(), " ") + ")"
	} else if *port != "" && flag.NArg() == 0 {
		conn, err = connect(*port)
	} else {
		flag.PrintDefaults()
		os.Exit(1)
	}
	check(err)
	//defer conn.Close()
	fmt.Println("Connected to:", *port)

	go serialInput() // feed the serIn channel

	// firmware upload uses serial in a different way, needs to quit when done
	if *upload != "" {
		firmwareUpload()
		return
	}

	rlInstance, err = readline.NewEx(&readline.Config{
		UniqueEditLine: true,
	})
	check(err)
	defer rlInstance.Close()

	go serialExchange()

	//conn.Flush()
	readWithTimeout() // get rid of partial pending data
	for {
		line, err := rlInstance.Readline()
		if err != nil { // io.EOF, readline.ErrInterrupt
			break
		}
		parseAndSend(line)
		if strings.HasPrefix(line, "include ") {
			fmt.Println("\\ done.")
		}
	}
}

// isNetPort returns true if the argument is of the form "...:<N>"
func isNetPort(s string) bool {
	if n := strings.Index(s, ":"); n > 0 {
		p, e := strconv.Atoi(s[n+1:])
		return e == nil && p > 0
	}
	return false
}

// connect to either a net port for telnet use, or to a serial device
func connect(name string) (io.ReadWriter, error) {
	if isNetPort(name) {
		return net.Dial("tcp", name)
	}

	config := serial.Config{Name: *port, Baud: *baud}
	if *upload != "" {
		config.Parity = serial.ParityEven
	}
	return serial.OpenPort(&config)
}

// launch an executable and talk to it via pipes
func launch(args []string) (io.ReadWriter, error) {
	cmd := exec.Command(args[0], args[1:]...)
	var cmdio exePipe
	var err error
	cmdio.to, err = cmd.StdinPipe()
	if err == nil {
		cmdio.from, err = cmd.StdoutPipe()
		if err == nil {
			err = cmd.Start()
		}
	}
	return &cmdio, err
}

type exePipe struct {
	from io.Reader
	to   io.Writer
}

func (e *exePipe) Read(p []byte) (n int, err error) {
	return e.from.Read(p)
}

func (e *exePipe) Write(p []byte) (n int, err error) {
	return e.to.Write(p)
}

func parseAndSend(line string) {
	if strings.HasPrefix(line, "include ") {
		for _, fname := range strings.Split(line[8:], " ") {
			doInclude(fname)
		}
	} else {
		outBound <- line
		<-progress
	}
}

func check(err error) {
	if err != nil {
		if rlInstance != nil {
			rlInstance.Close()
		}
		log.Fatal(err)
	}
}

func serialInput() {
	var f *os.File
	if *capture != "" {
		var err error
		opts := os.O_WRONLY | os.O_APPEND | os.O_CREATE
		f, err = os.OpenFile(*capture, opts, 0666)
		check(err)
		defer f.Close()
	}
	for {
		buf := make([]byte, 600)
		n, err := conn.Read(buf)
		check(err)
		if n == 0 {
			close(serIn)
			return
		}
		if f != nil {
			f.Write(buf[:n])
		}
		serIn <- buf[:n]
	}
}

func readWithTimeout() []byte {
	select {
	case data := <-serIn:
		return data
	case <-time.After(*timeout):
		return nil
	}
}

func serialExchange() {
	depth := 0
	for {
		select {

		case data := <-serIn:
			if len(data) == 0 {
				return
			}
			fmt.Print(string(data))

		case line := <-outBound:
			immediate := depth == 0
			// the task here is to omit "normal" output for included lines,
			// i.e. lines which only generate an echo, a space, and " ok.\n"
			// everything else should be echoed in full, including the input
			if len(line) > 0 {
				serialSend(line)
				prefix, matched := expectEcho(line, false, func(s string) {
					fmt.Print(s) // called to flush pending serial input lines
				})
				fmt.Print(prefix)
				if matched && immediate {
					fmt.Print(line)
					line = ""
				}
			}
			// now that the echo is done, send a CR and wait for the prompt
			serialSend("\r")
			prompt := " ok.\n"
			prefix, matched := expectEcho(prompt, immediate, func(s string) {
				fmt.Print(line + s) // show original command first
				line = ""
			})
			if !matched {
				prompt = ""
			}
			if immediate || prefix != " " || !matched {
				fmt.Print(line + prefix + prompt)
			}
			// signal to sender that this request has been processed
			progress <- matched

		case n := <-incLevel:
			depth += n
		}
	}
}

func expectEcho(match string, immed bool, flusher func(string)) (string, bool) {
	var collected []byte
	for {
		data := readWithTimeout()
		collected = append(collected, data...)
		if bytes.HasSuffix(collected, []byte(match)) {
			bytesBefore := len(collected) - len(match)
			return string(collected[:bytesBefore]), true
		}
		if immed || len(data) == 0 {
			return string(collected), false
		}
		if n := bytes.LastIndexByte(collected, '\n'); n >= 0 {
			flusher(string(collected[:n+1]))
			collected = collected[n+1:]
		}
	}
}

func serialSend(data string) {
	_, err := conn.Write([]byte(data))
	check(err)
}

func doInclude(fname string) {
	if fname == "" {
		return // silently ignore empty files
	}

	incLevel <- +1
	defer func() { incLevel <- -1 }()

	lineNum := 0
	fmt.Printf("\\       >>> include %s\n", fname)
	defer func() {
		fmt.Printf("\\       <<<<<<<<<<< %s (%d lines)\n", fname, lineNum)
	}()

	f, err := os.Open(fname)
	if err != nil {
		fmt.Println(err)
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		line := scanner.Text()
		lineNum++

		s := strings.TrimLeft(line, " ")
		if s == "" || strings.HasPrefix(s, "\\ ") {
			if len(*expand) == 0 {
				continue // don't send empty or comment-only lines
			}
		}

		parseAndSend(line)
	}
}

func expandFile() {
	go func() {
		for line := range outBound {
			fmt.Println(line)
			progress <- true
		}
	}()

	go func() {
		for range incLevel {
		}
	}()

	for _, fname := range strings.Split(*expand, ",") {
		doInclude(fname)
	}
}

func hexToBin(data []byte) []byte {
	var bin []byte
	for _, line := range strings.Split(string(data), "\n") {
		if strings.HasSuffix(line, "\r") {
			line = line[:len(line)-1]
		}
		if len(line) == 0 {
			continue
		}
		if line[0] != ':' || len(line) < 11 {
			fmt.Println("Not ihex format:", line)
			os.Exit(1)
		}
		bytes, err := hex.DecodeString(line[1:])
		check(err)
		if bytes[3] != 0x00 {
			continue
		}
		offset := (int(bytes[1]) << 8) + int(bytes[2])
		length := bytes[0]
		for offset > len(bin) {
			bin = append(bin, 0xFF)
		}
		bin = append(bin, bytes[4:4+length]...)
	}
	return bin
}

func firmwareUpload() {
	f, err := os.Open(*upload)
	if err != nil {
		fmt.Println(err)
	}
	defer f.Close()

	data, err := ioutil.ReadAll(f)
	check(err)

	// convert to binary if first bytes look like they are in hex format
	tag := ""
	if len(data) > 11 && data[0] == ':' {
		_, err = hex.DecodeString(string(data[1:11]))
		if err == nil {
			data = hexToBin(data)
			tag = " (converted from Intel HEX)"
		}
	}

	fmt.Printf("        File: %s\n", *upload)
	fmt.Printf("       Count: %d bytes%s\n", len(data), tag)
	fmt.Printf("    Checksum: %08x hex\n", crc32.ChecksumIEEE(data))

	uploadSTM32(data)
}
<|endoftext|>


<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package lang

import (
	"context"
	"log"

	"os"

	"encoding/json"

	"github.com/getgauge/common"
	"github.com/getgauge/gauge/api/infoGatherer"
	"github.com/getgauge/gauge/gauge"
	gm "github.com/getgauge/gauge/gauge_messages"
	"github.com/getgauge/gauge/logger"
	"github.com/getgauge/gauge/util"
	"github.com/op/go-logging"
	"github.com/sourcegraph/go-langserver/pkg/lsp"
	"github.com/sourcegraph/jsonrpc2"
)

type server struct{}

type infoProvider interface {
	Init()
	Steps() []*gauge.Step
	AllSteps() []*gauge.Step
	Concepts() []*gm.ConceptInfo
	Params(file string, argType gauge.ArgType) []gauge.StepArg
	Tags() []string
	SearchConceptDictionary(string) *gauge.Concept
	GetAvailableSpecDetails(specs []string) []*infoGatherer.SpecDetail
}

var provider infoProvider

func Server(p infoProvider) *server {
	provider = p
	provider.Init()
	startRunner()
	return &server{}
}

type lspHandler struct {
	jsonrpc2.Handler
}

type LangHandler struct {
}

type registrationParams struct {
	Registrations []registration `json:"registrations"`
}

type registration struct {
	Id              string      `json:"id"`
	Method          string      `json:"method"`
	RegisterOptions interface{} `json:"registerOptions"`
}

type textDocumentRegistrationOptions struct {
	DocumentSelector documentSelector `json:"documentSelector"`
}

type textDocumentChangeRegistrationOptions struct {
	textDocumentRegistrationOptions
	SyncKind lsp.TextDocumentSyncKind `json:"syncKind,omitempty"`
}

type codeLensRegistrationOptions struct {
	textDocumentRegistrationOptions
	ResolveProvider bool `json:"resolveProvider,omitempty"`
}

type documentSelector struct {
	Language string `json:"language"`
}

func newHandler() jsonrpc2.Handler {
	return lspHandler{jsonrpc2.HandlerWithError((&LangHandler{}).handle)}
}

func (h lspHandler) Handle(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) {
	go h.Handler.Handle(ctx, conn, req)
}

func (h *LangHandler) handle(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) (interface{}, error) {
	return h.Handle(ctx, conn, req)
}

func (h *LangHandler) Handle(ctx context.Context, conn jsonrpc2.JSONRPC2, req *jsonrpc2.Request) (interface{}, error) {
	switch req.Method {
	case "initialize":
		return gaugeLSPCapabilities(), nil
	case "initialized":
		registerRunnerCapabilities(conn, ctx)
		go publishDiagnostics(ctx, conn)
		return nil, nil
	case "shutdown":
		killRunner()
		return nil, nil
	case "exit":
		if c, ok := conn.(*jsonrpc2.Conn); ok {
			c.Close()
			os.Exit(0)
		}
		return nil, nil
	case "$/cancelRequest":
		return nil, nil
	case "textDocument/didOpen":
		return nil, documentOpened(req, ctx, conn)
	case "textDocument/didClose":
		return nil, documentClosed(req, ctx, conn)
	case "textDocument/didChange":
		return nil, documentChange(req, ctx, conn)
	case "textDocument/completion":
		return completion(req)
	case "completionItem/resolve":
		return resolveCompletion(req)
	case "textDocument/definition":
		return definition(req)
	case "textDocument/formatting":
		data, err := format(req)
		if err != nil {
			conn.Notify(ctx, "window/showMessage", lsp.ShowMessageParams{Type: 1, Message: err.Error()})
		}
		return data, err
	case "textDocument/codeLens":
		return codeLenses(req)
	case "textDocument/codeAction":
		return codeActions(req)
	case "textDocument/documentSymbol":
		return documentSymbols(req)
	case "workspace/symbol":
		return workspaceSymbols(req)
	case "gauge/stepReferences":
		return stepReferences(req)
	case "gauge/stepValueAt":
		return stepValueAt(req)
	case "gauge/scenarios":
		return scenarios(req)
	case "gauge/specs":
		return specs()
	default:
		return nil, nil
	}
}

func gaugeLSPCapabilities() lsp.InitializeResult {
	kind := lsp.TDSKFull
	return lsp.InitializeResult{
		Capabilities: lsp.ServerCapabilities{
			TextDocumentSync:           lsp.TextDocumentSyncOptionsOrKind{Kind: &kind, Options: &lsp.TextDocumentSyncOptions{Save: &lsp.SaveOptions{IncludeText: true}}},
			CompletionProvider:         &lsp.CompletionOptions{ResolveProvider: true, TriggerCharacters: []string{"*", "* ", "\"", "<", ":", ","}},
			DocumentFormattingProvider: true,
			CodeLensProvider:           &lsp.CodeLensOptions{ResolveProvider: false},
			DefinitionProvider:         true,
			CodeActionProvider:         true,
			DocumentSymbolProvider:     true,
			WorkspaceSymbolProvider:    true,
		},
	}
}

func documentOpened(req *jsonrpc2.Request, ctx context.Context, conn jsonrpc2.JSONRPC2) error {
	var params lsp.DidOpenTextDocumentParams
	var err error
	if err = json.Unmarshal(*req.Params, &params); err != nil {
		logger.APILog.Debugf("failed to parse request %s", err.Error())
		return err
	}
	if util.IsGaugeFile(params.TextDocument.URI) {
		openFile(params)
	} else if lRunner.runner != nil {
		err = cacheFileOnRunner(params.TextDocument.URI, params.TextDocument.Text)
	}
	go publishDiagnostics(ctx, conn)
	return err
}

func documentChange(req *jsonrpc2.Request, ctx context.Context, conn jsonrpc2.JSONRPC2) error {
	var params lsp.DidChangeTextDocumentParams
	var err error
	if err = json.Unmarshal(*req.Params, &params); err != nil {
		logger.APILog.Debugf("failed to parse request %s", err.Error())
		return err
	}
	if util.IsGaugeFile(params.TextDocument.URI) {
		changeFile(params)
	} else if lRunner.runner != nil {
		err = cacheFileOnRunner(params.TextDocument.URI, params.ContentChanges[0].Text)
	}
	go publishDiagnostics(ctx, conn)
	return err
}

func documentClosed(req *jsonrpc2.Request, ctx context.Context, conn jsonrpc2.JSONRPC2) error {
	var params lsp.DidCloseTextDocumentParams
	var err error
	if err := json.Unmarshal(*req.Params, &params); err != nil {
		logger.APILog.Debugf("failed to parse request %s", err.Error())
		return err
	}
	if util.IsGaugeFile(params.TextDocument.URI) {
		closeFile(params)
		if !common.FileExists(util.ConvertPathToURI(params.TextDocument.URI)) {
			publishDiagnostic(params.TextDocument.URI, []lsp.Diagnostic{}, conn, ctx)
		}
	} else if lRunner.runner != nil {
		cacheFileRequest := &gm.Message{MessageType: gm.Message_CacheFileRequest, CacheFileRequest: &gm.CacheFileRequest{FilePath: util.ConvertURItoFilePath(params.TextDocument.URI), IsClosed: true}}
		err = sendMessageToRunner(cacheFileRequest)
	}
	go publishDiagnostics(ctx, conn)
	return err
}

func registerRunnerCapabilities(conn jsonrpc2.JSONRPC2, ctx context.Context) {
	var result string
	// TODO : fetch the language dynamically
	conn.Call(ctx, "client/registerCapability", registrationParams{[]registration{
		{Id: "gauge-runner-didOpen", Method: "textDocument/didOpen", RegisterOptions: textDocumentRegistrationOptions{DocumentSelector: documentSelector{Language: "javascript"}}},
		{Id: "gauge-runner-didClose", Method: "textDocument/didClose", RegisterOptions: textDocumentRegistrationOptions{DocumentSelector: documentSelector{Language: "javascript"}}},
		{Id: "gauge-runner-didChange", Method: "textDocument/didChange", RegisterOptions: textDocumentChangeRegistrationOptions{textDocumentRegistrationOptions: textDocumentRegistrationOptions{DocumentSelector: documentSelector{Language: "javascript"}}, SyncKind: lsp.TDSKFull}},
		{Id: "gauge-runner-codelens", Method: "textDocument/codeLens", RegisterOptions: codeLensRegistrationOptions{textDocumentRegistrationOptions: textDocumentRegistrationOptions{DocumentSelector: documentSelector{Language: "javascript"}}, ResolveProvider: false}},
	}}, result)
}

type lspWriter struct {
}

func (w lspWriter) Write(p []byte) (n int, err error) {
	logger.LspLog.Debug(string(p))
	return os.Stderr.Write(p)
}

func (s *server) Start(logLevel string) {
	logger.APILog.Info("LangServer: reading on stdin, writing on stdout")
	var connOpt []jsonrpc2.ConnOpt
	if logLevel == "debug" {
		connOpt = append(connOpt, jsonrpc2.LogMessages(log.New(lspWriter{}, "", 0)))
	}
	ctx := context.Background()
	conn := jsonrpc2.NewConn(ctx, jsonrpc2.NewBufferedStream(stdRWC{}, jsonrpc2.VSCodeObjectCodec{}), newHandler(), connOpt...)
	logger.SetCustomLogger(lspLogger{conn, ctx})
	<-conn.DisconnectNotify()
	logger.APILog.Info("Connection closed")
}

type stdRWC struct{}

func (stdRWC) Read(p []byte) (int, error) {
	return os.Stdin.Read(p)
}

func (stdRWC) Write(p []byte) (int, error) {
	return os.Stdout.Write(p)
}

func (stdRWC) Close() error {
	if err := os.Stdin.Close(); err != nil {
		return err
	}
	return os.Stdout.Close()
}

type lspLogger struct {
	conn *jsonrpc2.Conn
	ctx  context.Context
}

func (c lspLogger) Log(logLevel logging.Level, msg string) {
	logger.APILog.Info(logLevel)
	var level lsp.MessageType
	switch logLevel {
	case logging.DEBUG:
		level = lsp.Log
	case logging.INFO:
		level = lsp.Info
	case logging.WARNING:
		level = lsp.MTWarning
	case logging.ERROR:
		level = lsp.MTError
	case logging.CRITICAL:
		level = lsp.MTError
	default:
		level = lsp.Info
	}
	c.conn.Notify(c.ctx, "window/logMessage", lsp.LogMessageParams{Type: level, Message: msg})
}
<commit_msg>setting fileType and pattern while initilizing runner capabilities<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package lang

import (
	"context"
	"fmt"
	"log"

	"os"

	"encoding/json"

	"github.com/getgauge/common"
	"github.com/getgauge/gauge/api/infoGatherer"
	"github.com/getgauge/gauge/config"
	"github.com/getgauge/gauge/gauge"
	gm "github.com/getgauge/gauge/gauge_messages"
	"github.com/getgauge/gauge/logger"
	"github.com/getgauge/gauge/util"
	"github.com/op/go-logging"
	"github.com/sourcegraph/go-langserver/pkg/lsp"
	"github.com/sourcegraph/jsonrpc2"
)

type server struct{}

type infoProvider interface {
	Init()
	Steps() []*gauge.Step
	AllSteps() []*gauge.Step
	Concepts() []*gm.ConceptInfo
	Params(file string, argType gauge.ArgType) []gauge.StepArg
	Tags() []string
	SearchConceptDictionary(string) *gauge.Concept
	GetAvailableSpecDetails(specs []string) []*infoGatherer.SpecDetail
}

var provider infoProvider

func Server(p infoProvider) *server {
	provider = p
	provider.Init()
	startRunner()
	return &server{}
}

type lspHandler struct {
	jsonrpc2.Handler
}

type LangHandler struct {
}

type registrationParams struct {
	Registrations []registration `json:"registrations"`
}

type registration struct {
	Id              string      `json:"id"`
	Method          string      `json:"method"`
	RegisterOptions interface{} `json:"registerOptions"`
}

type textDocumentRegistrationOptions struct {
	DocumentSelector documentSelector `json:"documentSelector"`
}

type textDocumentChangeRegistrationOptions struct {
	textDocumentRegistrationOptions
	SyncKind lsp.TextDocumentSyncKind `json:"syncKind,omitempty"`
}

type codeLensRegistrationOptions struct {
	textDocumentRegistrationOptions
	ResolveProvider bool `json:"resolveProvider,omitempty"`
}

type documentSelector struct {
	Scheme   string `json:"scheme"`
	Language string `json:"language"`
	Pattern  string `json:"pattern"`
}

func newHandler() jsonrpc2.Handler {
	return lspHandler{jsonrpc2.HandlerWithError((&LangHandler{}).handle)}
}

func (h lspHandler) Handle(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) {
	go h.Handler.Handle(ctx, conn, req)
}

func (h *LangHandler) handle(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) (interface{}, error) {
	return h.Handle(ctx, conn, req)
}

func (h *LangHandler) Handle(ctx context.Context, conn jsonrpc2.JSONRPC2, req *jsonrpc2.Request) (interface{}, error) {
	switch req.Method {
	case "initialize":
		return gaugeLSPCapabilities(), nil
	case "initialized":
		registerRunnerCapabilities(conn, ctx)
		go publishDiagnostics(ctx, conn)
		return nil, nil
	case "shutdown":
		killRunner()
		return nil, nil
	case "exit":
		if c, ok := conn.(*jsonrpc2.Conn); ok {
			c.Close()
			os.Exit(0)
		}
		return nil, nil
	case "$/cancelRequest":
		return nil, nil
	case "textDocument/didOpen":
		return nil, documentOpened(req, ctx, conn)
	case "textDocument/didClose":
		return nil, documentClosed(req, ctx, conn)
	case "textDocument/didChange":
		return nil, documentChange(req, ctx, conn)
	case "textDocument/completion":
		return completion(req)
	case "completionItem/resolve":
		return resolveCompletion(req)
	case "textDocument/definition":
		return definition(req)
	case "textDocument/formatting":
		data, err := format(req)
		if err != nil {
			conn.Notify(ctx, "window/showMessage", lsp.ShowMessageParams{Type: 1, Message: err.Error()})
		}
		return data, err
	case "textDocument/codeLens":
		return codeLenses(req)
	case "textDocument/codeAction":
		return codeActions(req)
	case "textDocument/documentSymbol":
		return documentSymbols(req)
	case "workspace/symbol":
		return workspaceSymbols(req)
	case "gauge/stepReferences":
		return stepReferences(req)
	case "gauge/stepValueAt":
		return stepValueAt(req)
	case "gauge/scenarios":
		return scenarios(req)
	case "gauge/specs":
		return specs()
	default:
		return nil, nil
	}
}

func gaugeLSPCapabilities() lsp.InitializeResult {
	kind := lsp.TDSKFull
	return lsp.InitializeResult{
		Capabilities: lsp.ServerCapabilities{
			TextDocumentSync:           lsp.TextDocumentSyncOptionsOrKind{Kind: &kind, Options: &lsp.TextDocumentSyncOptions{Save: &lsp.SaveOptions{IncludeText: true}}},
			CompletionProvider:         &lsp.CompletionOptions{ResolveProvider: true, TriggerCharacters: []string{"*", "* ", "\"", "<", ":", ","}},
			DocumentFormattingProvider: true,
			CodeLensProvider:           &lsp.CodeLensOptions{ResolveProvider: false},
			DefinitionProvider:         true,
			CodeActionProvider:         true,
			DocumentSymbolProvider:     true,
			WorkspaceSymbolProvider:    true,
		},
	}
}

func documentOpened(req *jsonrpc2.Request, ctx context.Context, conn jsonrpc2.JSONRPC2) error {
	var params lsp.DidOpenTextDocumentParams
	var err error
	if err = json.Unmarshal(*req.Params, &params); err != nil {
		logger.APILog.Debugf("failed to parse request %s", err.Error())
		return err
	}
	if util.IsGaugeFile(params.TextDocument.URI) {
		openFile(params)
	} else if lRunner.runner != nil {
		err = cacheFileOnRunner(params.TextDocument.URI, params.TextDocument.Text)
	}
	go publishDiagnostics(ctx, conn)
	return err
}

func documentChange(req *jsonrpc2.Request, ctx context.Context, conn jsonrpc2.JSONRPC2) error {
	var params lsp.DidChangeTextDocumentParams
	var err error
	if err = json.Unmarshal(*req.Params, &params); err != nil {
		logger.APILog.Debugf("failed to parse request %s", err.Error())
		return err
	}
	if util.IsGaugeFile(params.TextDocument.URI) {
		changeFile(params)
	} else if lRunner.runner != nil {
		err = cacheFileOnRunner(params.TextDocument.URI, params.ContentChanges[0].Text)
	}
	go publishDiagnostics(ctx, conn)
	return err
}

func documentClosed(req *jsonrpc2.Request, ctx context.Context, conn jsonrpc2.JSONRPC2) error {
	var params lsp.DidCloseTextDocumentParams
	var err error
	if err := json.Unmarshal(*req.Params, &params); err != nil {
		logger.APILog.Debugf("failed to parse request %s", err.Error())
		return err
	}
	if util.IsGaugeFile(params.TextDocument.URI) {
		closeFile(params)
		if !common.FileExists(util.ConvertPathToURI(params.TextDocument.URI)) {
			publishDiagnostic(params.TextDocument.URI, []lsp.Diagnostic{}, conn, ctx)
		}
	} else if lRunner.runner != nil {
		cacheFileRequest := &gm.Message{MessageType: gm.Message_CacheFileRequest, CacheFileRequest: &gm.CacheFileRequest{FilePath: util.ConvertURItoFilePath(params.TextDocument.URI), IsClosed: true}}
		err = sendMessageToRunner(cacheFileRequest)
	}
	go publishDiagnostics(ctx, conn)
	return err
}

func registerRunnerCapabilities(conn jsonrpc2.JSONRPC2, ctx context.Context) {
	var result string
	// TODO : fetch the language dynamically
	ds := documentSelector{
		Language: "javascript",
		Pattern:  fmt.Sprintf("%s/**/*", config.ProjectRoot),
		Scheme:   "file",
	}
	conn.Call(ctx, "client/registerCapability", registrationParams{[]registration{
		{Id: "gauge-runner-didOpen", Method: "textDocument/didOpen", RegisterOptions: textDocumentRegistrationOptions{DocumentSelector: ds}},
		{Id: "gauge-runner-didClose", Method: "textDocument/didClose", RegisterOptions: textDocumentRegistrationOptions{DocumentSelector: ds}},
		{Id: "gauge-runner-didChange", Method: "textDocument/didChange", RegisterOptions: textDocumentChangeRegistrationOptions{textDocumentRegistrationOptions: textDocumentRegistrationOptions{DocumentSelector: ds}, SyncKind: lsp.TDSKFull}},
		{Id: "gauge-runner-codelens", Method: "textDocument/codeLens", RegisterOptions: codeLensRegistrationOptions{textDocumentRegistrationOptions: textDocumentRegistrationOptions{DocumentSelector: ds}, ResolveProvider: false}},
	}}, result)
}

type lspWriter struct {
}

func (w lspWriter) Write(p []byte) (n int, err error) {
	logger.LspLog.Debug(string(p))
	return os.Stderr.Write(p)
}

func (s *server) Start(logLevel string) {
	logger.APILog.Info("LangServer: reading on stdin, writing on stdout")
	var connOpt []jsonrpc2.ConnOpt
	if logLevel == "debug" {
		connOpt = append(connOpt, jsonrpc2.LogMessages(log.New(lspWriter{}, "", 0)))
	}
	ctx := context.Background()
	conn := jsonrpc2.NewConn(ctx, jsonrpc2.NewBufferedStream(stdRWC{}, jsonrpc2.VSCodeObjectCodec{}), newHandler(), connOpt...)
	logger.SetCustomLogger(lspLogger{conn, ctx})
	<-conn.DisconnectNotify()
	logger.APILog.Info("Connection closed")
}

type stdRWC struct{}

func (stdRWC) Read(p []byte) (int, error) {
	return os.Stdin.Read(p)
}

func (stdRWC) Write(p []byte) (int, error) {
	return os.Stdout.Write(p)
}

func (stdRWC) Close() error {
	if err := os.Stdin.Close(); err != nil {
		return err
	}
	return os.Stdout.Close()
}

type lspLogger struct {
	conn *jsonrpc2.Conn
	ctx  context.Context
}

func (c lspLogger) Log(logLevel logging.Level, msg string) {
	logger.APILog.Info(logLevel)
	var level lsp.MessageType
	switch logLevel {
	case logging.DEBUG:
		level = lsp.Log
	case logging.INFO:
		level = lsp.Info
	case logging.WARNING:
		level = lsp.MTWarning
	case logging.ERROR:
		level = lsp.MTError
	case logging.CRITICAL:
		level = lsp.MTError
	default:
		level = lsp.Info
	}
	c.conn.Notify(c.ctx, "window/logMessage", lsp.LogMessageParams{Type: level, Message: msg})
}
<|endoftext|>


<commit_before>/*
 * Package for reading sequence from fasta file.
 * by Wei Shen (shenwei356@gmail.com)
 */

package seq

import (
	"bufio"
	"bytes"
	"errors"
	"io"
	"os"
	"strings"
)

/*
FastaReader is a fasta file parser, which returns a function that
returns a pair of head and sequence when it was called.

Example:
	NextSeq, err := seq.FastaReader("test.fa")
	if err != nil {
		recover()
		fmt.Println(err)
		return
	}

	for {
		head, seq, err := NextSeq()
		if err != nil {
			// fmt.Println(err)
			break
		}

		fmt.Printf(">%s\n%s\n", head, seq)
	}
*/
func FastaReader(file string) (func() (string, string, error), error) {
	fh, err := os.Open(file)
	if err != nil {
		recover()
		return nil, errors.New("Failed to open file: " + file)
	}

	reader := bufio.NewReader(fh)
	var buffer bytes.Buffer
	var lastHead string
	var fileHandlerClosed bool = false

	return func() (head, seq string, err error) {
		if fileHandlerClosed {
			return "", "", io.EOF
		}

		var str string
		for {
			str, err = reader.ReadString('\n')
			if err == io.EOF {
				seq = buffer.String()
				buffer.Reset()
				head = lastHead
				fh.Close()
				fileHandlerClosed = true
				err = nil
				return
			}

			if strings.HasPrefix(str, ">") {
				thisHead := strings.TrimRight(str[1:], "\r?\n")
				if buffer.Len() > 0 { // no-first seq head
					seq = buffer.String()
					buffer.Reset()
					head, lastHead = lastHead, thisHead
					return
				} else { // first sequence head
					lastHead = thisHead
				}
			} else { // append sequence
				buffer.WriteString(strings.TrimRight(str, "\r?\n"))
			}
		}
	}, err
}
<commit_msg>update a bug in parsing fastafile in fasta.go<commit_after>/*
 * Package for reading sequence from fasta file.
 * by Wei Shen (shenwei356@gmail.com)
 */

package seq

import (
	"bufio"
	"bytes"
	"errors"
	"io"
	"os"
	"strings"
)

/*
FastaReader is a fasta file parser, which returns a function that
returns a pair of head and sequence when it was called.

Example:
	NextSeq, err := seq.FastaReader("test.fa")
	if err != nil {
		recover()
		fmt.Println(err)
		return
	}

	for {
		head, seq, err := NextSeq()
		if err != nil {
			// fmt.Println(err)
			break
		}

		fmt.Printf(">%s\n%s\n", head, seq)
	}
*/
func FastaReader(file string) (func() (string, string, error), error) {
	fh, err := os.Open(file)
	if err != nil {
		recover()
		return nil, errors.New("Failed to open file: " + file)
	}

	reader := bufio.NewReader(fh)
	var buffer bytes.Buffer
	var lastHead string
	var fileHandlerClosed bool = false

	return func() (head, seq string, err error) {
		if fileHandlerClosed {
			return "", "", io.EOF
		}

		var str string
		for {
			str, err = reader.ReadString('\n')
			if err == io.EOF {
				// do not for get the last line,
				// even which does not ends with "\n"
				buffer.WriteString(strings.TrimRight(str, "\r?\n"))

				seq = buffer.String()
				buffer.Reset()
				head = lastHead
				fh.Close()
				fileHandlerClosed = true
				err = nil
				return
			}

			if strings.HasPrefix(str, ">") {
				thisHead := strings.TrimRight(str[1:], "\r?\n")
				if buffer.Len() > 0 { // no-first seq head
					seq = buffer.String()
					buffer.Reset()
					head, lastHead = lastHead, thisHead
					return
				} else { // first sequence head
					lastHead = thisHead
				}
			} else { // append sequence
				buffer.WriteString(strings.TrimRight(str, "\r?\n"))
			}
		}
	}, err
}
<|endoftext|>


<commit_before>// Package dyndns provides a tool for running a
// dynamic dns updating service.
package dyndns

import (
	"github.com/mfycheng/name-dyndns/api"
	"github.com/mfycheng/name-dyndns/log"
	"sync"
	"time"
)

var wg sync.WaitGroup

func updateDNSRecord(a api.API, domain, recordId string, newRecord api.DNSRecord) error {
	log.Logger.Printf("Deleting DNS record for %s.%s.\n", newRecord.Name, domain)
	err := a.DeleteDNSRecord(domain, newRecord.RecordId)
	if err != nil {
		return err
	}

	log.Logger.Printf("Creating DNS record for %s.%s: %s\n", newRecord.Name, domain, newRecord)
	return a.CreateDNSRecord(domain, newRecord)
}

func runConfig(c api.Config, daemon bool) {
	defer wg.Done()

	a := api.NewAPIFromConfig(c)
	for {
		log.Logger.Printf("Running update check for %s.", c.Domain)
		ip, err := GetExternalIP()
		if err != nil {
			log.Logger.Print("Failed to retreive IP: ")
			if daemon {
				log.Logger.Printf("Will retry in %d seconds...\n", c.Interval)
				time.Sleep(time.Duration(c.Interval) * time.Second)

				continue
			} else {
				log.Logger.Println("Giving up.")
				break
			}
		}

		// GetRecords retrieves a list of DNSRecords,
		// 1 per hostname with the associated domain.
		// If the content is not the current IP, then
		// update it.
		records, err := a.GetDNSRecords(c.Domain)
		if err != nil {
			log.Logger.Printf("Failed to retreive records for %s:\n\t%s\n", c.Domain, err)
			if daemon {
				log.Logger.Printf("Will retry in %d seconds...\n", c.Interval)
				time.Sleep(time.Duration(c.Interval) * time.Second)

				continue
			} else {
				log.Logger.Print("Giving up.")
				break
			}
		}

		for _, r := range records {
			if r.Content != ip {
				r.Content = ip
				err = updateDNSRecord(a, c.Domain, r.RecordId, r)
				if err != nil {
					log.Logger.Printf("Failed to update record %s [%s.%s] with IP: %s\n\t%s\n", r.RecordId, r.Name, c.Domain, ip, err)
				} else {
					log.Logger.Printf("Attempting to update record %s [%s.%s] with IP: %s\n", r.RecordId, r.Name, c.Domain, ip)
				}
			}
		}

		log.Logger.Println("Update complete.")
		if !daemon {
			log.Logger.Println("Non daemon mode, stopping.")
			return
		}
		log.Logger.Printf("Will update again in %d seconds.\n", c.Interval)

		time.Sleep(time.Duration(c.Interval) * time.Second)
	}
}

// For each domain, check if the host record matches
// the current external IP. If it does not, it updates.
// If daemon is true, then Run will run forever, polling at
// an interval specified in each config.
func Run(configs []api.Config, daemon bool) {
	for _, config := range configs {
		wg.Add(1)
		go runConfig(config, daemon)
	}

	wg.Wait()
}
<commit_msg>nitpick on formatting consistency<commit_after>// Package dyndns provides a tool for running a
// dynamic dns updating service.
package dyndns

import (
	"github.com/mfycheng/name-dyndns/api"
	"github.com/mfycheng/name-dyndns/log"
	"sync"
	"time"
)

var wg sync.WaitGroup

func updateDNSRecord(a api.API, domain, recordId string, newRecord api.DNSRecord) error {
	log.Logger.Printf("Deleting DNS record for %s.%s.\n", newRecord.Name, domain)
	err := a.DeleteDNSRecord(domain, newRecord.RecordId)
	if err != nil {
		return err
	}

	log.Logger.Printf("Creating DNS record for %s.%s: %s\n", newRecord.Name, domain, newRecord)
	return a.CreateDNSRecord(domain, newRecord)
}

func runConfig(c api.Config, daemon bool) {
	defer wg.Done()

	a := api.NewAPIFromConfig(c)
	for {
		log.Logger.Printf("Running update check for %s.", c.Domain)
		ip, err := GetExternalIP()
		if err != nil {
			log.Logger.Print("Failed to retreive IP: ")
			if daemon {
				log.Logger.Printf("Will retry in %d seconds...\n", c.Interval)
				time.Sleep(time.Duration(c.Interval) * time.Second)
				continue
			} else {
				log.Logger.Println("Giving up.")
				break
			}
		}

		// GetRecords retrieves a list of DNSRecords,
		// 1 per hostname with the associated domain.
		// If the content is not the current IP, then
		// update it.
		records, err := a.GetDNSRecords(c.Domain)
		if err != nil {
			log.Logger.Printf("Failed to retreive records for %s:\n\t%s\n", c.Domain, err)
			if daemon {
				log.Logger.Printf("Will retry in %d seconds...\n", c.Interval)
				time.Sleep(time.Duration(c.Interval) * time.Second)
				continue
			} else {
				log.Logger.Print("Giving up.")
				break
			}
		}

		for _, r := range records {
			if r.Content != ip {
				r.Content = ip
				err = updateDNSRecord(a, c.Domain, r.RecordId, r)
				if err != nil {
					log.Logger.Printf("Failed to update record %s [%s.%s] with IP: %s\n\t%s\n", r.RecordId, r.Name, c.Domain, ip, err)
				} else {
					log.Logger.Printf("Attempting to update record %s [%s.%s] with IP: %s\n", r.RecordId, r.Name, c.Domain, ip)
				}
			}
		}

		log.Logger.Println("Update complete.")
		if !daemon {
			log.Logger.Println("Non daemon mode, stopping.")
			return
		}
		log.Logger.Printf("Will update again in %d seconds.\n", c.Interval)

		time.Sleep(time.Duration(c.Interval) * time.Second)
	}
}

// For each domain, check if the host record matches
// the current external IP. If it does not, it updates.
// If daemon is true, then Run will run forever, polling at
// an interval specified in each config.
func Run(configs []api.Config, daemon bool) {
	for _, config := range configs {
		wg.Add(1)
		go runConfig(config, daemon)
	}

	wg.Wait()
}
<|endoftext|>


<commit_before>package command

import (
	"os"
	"strings"
	"testing"

	"github.com/mitchellh/cli"
	"github.com/zclconf/go-cty/cty"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/configs/configschema"
	"github.com/hashicorp/terraform/states"
	"github.com/hashicorp/terraform/states/statefile"
	"github.com/hashicorp/terraform/terraform"
)

func TestApply_destroy(t *testing.T) {
	originalState := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_instance",
				Name: "foo",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"bar"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
	})
	statePath := testStateFile(t, originalState)

	p := testProvider()
	p.GetSchemaReturn = &terraform.ProviderSchema{
		ResourceTypes: map[string]*configschema.Block{
			"test_instance": {
				Attributes: map[string]*configschema.Attribute{
					"id":  {Type: cty.String, Computed: true},
					"ami": {Type: cty.String, Optional: true},
				},
			},
		},
	}

	ui := new(cli.MockUi)
	c := &ApplyCommand{
		Destroy: true,
		Meta: Meta{
			testingOverrides: metaOverridesForProvider(p),
			Ui:               ui,
		},
	}

	// Run the apply command pointing to our existing state
	args := []string{
		"-auto-approve",
		"-state", statePath,
		testFixturePath("apply"),
	}
	if code := c.Run(args); code != 0 {
		t.Log(ui.OutputWriter.String())
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	// Verify a new state exists
	if _, err := os.Stat(statePath); err != nil {
		t.Fatalf("err: %s", err)
	}

	f, err := os.Open(statePath)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer f.Close()

	stateFile, err := statefile.Read(f)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	if stateFile.State == nil {
		t.Fatal("state should not be nil")
	}

	actualStr := strings.TrimSpace(stateFile.State.String())
	expectedStr := strings.TrimSpace(testApplyDestroyStr)
	if actualStr != expectedStr {
		t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
	}

	// Should have a backup file
	f, err = os.Open(statePath + DefaultBackupExtension)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	backupStateFile, err := statefile.Read(f)
	f.Close()
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	actualStr = strings.TrimSpace(backupStateFile.State.String())
	expectedStr = strings.TrimSpace(originalState.String())
	if actualStr != expectedStr {
		t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
	}
}

func TestApply_destroyLockedState(t *testing.T) {
	originalState := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_instance",
				Name: "foo",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"bar"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
	})
	statePath := testStateFile(t, originalState)

	unlock, err := testLockState("./testdata", statePath)
	if err != nil {
		t.Fatal(err)
	}
	defer unlock()

	p := testProvider()
	ui := new(cli.MockUi)
	c := &ApplyCommand{
		Destroy: true,
		Meta: Meta{
			testingOverrides: metaOverridesForProvider(p),
			Ui:               ui,
		},
	}

	// Run the apply command pointing to our existing state
	args := []string{
		"-auto-approve",
		"-state", statePath,
		testFixturePath("apply"),
	}

	if code := c.Run(args); code == 0 {
		t.Fatal("expected error")
	}

	output := ui.ErrorWriter.String()
	if !strings.Contains(output, "lock") {
		t.Fatal("command output does not look like a lock error:", output)
	}
}

func TestApply_destroyPlan(t *testing.T) {
	planPath := testPlanFileNoop(t)

	p := testProvider()
	ui := new(cli.MockUi)
	c := &ApplyCommand{
		Destroy: true,
		Meta: Meta{
			testingOverrides: metaOverridesForProvider(p),
			Ui:               ui,
		},
	}

	// Run the apply command pointing to our existing state
	args := []string{
		planPath,
	}
	if code := c.Run(args); code != 1 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}
}

func TestApply_destroyTargeted(t *testing.T) {
	originalState := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_instance",
				Name: "foo",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"i-ab123"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_load_balancer",
				Name: "foo",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"i-abc123"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
	})
	statePath := testStateFile(t, originalState)

	p := testProvider()
	p.GetSchemaReturn = &terraform.ProviderSchema{
		ResourceTypes: map[string]*configschema.Block{
			"test_instance": {
				Attributes: map[string]*configschema.Attribute{
					"id": {Type: cty.String, Computed: true},
				},
			},
			"test_load_balancer": {
				Attributes: map[string]*configschema.Attribute{
					"instances": {Type: cty.List(cty.String), Optional: true},
				},
			},
		},
	}

	ui := new(cli.MockUi)
	c := &ApplyCommand{
		Destroy: true,
		Meta: Meta{
			testingOverrides: metaOverridesForProvider(p),
			Ui:               ui,
		},
	}

	// Run the apply command pointing to our existing state
	args := []string{
		"-auto-approve",
		"-target", "test_instance.foo",
		"-state", statePath,
		testFixturePath("apply-destroy-targeted"),
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	// Verify a new state exists
	if _, err := os.Stat(statePath); err != nil {
		t.Fatalf("err: %s", err)
	}

	f, err := os.Open(statePath)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer f.Close()

	state, err := terraform.ReadState(f)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	if state == nil {
		t.Fatal("state should not be nil")
	}

	actualStr := strings.TrimSpace(state.String())
	expectedStr := strings.TrimSpace(testApplyDestroyStr)
	if actualStr != expectedStr {
		t.Fatalf("bad:\n\n%s\n\nexpected:\n\n%s", actualStr, expectedStr)
	}

	// Should have a backup file
	f, err = os.Open(statePath + DefaultBackupExtension)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	backupState, err := terraform.ReadState(f)
	f.Close()
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	actualStr = strings.TrimSpace(backupState.String())
	expectedStr = strings.TrimSpace(originalState.String())
	if actualStr != expectedStr {
		t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\nb%s", actualStr, expectedStr)
	}
}

const testApplyDestroyStr = `
<no state>
`
<commit_msg>command: Fix TestApply_destroyTargeted for new provider and state types<commit_after>package command

import (
	"os"
	"strings"
	"testing"

	"github.com/davecgh/go-spew/spew"
	"github.com/mitchellh/cli"
	"github.com/zclconf/go-cty/cty"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/configs/configschema"
	"github.com/hashicorp/terraform/providers"
	"github.com/hashicorp/terraform/states"
	"github.com/hashicorp/terraform/states/statefile"
	"github.com/hashicorp/terraform/terraform"
)

func TestApply_destroy(t *testing.T) {
	originalState := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_instance",
				Name: "foo",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"bar"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
	})
	statePath := testStateFile(t, originalState)

	p := testProvider()
	p.GetSchemaReturn = &terraform.ProviderSchema{
		ResourceTypes: map[string]*configschema.Block{
			"test_instance": {
				Attributes: map[string]*configschema.Attribute{
					"id":  {Type: cty.String, Computed: true},
					"ami": {Type: cty.String, Optional: true},
				},
			},
		},
	}

	ui := new(cli.MockUi)
	c := &ApplyCommand{
		Destroy: true,
		Meta: Meta{
			testingOverrides: metaOverridesForProvider(p),
			Ui:               ui,
		},
	}

	// Run the apply command pointing to our existing state
	args := []string{
		"-auto-approve",
		"-state", statePath,
		testFixturePath("apply"),
	}
	if code := c.Run(args); code != 0 {
		t.Log(ui.OutputWriter.String())
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	// Verify a new state exists
	if _, err := os.Stat(statePath); err != nil {
		t.Fatalf("err: %s", err)
	}

	f, err := os.Open(statePath)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer f.Close()

	stateFile, err := statefile.Read(f)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	if stateFile.State == nil {
		t.Fatal("state should not be nil")
	}

	actualStr := strings.TrimSpace(stateFile.State.String())
	expectedStr := strings.TrimSpace(testApplyDestroyStr)
	if actualStr != expectedStr {
		t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
	}

	// Should have a backup file
	f, err = os.Open(statePath + DefaultBackupExtension)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	backupStateFile, err := statefile.Read(f)
	f.Close()
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	actualStr = strings.TrimSpace(backupStateFile.State.String())
	expectedStr = strings.TrimSpace(originalState.String())
	if actualStr != expectedStr {
		t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
	}
}

func TestApply_destroyLockedState(t *testing.T) {
	originalState := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_instance",
				Name: "foo",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"bar"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
	})
	statePath := testStateFile(t, originalState)

	unlock, err := testLockState("./testdata", statePath)
	if err != nil {
		t.Fatal(err)
	}
	defer unlock()

	p := testProvider()
	ui := new(cli.MockUi)
	c := &ApplyCommand{
		Destroy: true,
		Meta: Meta{
			testingOverrides: metaOverridesForProvider(p),
			Ui:               ui,
		},
	}

	// Run the apply command pointing to our existing state
	args := []string{
		"-auto-approve",
		"-state", statePath,
		testFixturePath("apply"),
	}

	if code := c.Run(args); code == 0 {
		t.Fatal("expected error")
	}

	output := ui.ErrorWriter.String()
	if !strings.Contains(output, "lock") {
		t.Fatal("command output does not look like a lock error:", output)
	}
}

func TestApply_destroyPlan(t *testing.T) {
	planPath := testPlanFileNoop(t)

	p := testProvider()
	ui := new(cli.MockUi)
	c := &ApplyCommand{
		Destroy: true,
		Meta: Meta{
			testingOverrides: metaOverridesForProvider(p),
			Ui:               ui,
		},
	}

	// Run the apply command pointing to our existing state
	args := []string{
		planPath,
	}
	if code := c.Run(args); code != 1 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}
}

func TestApply_destroyTargeted(t *testing.T) {
	originalState := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_instance",
				Name: "foo",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"i-ab123"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_load_balancer",
				Name: "foo",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"i-abc123"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
	})
	statePath := testStateFile(t, originalState)

	p := testProvider()
	p.GetSchemaReturn = &terraform.ProviderSchema{
		ResourceTypes: map[string]*configschema.Block{
			"test_instance": {
				Attributes: map[string]*configschema.Attribute{
					"id": {Type: cty.String, Computed: true},
				},
			},
			"test_load_balancer": {
				Attributes: map[string]*configschema.Attribute{
					"id":        {Type: cty.String, Computed: true},
					"instances": {Type: cty.List(cty.String), Optional: true},
				},
			},
		},
	}
	p.PlanResourceChangeFn = func (req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
		return providers.PlanResourceChangeResponse{
			PlannedState: req.ProposedNewState,
		}
	}

	ui := new(cli.MockUi)
	c := &ApplyCommand{
		Destroy: true,
		Meta: Meta{
			testingOverrides: metaOverridesForProvider(p),
			Ui:               ui,
		},
	}

	// Run the apply command pointing to our existing state
	args := []string{
		"-auto-approve",
		"-target", "test_instance.foo",
		"-state", statePath,
		testFixturePath("apply-destroy-targeted"),
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	// Verify a new state exists
	if _, err := os.Stat(statePath); err != nil {
		t.Fatalf("err: %s", err)
	}

	f, err := os.Open(statePath)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer f.Close()

	stateFile, err := statefile.Read(f)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	if stateFile == nil || stateFile.State == nil {
		t.Fatal("state should not be nil")
	}

	spew.Config.DisableMethods = true
	if !stateFile.State.Empty() {
		t.Fatalf("unexpected final state\ngot: %s\nwant: empty state", spew.Sdump(stateFile.State))
	}

	// Should have a backup file
	f, err = os.Open(statePath + DefaultBackupExtension)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	backupStateFile, err := statefile.Read(f)
	f.Close()
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	actualStr := strings.TrimSpace(backupStateFile.State.String())
	expectedStr := strings.TrimSpace(originalState.String())
	if actualStr != expectedStr {
		t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\nb%s", actualStr, expectedStr)
	}
}

const testApplyDestroyStr = `
<no state>
`
<|endoftext|>


<commit_before>package users

import (
	"sort"

	"github.com/aerogo/aero"
	"github.com/animenotifier/arn"
	"github.com/animenotifier/notify.moe/components"
)

// Active ...
func Active(ctx *aero.Context) string {
	users := arn.FilterUsers(func(user *arn.User) bool {
		return user.IsActive() && user.HasAvatar()
	})

	sort.Slice(users, func(i, j int) bool {
		return len(users[i].AnimeList().Watching().Items) > len(users[j].AnimeList().Watching().Items)
	})

	// arn.SortUsersLastSeen(users)

	return ctx.HTML(components.Users(users))
}

// Osu ...
func Osu(ctx *aero.Context) string {
	users := arn.FilterUsers(func(user *arn.User) bool {
		return user.IsActive() && user.HasAvatar() && user.Accounts.Osu.PP > 0
	})

	// Sort by pp
	sort.Slice(users, func(i, j int) bool {
		return users[i].Accounts.Osu.PP > users[j].Accounts.Osu.PP
	})

	if len(users) > 50 {
		users = users[:50]
	}

	return ctx.HTML(components.OsuRankingList(users))
}

// Staff ...
func Staff(ctx *aero.Context) string {
	users := arn.FilterUsers(func(user *arn.User) bool {
		return user.IsActive() && user.HasAvatar() && user.Role != ""
	})

	sort.Slice(users, func(i, j int) bool {
		if users[i].Role == "" {
			return false
		}

		if users[j].Role == "" {
			return true
		}

		return users[i].Role == "admin"
	})

	return ctx.HTML(components.Users(users))
}
<commit_msg>Sort by followers and make it deterministic<commit_after>package users

import (
	"sort"

	"github.com/aerogo/aero"
	"github.com/animenotifier/arn"
	"github.com/animenotifier/notify.moe/components"
)

// Active ...
func Active(ctx *aero.Context) string {
	users := arn.FilterUsers(func(user *arn.User) bool {
		return user.IsActive() && user.HasAvatar()
	})

	sort.Slice(users, func(i, j int) bool {
		followersA := users[i].FollowersCount()
		followersB := users[j].FollowersCount()

		if followersA == followersB {
			return users[i].Nick > users[j].Nick
		}

		return followersA > followersB
	})

	// arn.SortUsersLastSeen(users)

	return ctx.HTML(components.Users(users))
}

// Osu ...
func Osu(ctx *aero.Context) string {
	users := arn.FilterUsers(func(user *arn.User) bool {
		return user.IsActive() && user.HasAvatar() && user.Accounts.Osu.PP > 0
	})

	// Sort by pp
	sort.Slice(users, func(i, j int) bool {
		return users[i].Accounts.Osu.PP > users[j].Accounts.Osu.PP
	})

	if len(users) > 50 {
		users = users[:50]
	}

	return ctx.HTML(components.OsuRankingList(users))
}

// Staff ...
func Staff(ctx *aero.Context) string {
	users := arn.FilterUsers(func(user *arn.User) bool {
		return user.IsActive() && user.HasAvatar() && user.Role != ""
	})

	sort.Slice(users, func(i, j int) bool {
		if users[i].Role == "" {
			return false
		}

		if users[j].Role == "" {
			return true
		}

		return users[i].Role == "admin"
	})

	return ctx.HTML(components.Users(users))
}
<|endoftext|>