<commit_before>package api

import (
	"github.com/go-macaron/binding"
	"github.com/grafana/grafana/pkg/api/avatar"
	"github.com/grafana/grafana/pkg/api/dtos"
	"github.com/grafana/grafana/pkg/api/live"
	"github.com/grafana/grafana/pkg/middleware"
	m "github.com/grafana/grafana/pkg/models"
	"gopkg.in/macaron.v1"
)

// Register adds http routes
func Register(r *macaron.Macaron) {
	reqSignedIn := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true})
	reqGrafanaAdmin := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true, ReqGrafanaAdmin: true})
	reqEditorRole := middleware.RoleAuth(m.ROLE_EDITOR, m.ROLE_ADMIN)
	reqOrgAdmin := middleware.RoleAuth(m.ROLE_ADMIN)
	quota := middleware.Quota
	bind := binding.Bind

	// automatically set HEAD for every GET
	r.SetAutoHead(true)

	// not logged in views
	r.Get("/", reqSignedIn, Index)
	r.Get("/logout", Logout)
	r.Post("/login", quota("session"), bind(dtos.LoginCommand{}), wrap(LoginPost))
	r.Get("/login/:name", quota("session"), OAuthLogin)
	r.Get("/login", LoginView)
	r.Get("/invite/:code", Index)

	// authed views
	r.Get("/profile/", reqSignedIn, Index)
	r.Get("/profile/password", reqSignedIn, Index)
	r.Get("/profile/switch-org/:id", reqSignedIn, ChangeActiveOrgAndRedirectToHome)
	r.Get("/org/", reqSignedIn, Index)
	r.Get("/org/new", reqSignedIn, Index)
	r.Get("/datasources/", reqSignedIn, Index)
	r.Get("/datasources/new", reqSignedIn, Index)
	r.Get("/datasources/edit/*", reqSignedIn, Index)
	r.Get("/org/users/", reqSignedIn, Index)
	r.Get("/org/apikeys/", reqSignedIn, Index)
	r.Get("/dashboard/import/", reqSignedIn, Index)
	r.Get("/admin", reqGrafanaAdmin, Index)
	r.Get("/admin/settings", reqGrafanaAdmin, Index)
	r.Get("/admin/users", reqGrafanaAdmin, Index)
	r.Get("/admin/users/create", reqGrafanaAdmin, Index)
	r.Get("/admin/users/edit/:id", reqGrafanaAdmin, Index)
	r.Get("/admin/orgs", reqGrafanaAdmin, Index)
	r.Get("/admin/orgs/edit/:id", reqGrafanaAdmin, Index)
	r.Get("/admin/stats", reqGrafanaAdmin, Index)

	r.Get("/styleguide", reqSignedIn, Index)

	r.Get("/plugins", reqSignedIn, Index)
	r.Get("/plugins/:id/edit", reqSignedIn, Index)
	r.Get("/plugins/:id/page/:page", reqSignedIn, Index)

	r.Get("/dashboard/*", reqSignedIn, Index)
	r.Get("/dashboard-solo/*", reqSignedIn, Index)
	r.Get("/import/dashboard", reqSignedIn, Index)
	r.Get("/dashboards/*", reqSignedIn, Index)

	r.Get("/playlists/", reqSignedIn, Index)
	r.Get("/playlists/*", reqSignedIn, Index)
	r.Get("/alerting/", reqSignedIn, Index)
	r.Get("/alerting/*", reqSignedIn, Index)

	// sign up
	r.Get("/signup", Index)
	r.Get("/api/user/signup/options", wrap(GetSignUpOptions))
	r.Post("/api/user/signup", quota("user"), bind(dtos.SignUpForm{}), wrap(SignUp))
	r.Post("/api/user/signup/step2", bind(dtos.SignUpStep2Form{}), wrap(SignUpStep2))

	// invited
	r.Get("/api/user/invite/:code", wrap(GetInviteInfoByCode))
	r.Post("/api/user/invite/complete", bind(dtos.CompleteInviteForm{}), wrap(CompleteInvite))

	// reset password
	r.Get("/user/password/send-reset-email", Index)
	r.Get("/user/password/reset", Index)

	r.Post("/api/user/password/send-reset-email", bind(dtos.SendResetPasswordEmailForm{}), wrap(SendResetPasswordEmail))
	r.Post("/api/user/password/reset", bind(dtos.ResetUserPasswordForm{}), wrap(ResetPassword))

	// dashboard snapshots
	r.Get("/dashboard/snapshot/*", Index)
	r.Get("/dashboard/snapshots/", reqSignedIn, Index)

	// api for dashboard snapshots
	r.Post("/api/snapshots/", bind(m.CreateDashboardSnapshotCommand{}), CreateDashboardSnapshot)
	r.Get("/api/snapshot/shared-options/", GetSharingOptions)
	r.Get("/api/snapshots/:key", GetDashboardSnapshot)
	r.Get("/api/snapshots-delete/:key", reqEditorRole, DeleteDashboardSnapshot)

	// api renew session based on remember cookie
	r.Get("/api/login/ping", quota("session"), LoginApiPing)

	// authed api
	r.Group("/api", func() {

		// user (signed in)
		r.Group("/user", func() {
			r.Get("/", wrap(GetSignedInUser))
			r.Put("/", bind(m.UpdateUserCommand{}), wrap(UpdateSignedInUser))
			r.Post("/using/:id", wrap(UserSetUsingOrg))
			r.Get("/orgs", wrap(GetSignedInUserOrgList))

			r.Post("/stars/dashboard/:id", wrap(StarDashboard))
			r.Delete("/stars/dashboard/:id", wrap(UnstarDashboard))

			r.Put("/password", bind(m.ChangeUserPasswordCommand{}), wrap(ChangeUserPassword))
			r.Get("/quotas", wrap(GetUserQuotas))

			r.Get("/preferences", wrap(GetUserPreferences))
			r.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), wrap(UpdateUserPreferences))
		})

		// users (admin permission required)
		r.Group("/users", func() {
			r.Get("/", wrap(SearchUsers))
			r.Get("/:id", wrap(GetUserById))
			r.Get("/:id/orgs", wrap(GetUserOrgList))
			r.Put("/:id", bind(m.UpdateUserCommand{}), wrap(UpdateUser))
			r.Post("/:id/using/:orgId", wrap(UpdateUserActiveOrg))
		}, reqGrafanaAdmin)

		// org information available to all users.
		r.Group("/org", func() {
			r.Get("/", wrap(GetOrgCurrent))
			r.Get("/quotas", wrap(GetOrgQuotas))
		})

		// current org
		r.Group("/org", func() {
			r.Put("/", bind(dtos.UpdateOrgForm{}), wrap(UpdateOrgCurrent))
			r.Put("/address", bind(dtos.UpdateOrgAddressForm{}), wrap(UpdateOrgAddressCurrent))
			r.Post("/users", quota("user"), bind(m.AddOrgUserCommand{}), wrap(AddOrgUserToCurrentOrg))
			r.Get("/users", wrap(GetOrgUsersForCurrentOrg))
			r.Patch("/users/:userId", bind(m.UpdateOrgUserCommand{}), wrap(UpdateOrgUserForCurrentOrg))
			r.Delete("/users/:userId", wrap(RemoveOrgUserForCurrentOrg))

			// invites
			r.Get("/invites", wrap(GetPendingOrgInvites))
			r.Post("/invites", quota("user"), bind(dtos.AddInviteForm{}), wrap(AddOrgInvite))
			r.Patch("/invites/:code/revoke", wrap(RevokeInvite))

			// prefs
			r.Get("/preferences", wrap(GetOrgPreferences))
			r.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), wrap(UpdateOrgPreferences))
		}, reqOrgAdmin)

		// create new org
		r.Post("/orgs", quota("org"), bind(m.CreateOrgCommand{}), wrap(CreateOrg))

		// search all orgs
		r.Get("/orgs", reqGrafanaAdmin, wrap(SearchOrgs))

		// orgs (admin routes)
		r.Group("/orgs/:orgId", func() {
			r.Get("/", wrap(GetOrgById))
			r.Put("/", bind(dtos.UpdateOrgForm{}), wrap(UpdateOrg))
			r.Put("/address", bind(dtos.UpdateOrgAddressForm{}), wrap(UpdateOrgAddress))
			r.Delete("/", wrap(DeleteOrgById))
			r.Get("/users", wrap(GetOrgUsers))
			r.Post("/users", bind(m.AddOrgUserCommand{}), wrap(AddOrgUser))
			r.Patch("/users/:userId", bind(m.UpdateOrgUserCommand{}), wrap(UpdateOrgUser))
			r.Delete("/users/:userId", wrap(RemoveOrgUser))
			r.Get("/quotas", wrap(GetOrgQuotas))
			r.Put("/quotas/:target", bind(m.UpdateOrgQuotaCmd{}), wrap(UpdateOrgQuota))
		}, reqGrafanaAdmin)

		// orgs (admin routes)
		r.Group("/orgs/name/:name", func() {
			r.Get("/", wrap(GetOrgByName))
		}, reqGrafanaAdmin)

		// auth api keys
		r.Group("/auth/keys", func() {
			r.Get("/", wrap(GetApiKeys))
			r.Post("/", quota("api_key"), bind(m.AddApiKeyCommand{}), wrap(AddApiKey))
			r.Delete("/:id", wrap(DeleteApiKey))
		}, reqOrgAdmin)

		// Preferences
		r.Group("/preferences", func() {
			r.Post("/set-home-dash", bind(m.SavePreferencesCommand{}), wrap(SetHomeDashboard))
		})

		// Data sources
		r.Group("/datasources", func() {
			r.Get("/", GetDataSources)
			r.Post("/", quota("data_source"), bind(m.AddDataSourceCommand{}), AddDataSource)
			r.Put("/:id", bind(m.UpdateDataSourceCommand{}), UpdateDataSource)
			r.Delete("/:id", DeleteDataSource)
			r.Get("/:id", wrap(GetDataSourceById))
			r.Get("/name/:name", wrap(GetDataSourceByName))
		}, reqOrgAdmin)

		r.Get("/datasources/id/:name", wrap(GetDataSourceIdByName), reqSignedIn)

		r.Get("/plugins", wrap(GetPluginList))
		r.Get("/plugins/:pluginId/settings", wrap(GetPluginSettingById))

		r.Group("/plugins", func() {
			r.Get("/:pluginId/readme", wrap(GetPluginReadme))
			r.Get("/:pluginId/dashboards/", wrap(GetPluginDashboards))
			r.Post("/:pluginId/settings", bind(m.UpdatePluginSettingCmd{}), wrap(UpdatePluginSetting))
		}, reqOrgAdmin)

		r.Get("/frontend/settings/", GetFrontendSettings)
		r.Any("/datasources/proxy/:id/*", reqSignedIn, ProxyDataSourceRequest)
		r.Any("/datasources/proxy/:id", reqSignedIn, ProxyDataSourceRequest)

		// Dashboard
		r.Group("/dashboards", func() {
			r.Combo("/db/:slug").Get(GetDashboard).Delete(DeleteDashboard)
			r.Post("/db", reqEditorRole, bind(m.SaveDashboardCommand{}), wrap(PostDashboard))
			r.Get("/file/:file", GetDashboardFromJsonFile)
			r.Get("/home", wrap(GetHomeDashboard))
			r.Get("/tags", GetDashboardTags)
			r.Post("/import", bind(dtos.ImportDashboardCommand{}), wrap(ImportDashboard))
		})

		// Dashboard snapshots
		r.Group("/dashboard/snapshots", func() {
			r.Get("/", wrap(SearchDashboardSnapshots))
		})

		// Playlist
		r.Group("/playlists", func() {
			r.Get("/", wrap(SearchPlaylists))
			r.Get("/:id", ValidateOrgPlaylist, wrap(GetPlaylist))
			r.Get("/:id/items", ValidateOrgPlaylist, wrap(GetPlaylistItems))
			r.Get("/:id/dashboards", ValidateOrgPlaylist, wrap(GetPlaylistDashboards))
			r.Delete("/:id", reqEditorRole, ValidateOrgPlaylist, wrap(DeletePlaylist))
			r.Put("/:id", reqEditorRole, bind(m.UpdatePlaylistCommand{}), ValidateOrgPlaylist, wrap(UpdatePlaylist))
			r.Post("/", reqEditorRole, bind(m.CreatePlaylistCommand{}), wrap(CreatePlaylist))
		})

		// Search
		r.Get("/search/", Search)

		// metrics
		r.Get("/metrics/test", wrap(GetTestMetrics))

		// metrics
		r.Get("/metrics", wrap(GetInternalMetrics))

		r.Group("/alerts", func() {
			r.Post("/test", bind(dtos.AlertTestCommand{}), wrap(AlertTest))
			r.Get("/:alertId", ValidateOrgAlert, wrap(GetAlert))
			r.Get("/", wrap(GetAlerts))
		})

		r.Get("/alert-notifications", wrap(GetAlertNotifications))

		r.Group("/alert-notifications", func() {
			r.Post("/test", bind(dtos.NotificationTestCommand{}), wrap(NotificationTest))
			r.Post("/", bind(m.CreateAlertNotificationCommand{}), wrap(CreateAlertNotification))
			r.Put("/:notificationId", bind(m.UpdateAlertNotificationCommand{}), wrap(UpdateAlertNotification))
			r.Get("/:notificationId", wrap(GetAlertNotificationById))
			r.Delete("/:notificationId", wrap(DeleteAlertNotification))
		}, reqOrgAdmin)

		r.Get("/annotations", wrap(GetAnnotations))

		// error test
		r.Get("/metrics/error", wrap(GenerateError))

	}, reqSignedIn)

	// admin api
	r.Group("/api/admin", func() {
		r.Get("/settings", AdminGetSettings)
		r.Post("/users", bind(dtos.AdminCreateUserForm{}), AdminCreateUser)
		r.Put("/users/:id/password", bind(dtos.AdminUpdateUserPasswordForm{}), AdminUpdateUserPassword)
		r.Put("/users/:id/permissions", bind(dtos.AdminUpdateUserPermissionsForm{}), AdminUpdateUserPermissions)
		r.Delete("/users/:id", AdminDeleteUser)
		r.Get("/users/:id/quotas", wrap(GetUserQuotas))
		r.Put("/users/:id/quotas/:target", bind(m.UpdateUserQuotaCmd{}), wrap(UpdateUserQuota))
		r.Get("/stats", AdminGetStats)
	}, reqGrafanaAdmin)

	// rendering
	r.Get("/render/*", reqSignedIn, RenderToPng)

	// grafana.net proxy
	r.Any("/api/gnet/*", reqSignedIn, ProxyGnetRequest)

	// Gravatar service.
	avt := avatar.CacheServer()
	r.Get("/avatar/:hash", avt.ServeHTTP)

	// Websocket
	liveConn := live.New()
	r.Any("/ws", liveConn.Serve)

	// streams
	r.Post("/api/streams/push", reqSignedIn, bind(dtos.StreamMessage{}), liveConn.PushToStream)

	InitAppPluginRoutes(r)

}
<commit_msg>allow non-admin users to view plugin readme<commit_after>package api

import (
	"github.com/go-macaron/binding"
	"github.com/grafana/grafana/pkg/api/avatar"
	"github.com/grafana/grafana/pkg/api/dtos"
	"github.com/grafana/grafana/pkg/api/live"
	"github.com/grafana/grafana/pkg/middleware"
	m "github.com/grafana/grafana/pkg/models"
	"gopkg.in/macaron.v1"
)

// Register adds http routes
func Register(r *macaron.Macaron) {
	reqSignedIn := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true})
	reqGrafanaAdmin := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true, ReqGrafanaAdmin: true})
	reqEditorRole := middleware.RoleAuth(m.ROLE_EDITOR, m.ROLE_ADMIN)
	reqOrgAdmin := middleware.RoleAuth(m.ROLE_ADMIN)
	quota := middleware.Quota
	bind := binding.Bind

	// automatically set HEAD for every GET
	r.SetAutoHead(true)

	// not logged in views
	r.Get("/", reqSignedIn, Index)
	r.Get("/logout", Logout)
	r.Post("/login", quota("session"), bind(dtos.LoginCommand{}), wrap(LoginPost))
	r.Get("/login/:name", quota("session"), OAuthLogin)
	r.Get("/login", LoginView)
	r.Get("/invite/:code", Index)

	// authed views
	r.Get("/profile/", reqSignedIn, Index)
	r.Get("/profile/password", reqSignedIn, Index)
	r.Get("/profile/switch-org/:id", reqSignedIn, ChangeActiveOrgAndRedirectToHome)
	r.Get("/org/", reqSignedIn, Index)
	r.Get("/org/new", reqSignedIn, Index)
	r.Get("/datasources/", reqSignedIn, Index)
	r.Get("/datasources/new", reqSignedIn, Index)
	r.Get("/datasources/edit/*", reqSignedIn, Index)
	r.Get("/org/users/", reqSignedIn, Index)
	r.Get("/org/apikeys/", reqSignedIn, Index)
	r.Get("/dashboard/import/", reqSignedIn, Index)
	r.Get("/admin", reqGrafanaAdmin, Index)
	r.Get("/admin/settings", reqGrafanaAdmin, Index)
	r.Get("/admin/users", reqGrafanaAdmin, Index)
	r.Get("/admin/users/create", reqGrafanaAdmin, Index)
	r.Get("/admin/users/edit/:id", reqGrafanaAdmin, Index)
	r.Get("/admin/orgs", reqGrafanaAdmin, Index)
	r.Get("/admin/orgs/edit/:id", reqGrafanaAdmin, Index)
	r.Get("/admin/stats", reqGrafanaAdmin, Index)

	r.Get("/styleguide", reqSignedIn, Index)

	r.Get("/plugins", reqSignedIn, Index)
	r.Get("/plugins/:id/edit", reqSignedIn, Index)
	r.Get("/plugins/:id/page/:page", reqSignedIn, Index)

	r.Get("/dashboard/*", reqSignedIn, Index)
	r.Get("/dashboard-solo/*", reqSignedIn, Index)
	r.Get("/import/dashboard", reqSignedIn, Index)
	r.Get("/dashboards/*", reqSignedIn, Index)

	r.Get("/playlists/", reqSignedIn, Index)
	r.Get("/playlists/*", reqSignedIn, Index)
	r.Get("/alerting/", reqSignedIn, Index)
	r.Get("/alerting/*", reqSignedIn, Index)

	// sign up
	r.Get("/signup", Index)
	r.Get("/api/user/signup/options", wrap(GetSignUpOptions))
	r.Post("/api/user/signup", quota("user"), bind(dtos.SignUpForm{}), wrap(SignUp))
	r.Post("/api/user/signup/step2", bind(dtos.SignUpStep2Form{}), wrap(SignUpStep2))

	// invited
	r.Get("/api/user/invite/:code", wrap(GetInviteInfoByCode))
	r.Post("/api/user/invite/complete", bind(dtos.CompleteInviteForm{}), wrap(CompleteInvite))

	// reset password
	r.Get("/user/password/send-reset-email", Index)
	r.Get("/user/password/reset", Index)

	r.Post("/api/user/password/send-reset-email", bind(dtos.SendResetPasswordEmailForm{}), wrap(SendResetPasswordEmail))
	r.Post("/api/user/password/reset", bind(dtos.ResetUserPasswordForm{}), wrap(ResetPassword))

	// dashboard snapshots
	r.Get("/dashboard/snapshot/*", Index)
	r.Get("/dashboard/snapshots/", reqSignedIn, Index)

	// api for dashboard snapshots
	r.Post("/api/snapshots/", bind(m.CreateDashboardSnapshotCommand{}), CreateDashboardSnapshot)
	r.Get("/api/snapshot/shared-options/", GetSharingOptions)
	r.Get("/api/snapshots/:key", GetDashboardSnapshot)
	r.Get("/api/snapshots-delete/:key", reqEditorRole, DeleteDashboardSnapshot)

	// api renew session based on remember cookie
	r.Get("/api/login/ping", quota("session"), LoginApiPing)

	// authed api
	r.Group("/api", func() {

		// user (signed in)
		r.Group("/user", func() {
			r.Get("/", wrap(GetSignedInUser))
			r.Put("/", bind(m.UpdateUserCommand{}), wrap(UpdateSignedInUser))
			r.Post("/using/:id", wrap(UserSetUsingOrg))
			r.Get("/orgs", wrap(GetSignedInUserOrgList))

			r.Post("/stars/dashboard/:id", wrap(StarDashboard))
			r.Delete("/stars/dashboard/:id", wrap(UnstarDashboard))

			r.Put("/password", bind(m.ChangeUserPasswordCommand{}), wrap(ChangeUserPassword))
			r.Get("/quotas", wrap(GetUserQuotas))

			r.Get("/preferences", wrap(GetUserPreferences))
			r.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), wrap(UpdateUserPreferences))
		})

		// users (admin permission required)
		r.Group("/users", func() {
			r.Get("/", wrap(SearchUsers))
			r.Get("/:id", wrap(GetUserById))
			r.Get("/:id/orgs", wrap(GetUserOrgList))
			r.Put("/:id", bind(m.UpdateUserCommand{}), wrap(UpdateUser))
			r.Post("/:id/using/:orgId", wrap(UpdateUserActiveOrg))
		}, reqGrafanaAdmin)

		// org information available to all users.
		r.Group("/org", func() {
			r.Get("/", wrap(GetOrgCurrent))
			r.Get("/quotas", wrap(GetOrgQuotas))
		})

		// current org
		r.Group("/org", func() {
			r.Put("/", bind(dtos.UpdateOrgForm{}), wrap(UpdateOrgCurrent))
			r.Put("/address", bind(dtos.UpdateOrgAddressForm{}), wrap(UpdateOrgAddressCurrent))
			r.Post("/users", quota("user"), bind(m.AddOrgUserCommand{}), wrap(AddOrgUserToCurrentOrg))
			r.Get("/users", wrap(GetOrgUsersForCurrentOrg))
			r.Patch("/users/:userId", bind(m.UpdateOrgUserCommand{}), wrap(UpdateOrgUserForCurrentOrg))
			r.Delete("/users/:userId", wrap(RemoveOrgUserForCurrentOrg))

			// invites
			r.Get("/invites", wrap(GetPendingOrgInvites))
			r.Post("/invites", quota("user"), bind(dtos.AddInviteForm{}), wrap(AddOrgInvite))
			r.Patch("/invites/:code/revoke", wrap(RevokeInvite))

			// prefs
			r.Get("/preferences", wrap(GetOrgPreferences))
			r.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), wrap(UpdateOrgPreferences))
		}, reqOrgAdmin)

		// create new org
		r.Post("/orgs", quota("org"), bind(m.CreateOrgCommand{}), wrap(CreateOrg))

		// search all orgs
		r.Get("/orgs", reqGrafanaAdmin, wrap(SearchOrgs))

		// orgs (admin routes)
		r.Group("/orgs/:orgId", func() {
			r.Get("/", wrap(GetOrgById))
			r.Put("/", bind(dtos.UpdateOrgForm{}), wrap(UpdateOrg))
			r.Put("/address", bind(dtos.UpdateOrgAddressForm{}), wrap(UpdateOrgAddress))
			r.Delete("/", wrap(DeleteOrgById))
			r.Get("/users", wrap(GetOrgUsers))
			r.Post("/users", bind(m.AddOrgUserCommand{}), wrap(AddOrgUser))
			r.Patch("/users/:userId", bind(m.UpdateOrgUserCommand{}), wrap(UpdateOrgUser))
			r.Delete("/users/:userId", wrap(RemoveOrgUser))
			r.Get("/quotas", wrap(GetOrgQuotas))
			r.Put("/quotas/:target", bind(m.UpdateOrgQuotaCmd{}), wrap(UpdateOrgQuota))
		}, reqGrafanaAdmin)

		// orgs (admin routes)
		r.Group("/orgs/name/:name", func() {
			r.Get("/", wrap(GetOrgByName))
		}, reqGrafanaAdmin)

		// auth api keys
		r.Group("/auth/keys", func() {
			r.Get("/", wrap(GetApiKeys))
			r.Post("/", quota("api_key"), bind(m.AddApiKeyCommand{}), wrap(AddApiKey))
			r.Delete("/:id", wrap(DeleteApiKey))
		}, reqOrgAdmin)

		// Preferences
		r.Group("/preferences", func() {
			r.Post("/set-home-dash", bind(m.SavePreferencesCommand{}), wrap(SetHomeDashboard))
		})

		// Data sources
		r.Group("/datasources", func() {
			r.Get("/", GetDataSources)
			r.Post("/", quota("data_source"), bind(m.AddDataSourceCommand{}), AddDataSource)
			r.Put("/:id", bind(m.UpdateDataSourceCommand{}), UpdateDataSource)
			r.Delete("/:id", DeleteDataSource)
			r.Get("/:id", wrap(GetDataSourceById))
			r.Get("/name/:name", wrap(GetDataSourceByName))
		}, reqOrgAdmin)

		r.Get("/datasources/id/:name", wrap(GetDataSourceIdByName), reqSignedIn)

		r.Get("/plugins", wrap(GetPluginList))
		r.Get("/plugins/:pluginId/settings", wrap(GetPluginSettingById))
		r.Get("/plugins/:pluginId/readme", wrap(GetPluginReadme))

		r.Group("/plugins", func() {
			r.Get("/:pluginId/dashboards/", wrap(GetPluginDashboards))
			r.Post("/:pluginId/settings", bind(m.UpdatePluginSettingCmd{}), wrap(UpdatePluginSetting))
		}, reqOrgAdmin)

		r.Get("/frontend/settings/", GetFrontendSettings)
		r.Any("/datasources/proxy/:id/*", reqSignedIn, ProxyDataSourceRequest)
		r.Any("/datasources/proxy/:id", reqSignedIn, ProxyDataSourceRequest)

		// Dashboard
		r.Group("/dashboards", func() {
			r.Combo("/db/:slug").Get(GetDashboard).Delete(DeleteDashboard)
			r.Post("/db", reqEditorRole, bind(m.SaveDashboardCommand{}), wrap(PostDashboard))
			r.Get("/file/:file", GetDashboardFromJsonFile)
			r.Get("/home", wrap(GetHomeDashboard))
			r.Get("/tags", GetDashboardTags)
			r.Post("/import", bind(dtos.ImportDashboardCommand{}), wrap(ImportDashboard))
		})

		// Dashboard snapshots
		r.Group("/dashboard/snapshots", func() {
			r.Get("/", wrap(SearchDashboardSnapshots))
		})

		// Playlist
		r.Group("/playlists", func() {
			r.Get("/", wrap(SearchPlaylists))
			r.Get("/:id", ValidateOrgPlaylist, wrap(GetPlaylist))
			r.Get("/:id/items", ValidateOrgPlaylist, wrap(GetPlaylistItems))
			r.Get("/:id/dashboards", ValidateOrgPlaylist, wrap(GetPlaylistDashboards))
			r.Delete("/:id", reqEditorRole, ValidateOrgPlaylist, wrap(DeletePlaylist))
			r.Put("/:id", reqEditorRole, bind(m.UpdatePlaylistCommand{}), ValidateOrgPlaylist, wrap(UpdatePlaylist))
			r.Post("/", reqEditorRole, bind(m.CreatePlaylistCommand{}), wrap(CreatePlaylist))
		})

		// Search
		r.Get("/search/", Search)

		// metrics
		r.Get("/metrics/test", wrap(GetTestMetrics))

		// metrics
		r.Get("/metrics", wrap(GetInternalMetrics))

		r.Group("/alerts", func() {
			r.Post("/test", bind(dtos.AlertTestCommand{}), wrap(AlertTest))
			r.Get("/:alertId", ValidateOrgAlert, wrap(GetAlert))
			r.Get("/", wrap(GetAlerts))
		})

		r.Get("/alert-notifications", wrap(GetAlertNotifications))

		r.Group("/alert-notifications", func() {
			r.Post("/test", bind(dtos.NotificationTestCommand{}), wrap(NotificationTest))
			r.Post("/", bind(m.CreateAlertNotificationCommand{}), wrap(CreateAlertNotification))
			r.Put("/:notificationId", bind(m.UpdateAlertNotificationCommand{}), wrap(UpdateAlertNotification))
			r.Get("/:notificationId", wrap(GetAlertNotificationById))
			r.Delete("/:notificationId", wrap(DeleteAlertNotification))
		}, reqOrgAdmin)

		r.Get("/annotations", wrap(GetAnnotations))

		// error test
		r.Get("/metrics/error", wrap(GenerateError))

	}, reqSignedIn)

	// admin api
	r.Group("/api/admin", func() {
		r.Get("/settings", AdminGetSettings)
		r.Post("/users", bind(dtos.AdminCreateUserForm{}), AdminCreateUser)
		r.Put("/users/:id/password", bind(dtos.AdminUpdateUserPasswordForm{}), AdminUpdateUserPassword)
		r.Put("/users/:id/permissions", bind(dtos.AdminUpdateUserPermissionsForm{}), AdminUpdateUserPermissions)
		r.Delete("/users/:id", AdminDeleteUser)
		r.Get("/users/:id/quotas", wrap(GetUserQuotas))
		r.Put("/users/:id/quotas/:target", bind(m.UpdateUserQuotaCmd{}), wrap(UpdateUserQuota))
		r.Get("/stats", AdminGetStats)
	}, reqGrafanaAdmin)

	// rendering
	r.Get("/render/*", reqSignedIn, RenderToPng)

	// grafana.net proxy
	r.Any("/api/gnet/*", reqSignedIn, ProxyGnetRequest)

	// Gravatar service.
	avt := avatar.CacheServer()
	r.Get("/avatar/:hash", avt.ServeHTTP)

	// Websocket
	liveConn := live.New()
	r.Any("/ws", liveConn.Serve)

	// streams
	r.Post("/api/streams/push", reqSignedIn, bind(dtos.StreamMessage{}), liveConn.PushToStream)

	InitAppPluginRoutes(r)

}
<|endoftext|>


<commit_before>//go:generate go run gen/html.go
//go:generate go run gen/scripts.go
//go:generate go fmt

// Package app is a package to build progressive web apps (PWA) with Go
// programming language and WebAssembly.
// It uses a declarative syntax that allows creating and dealing with HTML
// elements only by using Go, and without writing any HTML markup.
// The package also provides an http.handler ready to serve all the required
// resources to run Go-based progressive web apps.
package app

import (
	"context"
	"fmt"
	"net/url"
	"os"
	"runtime"
	"strings"
	"time"

	"github.com/maxence-charriere/go-app/v8/pkg/errors"
)

const (
	// IsClient reports whether the code is running as a client in the
	// WebAssembly binary (app.wasm).
	IsClient = runtime.GOARCH == "wasm" && runtime.GOOS == "js"

	// IsServer reports whether the code is running on a server for
	// pre-rendering purposes.
	IsServer = runtime.GOARCH != "wasm" || runtime.GOOS != "js"

	orientationChangeDelay = time.Millisecond * 500
)

var (
	staticResourcesURL string
	rootPrefix         string
	appUpdateAvailable bool
	lastURLVisited     *url.URL
)

// Getenv retrieves the value of the environment variable named by the key. It
// returns the value, which will be empty if the variable is not present.
func Getenv(k string) string {
	if IsServer {
		return os.Getenv(k)
	}

	env := Window().Call("goappGetenv", k)
	if !env.Truthy() {
		return ""
	}
	return env.String()
}

// KeepBodyClean prevents third-party Javascript libraries to add nodes to the
// body element.
func KeepBodyClean() (close func()) {
	if IsServer {
		return func() {}
	}

	release := Window().Call("goappKeepBodyClean")
	return func() {
		release.Invoke()
	}
}

// Window returns the JavaScript "window" object.
func Window() BrowserWindow {
	return window
}

// RunWhenOnBrowser starts the app, displaying the component associated with the
// current URL path.
//
// This call is skipped when the program is not run on a web browser. This
// allows writing client and server-side code without separation or
// pre-compilation flags.
//
// Eg:
//  func main() {
// 		// Define app routes.
// 		app.Route("/", myComponent{})
// 		app.Route("/other-page", myOtherComponent{})
//
// 		// Run the application when on a web browser (only executed on client side).
// 		app.RunWhenOnBrowser()
//
// 		// Launch the server that serves the app (only executed on server side):
// 		http.Handle("/", &app.Handler{Name: "My app"})
// 		http.ListenAndServe(":8080", nil)
//  }
func RunWhenOnBrowser() {
	if IsServer {
		return
	}

	defer func() {
		err := recover()
		displayLoadError(err)
		panic(err)
	}()

	staticResourcesResolver := newClientStaticResourceResolver(Getenv("GOAPP_STATIC_RESOURCES_URL"))
	rootPrefix = Getenv("GOAPP_ROOT_PREFIX")

	disp := newUIDispatcher(IsServer, browserPage{}, staticResourcesResolver)
	defer disp.Close()
	disp.body = newClientBody(disp)
	window.setBody(disp.body)

	onAchorClick := FuncOf(onAchorClick(disp))
	defer onAchorClick.Release()
	Window().Set("onclick", onAchorClick)

	onPopState := FuncOf(onPopState(disp))
	defer onPopState.Release()
	Window().Set("onpopstate", onPopState)

	onAppUpdate := FuncOf(onAppUpdate(disp))
	defer onAppUpdate.Release()
	Window().Set("goappOnUpdate", onAppUpdate)

	closeAppResize := Window().AddEventListener("resize", onAppResize)
	defer closeAppResize()

	closeAppOrientationChange := Window().AddEventListener("orientationchange", onAppOrientationChange)
	defer closeAppOrientationChange()

	performNavigate(disp, Window().URL(), false)
	disp.start(context.Background())
}

func displayLoadError(err interface{}) {
	loadingLabel := Window().
		Get("document").
		Call("getElementById", "app-wasm-loader-label")
	if !loadingLabel.Truthy() {
		return
	}
	loadingLabel.setInnerText(fmt.Sprint(err))
}

func newClientStaticResourceResolver(staticResourceURL string) func(string) string {
	return func(path string) string {
		if isRemoteLocation(path) || !isStaticResourcePath(path) {
			return path
		}

		var b strings.Builder
		b.WriteString(staticResourceURL)
		b.WriteByte('/')
		b.WriteString(strings.TrimPrefix(path, "/"))
		return b.String()
	}
}

func newClientBody(d Dispatcher) *htmlBody {
	ctx, cancel := context.WithCancel(context.Background())
	body := &htmlBody{
		elem: elem{
			ctx:       ctx,
			ctxCancel: cancel,
			jsvalue:   Window().Get("document").Get("body"),
			tag:       "body",
			disp:      d,
		},
	}
	body.setSelf(body)

	ctx, cancel = context.WithCancel(context.Background())
	content := &htmlDiv{
		elem: elem{
			ctx:       ctx,
			ctxCancel: cancel,
			jsvalue:   body.JSValue().firstElementChild(),
			tag:       "div",
			disp:      d,
		},
	}
	content.setSelf(content)
	content.setParent(body)

	body.body = append(body.body, content)
	return body
}

func onAchorClick(d *uiDispatcher) func(Value, []Value) interface{} {
	return func(this Value, args []Value) interface{} {
		event := Event{Value: args[0]}
		elem := event.Get("target")

		for {
			switch elem.Get("tagName").String() {
			case "A":
				if target := elem.Get("target"); target.Truthy() && target.String() == "_blank" {
					return nil
				}

				u := elem.Get("href").String()
				if u, _ := url.Parse(u); isExternalNavigation(u) {
					elem.Set("target", "_blank")
					return nil
				}

				if meta := event.Get("metaKey"); meta.Truthy() && meta.Bool() {
					return nil
				}

				if ctrl := event.Get("ctrlKey"); ctrl.Truthy() && ctrl.Bool() {
					return nil
				}

				event.PreventDefault()
				navigate(d, u)
				return nil

			case "BODY":
				return nil

			default:
				elem = elem.Get("parentElement")
				if !elem.Truthy() {
					return nil
				}
			}
		}
	}
}

func onPopState(d Dispatcher) func(this Value, args []Value) interface{} {
	return func(this Value, args []Value) interface{} {
		d.Dispatch(func() {
			navigateTo(d, Window().URL(), false)
		})
		return nil
	}
}

func navigate(d Dispatcher, rawURL string) {
	u, err := url.Parse(rawURL)
	if err != nil {
		Log("%s", errors.New("navigating to URL failed").
			Tag("url", rawURL).
			Wrap(err))
		return
	}
	navigateTo(d, u, true)
}

func navigateTo(d Dispatcher, u *url.URL, updateHistory bool) {
	if IsServer {
		return
	}

	if isExternalNavigation(u) {
		Window().Get("location").Set("href", u.String())
		return
	}

	luv := lastURLVisited

	if u.String() == luv.String() {
		return
	}

	if u.Path == luv.Path && u.Fragment != luv.Fragment {
		if updateHistory {
			Window().addHistory(u)
		} else {
			lastURLVisited = u
		}

		d.(*uiDispatcher).Nav(u)
		d.Dispatch(func() {
			if isFragmentNavigation(u) {
				Window().ScrollToID(u.Fragment)
			}
		})
		return
	}

	performNavigate(d, u, updateHistory)
}

func performNavigate(d Dispatcher, u *url.URL, updateHistory bool) {
	if IsServer {
		return
	}

	compo, ok := routes.createComponent(strings.TrimPrefix(u.Path, rootPrefix))
	if !ok {
		compo = &notFound{}
	}

	disp := d.(*uiDispatcher)
	disp.Mount(compo)

	if updateHistory {
		Window().addHistory(u)
	} else {
		lastURLVisited = u
	}

	disp.Nav(u)
	if isFragmentNavigation(u) {
		disp.Dispatch(func() {
			Window().ScrollToID(u.Fragment)
		})
	}
}

func isExternalNavigation(u *url.URL) bool {
	return u.Host != "" && u.Host != Window().URL().Host
}

func isFragmentNavigation(u *url.URL) bool {
	return u.Fragment != ""
}

func onAppUpdate(d *uiDispatcher) func(this Value, args []Value) interface{} {
	return func(this Value, args []Value) interface{} {
		d.Dispatch(func() {
			appUpdateAvailable = true
		})
		d.AppUpdate()
		d.Dispatch(func() {
			fmt.Println("app has been updated, reload to see changes")
		})
		return nil
	}
}

func onAppResize(ctx Context, e Event) {
	if d, ok := ctx.dispatcher.(*uiDispatcher); ok {
		d.AppResize()
	}
}

func onAppOrientationChange(ctx Context, e Event) {
	if d, ok := ctx.dispatcher.(*uiDispatcher); ok {
		go func() {
			time.Sleep(orientationChangeDelay)
			d.AppResize()
		}()
	}
}
<commit_msg>cleanup<commit_after>//go:generate go run gen/html.go
//go:generate go run gen/scripts.go
//go:generate go fmt

// Package app is a package to build progressive web apps (PWA) with Go
// programming language and WebAssembly.
// It uses a declarative syntax that allows creating and dealing with HTML
// elements only by using Go, and without writing any HTML markup.
// The package also provides an http.handler ready to serve all the required
// resources to run Go-based progressive web apps.
package app

import (
	"context"
	"fmt"
	"net/url"
	"os"
	"runtime"
	"strings"
	"time"

	"github.com/maxence-charriere/go-app/v8/pkg/errors"
)

const (
	// IsClient reports whether the code is running as a client in the
	// WebAssembly binary (app.wasm).
	IsClient = runtime.GOARCH == "wasm" && runtime.GOOS == "js"

	// IsServer reports whether the code is running on a server for
	// pre-rendering purposes.
	IsServer = runtime.GOARCH != "wasm" || runtime.GOOS != "js"

	orientationChangeDelay = time.Millisecond * 500
)

var (
	rootPrefix         string
	appUpdateAvailable bool
	lastURLVisited     *url.URL
)

// Getenv retrieves the value of the environment variable named by the key. It
// returns the value, which will be empty if the variable is not present.
func Getenv(k string) string {
	if IsServer {
		return os.Getenv(k)
	}

	env := Window().Call("goappGetenv", k)
	if !env.Truthy() {
		return ""
	}
	return env.String()
}

// KeepBodyClean prevents third-party Javascript libraries to add nodes to the
// body element.
func KeepBodyClean() (close func()) {
	if IsServer {
		return func() {}
	}

	release := Window().Call("goappKeepBodyClean")
	return func() {
		release.Invoke()
	}
}

// Window returns the JavaScript "window" object.
func Window() BrowserWindow {
	return window
}

// RunWhenOnBrowser starts the app, displaying the component associated with the
// current URL path.
//
// This call is skipped when the program is not run on a web browser. This
// allows writing client and server-side code without separation or
// pre-compilation flags.
//
// Eg:
//  func main() {
// 		// Define app routes.
// 		app.Route("/", myComponent{})
// 		app.Route("/other-page", myOtherComponent{})
//
// 		// Run the application when on a web browser (only executed on client side).
// 		app.RunWhenOnBrowser()
//
// 		// Launch the server that serves the app (only executed on server side):
// 		http.Handle("/", &app.Handler{Name: "My app"})
// 		http.ListenAndServe(":8080", nil)
//  }
func RunWhenOnBrowser() {
	if IsServer {
		return
	}

	defer func() {
		err := recover()
		displayLoadError(err)
		panic(err)
	}()

	staticResourcesResolver := newClientStaticResourceResolver(Getenv("GOAPP_STATIC_RESOURCES_URL"))
	rootPrefix = Getenv("GOAPP_ROOT_PREFIX")

	disp := newUIDispatcher(IsServer, browserPage{}, staticResourcesResolver)
	defer disp.Close()
	disp.body = newClientBody(disp)
	window.setBody(disp.body)

	onAchorClick := FuncOf(onAchorClick(disp))
	defer onAchorClick.Release()
	Window().Set("onclick", onAchorClick)

	onPopState := FuncOf(onPopState(disp))
	defer onPopState.Release()
	Window().Set("onpopstate", onPopState)

	onAppUpdate := FuncOf(onAppUpdate(disp))
	defer onAppUpdate.Release()
	Window().Set("goappOnUpdate", onAppUpdate)

	closeAppResize := Window().AddEventListener("resize", onAppResize)
	defer closeAppResize()

	closeAppOrientationChange := Window().AddEventListener("orientationchange", onAppOrientationChange)
	defer closeAppOrientationChange()

	performNavigate(disp, Window().URL(), false)
	disp.start(context.Background())
}

func displayLoadError(err interface{}) {
	loadingLabel := Window().
		Get("document").
		Call("getElementById", "app-wasm-loader-label")
	if !loadingLabel.Truthy() {
		return
	}
	loadingLabel.setInnerText(fmt.Sprint(err))
}

func newClientStaticResourceResolver(staticResourceURL string) func(string) string {
	return func(path string) string {
		if isRemoteLocation(path) || !isStaticResourcePath(path) {
			return path
		}

		var b strings.Builder
		b.WriteString(staticResourceURL)
		b.WriteByte('/')
		b.WriteString(strings.TrimPrefix(path, "/"))
		return b.String()
	}
}

func newClientBody(d Dispatcher) *htmlBody {
	ctx, cancel := context.WithCancel(context.Background())
	body := &htmlBody{
		elem: elem{
			ctx:       ctx,
			ctxCancel: cancel,
			jsvalue:   Window().Get("document").Get("body"),
			tag:       "body",
			disp:      d,
		},
	}
	body.setSelf(body)

	ctx, cancel = context.WithCancel(context.Background())
	content := &htmlDiv{
		elem: elem{
			ctx:       ctx,
			ctxCancel: cancel,
			jsvalue:   body.JSValue().firstElementChild(),
			tag:       "div",
			disp:      d,
		},
	}
	content.setSelf(content)
	content.setParent(body)

	body.body = append(body.body, content)
	return body
}

func onAchorClick(d *uiDispatcher) func(Value, []Value) interface{} {
	return func(this Value, args []Value) interface{} {
		event := Event{Value: args[0]}
		elem := event.Get("target")

		for {
			switch elem.Get("tagName").String() {
			case "A":
				if target := elem.Get("target"); target.Truthy() && target.String() == "_blank" {
					return nil
				}

				u := elem.Get("href").String()
				if u, _ := url.Parse(u); isExternalNavigation(u) {
					elem.Set("target", "_blank")
					return nil
				}

				if meta := event.Get("metaKey"); meta.Truthy() && meta.Bool() {
					return nil
				}

				if ctrl := event.Get("ctrlKey"); ctrl.Truthy() && ctrl.Bool() {
					return nil
				}

				event.PreventDefault()
				navigate(d, u)
				return nil

			case "BODY":
				return nil

			default:
				elem = elem.Get("parentElement")
				if !elem.Truthy() {
					return nil
				}
			}
		}
	}
}

func onPopState(d Dispatcher) func(this Value, args []Value) interface{} {
	return func(this Value, args []Value) interface{} {
		d.Dispatch(func() {
			navigateTo(d, Window().URL(), false)
		})
		return nil
	}
}

func navigate(d Dispatcher, rawURL string) {
	u, err := url.Parse(rawURL)
	if err != nil {
		Log("%s", errors.New("navigating to URL failed").
			Tag("url", rawURL).
			Wrap(err))
		return
	}
	navigateTo(d, u, true)
}

func navigateTo(d Dispatcher, u *url.URL, updateHistory bool) {
	if IsServer {
		return
	}

	if isExternalNavigation(u) {
		Window().Get("location").Set("href", u.String())
		return
	}

	luv := lastURLVisited

	if u.String() == luv.String() {
		return
	}

	if u.Path == luv.Path && u.Fragment != luv.Fragment {
		if updateHistory {
			Window().addHistory(u)
		} else {
			lastURLVisited = u
		}

		d.(*uiDispatcher).Nav(u)
		d.Dispatch(func() {
			if isFragmentNavigation(u) {
				Window().ScrollToID(u.Fragment)
			}
		})
		return
	}

	performNavigate(d, u, updateHistory)
}

func performNavigate(d Dispatcher, u *url.URL, updateHistory bool) {
	if IsServer {
		return
	}

	compo, ok := routes.createComponent(strings.TrimPrefix(u.Path, rootPrefix))
	if !ok {
		compo = &notFound{}
	}

	disp := d.(*uiDispatcher)
	disp.Mount(compo)

	if updateHistory {
		Window().addHistory(u)
	} else {
		lastURLVisited = u
	}

	disp.Nav(u)
	if isFragmentNavigation(u) {
		disp.Dispatch(func() {
			Window().ScrollToID(u.Fragment)
		})
	}
}

func isExternalNavigation(u *url.URL) bool {
	return u.Host != "" && u.Host != Window().URL().Host
}

func isFragmentNavigation(u *url.URL) bool {
	return u.Fragment != ""
}

func onAppUpdate(d *uiDispatcher) func(this Value, args []Value) interface{} {
	return func(this Value, args []Value) interface{} {
		d.Dispatch(func() {
			appUpdateAvailable = true
		})
		d.AppUpdate()
		d.Dispatch(func() {
			fmt.Println("app has been updated, reload to see changes")
		})
		return nil
	}
}

func onAppResize(ctx Context, e Event) {
	if d, ok := ctx.dispatcher.(*uiDispatcher); ok {
		d.AppResize()
	}
}

func onAppOrientationChange(ctx Context, e Event) {
	if d, ok := ctx.dispatcher.(*uiDispatcher); ok {
		go func() {
			time.Sleep(orientationChangeDelay)
			d.AppResize()
		}()
	}
}
<|endoftext|>


<commit_before>package ffi

// #include "ffi.h"
// typedef void (*_go_ffi_fctptr_t)(void);
import "C"

import (
	"fmt"
	"reflect"
	"unsafe"

	"bitbucket.org/binet/go-ffi/pkg/dl"
)

// Abi is the ffi abi of the local plateform
type Abi C.ffi_abi

const (
	FirstAbi   Abi = C.FFI_FIRST_ABI
	DefaultAbi Abi = C.FFI_DEFAULT_ABI
	LastAbi    Abi = C.FFI_LAST_ABI
)

const (
	TrampolineSize = C.FFI_TRAMPOLINE_SIZE
	NativeRawApi   = C.FFI_NATIVE_RAW_API

	//Closures          = C.FFI_CLOSURES
	//TypeSmallStruct1B = C.FFI_TYPE_SMALL_STRUCT_1B
	//TypeSmallStruct2B = C.FFI_TYPE_SMALL_STRUCT_2B
	//TypeSmallStruct4B = C.FFI_TYPE_SMALL_STRUCT_4B
)

// Type is a FFI type, describing functions' type arguments
type Type struct {
	c *C.ffi_type
}

var (
	Void       = Type{&C.ffi_type_void}
	Uchar      = Type{&C.ffi_type_uchar}
	Schar      = Type{&C.ffi_type_schar}
	Ushort     = Type{&C.ffi_type_ushort}
	Sshort     = Type{&C.ffi_type_sshort}
	Uint       = Type{&C.ffi_type_uint}
	Sint       = Type{&C.ffi_type_sint}
	Ulong      = Type{&C.ffi_type_ulong}
	Slong      = Type{&C.ffi_type_slong}
	Uint8      = Type{&C.ffi_type_uint8}
	Sint8      = Type{&C.ffi_type_sint8}
	Uint16     = Type{&C.ffi_type_uint16}
	Sint16     = Type{&C.ffi_type_sint16}
	Uint32     = Type{&C.ffi_type_uint32}
	Sint32     = Type{&C.ffi_type_sint32}
	Uint64     = Type{&C.ffi_type_uint64}
	Sint64     = Type{&C.ffi_type_sint64}
	Float      = Type{&C.ffi_type_float}
	Double     = Type{&C.ffi_type_double}
	LongDouble = Type{&C.ffi_type_longdouble}
	Pointer    = Type{&C.ffi_type_pointer}
)

type Status uint32

const (
	Ok         Status = C.FFI_OK
	BadTypedef Status = C.FFI_BAD_TYPEDEF
	BadAbi     Status = C.FFI_BAD_ABI
)

func (sc Status) String() string {
	switch sc {
	case Ok:
		return "FFI_OK"
	case BadTypedef:
		return "FFI_BAD_TYPEDEF"
	case BadAbi:
		return "FFI_BAD_ABI"
	}
	panic("unreachable")
}

// // Arg is a ffi argument
// type Arg struct {
// 	c C.ffi_arg
// }

// // SArg is a ffi argument
// type SArg struct {
// 	c C.ffi_sarg
// }

// Cif is the ffi call interface
type Cif struct {
	c C.ffi_cif
}

type FctPtr struct {
	c C._go_ffi_fctptr_t
}

// NewCif creates a new ffi call interface object
func NewCif(abi Abi, rtype Type, args []Type) (*Cif, error) {
	cif := &Cif{}
	c_nargs := C.uint(len(args))
	var c_args **C.ffi_type = nil
	if len(args) > 0 {
		var cargs = make([]*C.ffi_type, len(args))
		for i, _ := range args {
			cargs[i] = args[i].c
		}
		c_args = &cargs[0]
	}
	sc := C.ffi_prep_cif(&cif.c, C.ffi_abi(abi), c_nargs, rtype.c, c_args)
	if sc != C.FFI_OK {
		return nil, fmt.Errorf("error while preparing cif (%s)",
			Status(sc))
	}
	return cif, nil
}

// Call invokes the cif with the provided function pointer and arguments
func (cif *Cif) Call(fct FctPtr, args ...interface{}) (reflect.Value, error) {
	nargs := len(args)
	if nargs != int(cif.c.nargs) {
		return reflect.New(reflect.TypeOf(0)), fmt.Errorf("ffi: invalid number of arguments. expected '%d', got '%s'.",
			int(cif.c.nargs), nargs)
	}
	var c_args *unsafe.Pointer = nil
	if nargs > 0 {
		cargs := make([]unsafe.Pointer, nargs)
		for i, _ := range args {
			//fmt.Printf("[%d]: (%v)\n", i, args[i])
			cargs[i] = to_voidptr(args[i])
		}
		c_args = &cargs[0]
	}
	out := reflect.New(rtype_from_ffi(cif.c.rtype))
	var c_out unsafe.Pointer = unsafe.Pointer(out.Elem().UnsafeAddr())
	//println("...ffi_call...")
	C.ffi_call(&cif.c, fct.c, c_out, c_args)
	//fmt.Printf("...ffi_call...[done] [%v]\n",out.Elem())
	return out.Elem(), nil
}

func rtype_from_ffi(t *C.ffi_type) reflect.Type {
	switch t {
	case &C.ffi_type_uint:
		return reflect.TypeOf(uint(0))
	case &C.ffi_type_sint:
		return reflect.TypeOf(int(0))
	case &C.ffi_type_float:
		return reflect.TypeOf(float32(0))
	case &C.ffi_type_double:
		return reflect.TypeOf(float64(0))
	}
	panic("unreachable")
}

// void ffi_call(ffi_cif *cif,
// 	      void (*fn)(void),
// 	      void *rvalue,
// 	      void **avalue);

// Closure models a ffi closure
type Closure struct {
	c C.ffi_closure
}

// utils ---

// to_voidptr wraps an interface value into an unsafe.Pointer
func to_voidptr(v interface{}) unsafe.Pointer {
	t := reflect.TypeOf(v)
	rv := reflect.ValueOf(v)
	//println("-->",t.Kind().String())
	switch t.Kind() {
	case reflect.String:
		//fixme: memleak
		cstr := C.CString(rv.String())
		return unsafe.Pointer(&cstr)
	case reflect.Ptr:
		return unsafe.Pointer(rv.Elem().UnsafeAddr())
	case reflect.Float64:
		vv := rv.Float()
		rv = reflect.ValueOf(&vv)
		return unsafe.Pointer(rv.Elem().UnsafeAddr())
	}
	panic("to-voidptr: unreachable [" + t.Kind().String() + "]")
	return nil
}

// Library is a dl-opened library holding the corresponding dl.Handle
type Library struct {
	handle dl.Handle
}

func NewLibrary(libname string) (lib Library, err error) {
	lib.handle, err = dl.Open(libname, dl.Now)
	return
}

func (lib Library) Close() error {
	return lib.handle.Close()
}

// Function is a dl-loaded function from a dl-opened library
type Function func(args ...interface{}) reflect.Value

type cfct struct {
	addr unsafe.Pointer
}

var nil_fct Function = func(args ...interface{}) reflect.Value {
	panic("ffi: nil_fct called")
}

/*
func (lib Library) Fct(fctname string) (Function, error) {
	println("Fct(",fctname,")...")
	sym, err := lib.handle.Symbol(fctname)
	if err != nil {
		return nil_fct, err
	}

	addr := (C._go_ffi_fctptr_t)(unsafe.Pointer(sym))
	cif, err := NewCif(DefaultAbi, Double, []Type{Double})
	if err != nil {
		return nil_fct, err
	}

	fct := func(args ...interface{}) reflect.Value {
		println("...call.cif...")
		out, err := cif.Call(FctPtr{addr}, args...)
		if err != nil {
			panic(err)
		}
		println("...call.cif...[done]")
		return out
	}
	return Function(fct), nil
}
*/

func (lib Library) Fct(fctname string, rtype Type, argtypes []Type) (Function, error) {
	//println("Fct(",fctname,")...")
	sym, err := lib.handle.Symbol(fctname)
	if err != nil {
		return nil_fct, err
	}

	addr := (C._go_ffi_fctptr_t)(unsafe.Pointer(sym))
	cif, err := NewCif(DefaultAbi, rtype, argtypes)
	if err != nil {
		return nil_fct, err
	}

	fct := func(args ...interface{}) reflect.Value {
		//println("...call.cif...")
		out, err := cif.Call(FctPtr{addr}, args...)
		if err != nil {
			panic(err)
		}
		//println("...call.cif...[done]")
		return out
	}
	return Function(fct), nil
}

// EOF
<commit_msg>handle some more types<commit_after>package ffi

// #include <stdlib.h>
// #include "ffi.h"
// typedef void (*_go_ffi_fctptr_t)(void);
import "C"

import (
	"fmt"
	"reflect"
	"unsafe"

	"bitbucket.org/binet/go-ffi/pkg/dl"
)

// Abi is the ffi abi of the local plateform
type Abi C.ffi_abi

const (
	FirstAbi   Abi = C.FFI_FIRST_ABI
	DefaultAbi Abi = C.FFI_DEFAULT_ABI
	LastAbi    Abi = C.FFI_LAST_ABI
)

const (
	TrampolineSize = C.FFI_TRAMPOLINE_SIZE
	NativeRawApi   = C.FFI_NATIVE_RAW_API

	//Closures          = C.FFI_CLOSURES
	//TypeSmallStruct1B = C.FFI_TYPE_SMALL_STRUCT_1B
	//TypeSmallStruct2B = C.FFI_TYPE_SMALL_STRUCT_2B
	//TypeSmallStruct4B = C.FFI_TYPE_SMALL_STRUCT_4B
)

// Type is a FFI type, describing functions' type arguments
type Type struct {
	c *C.ffi_type
}

var (
	Void       = Type{&C.ffi_type_void}
	Uchar      = Type{&C.ffi_type_uchar}
	Schar      = Type{&C.ffi_type_schar}
	Ushort     = Type{&C.ffi_type_ushort}
	Sshort     = Type{&C.ffi_type_sshort}
	Uint       = Type{&C.ffi_type_uint}
	Sint       = Type{&C.ffi_type_sint}
	Ulong      = Type{&C.ffi_type_ulong}
	Slong      = Type{&C.ffi_type_slong}
	Uint8      = Type{&C.ffi_type_uint8}
	Sint8      = Type{&C.ffi_type_sint8}
	Uint16     = Type{&C.ffi_type_uint16}
	Sint16     = Type{&C.ffi_type_sint16}
	Uint32     = Type{&C.ffi_type_uint32}
	Sint32     = Type{&C.ffi_type_sint32}
	Uint64     = Type{&C.ffi_type_uint64}
	Sint64     = Type{&C.ffi_type_sint64}
	Float      = Type{&C.ffi_type_float}
	Double     = Type{&C.ffi_type_double}
	LongDouble = Type{&C.ffi_type_longdouble}
	Pointer    = Type{&C.ffi_type_pointer}
)

type Status uint32

const (
	Ok         Status = C.FFI_OK
	BadTypedef Status = C.FFI_BAD_TYPEDEF
	BadAbi     Status = C.FFI_BAD_ABI
)

func (sc Status) String() string {
	switch sc {
	case Ok:
		return "FFI_OK"
	case BadTypedef:
		return "FFI_BAD_TYPEDEF"
	case BadAbi:
		return "FFI_BAD_ABI"
	}
	panic("unreachable")
}

// // Arg is a ffi argument
// type Arg struct {
// 	c C.ffi_arg
// }

// // SArg is a ffi argument
// type SArg struct {
// 	c C.ffi_sarg
// }

// Cif is the ffi call interface
type Cif struct {
	c C.ffi_cif
}

type FctPtr struct {
	c C._go_ffi_fctptr_t
}

// NewCif creates a new ffi call interface object
func NewCif(abi Abi, rtype Type, args []Type) (*Cif, error) {
	cif := &Cif{}
	c_nargs := C.uint(len(args))
	var c_args **C.ffi_type = nil
	if len(args) > 0 {
		var cargs = make([]*C.ffi_type, len(args))
		for i, _ := range args {
			cargs[i] = args[i].c
		}
		c_args = &cargs[0]
	}
	sc := C.ffi_prep_cif(&cif.c, C.ffi_abi(abi), c_nargs, rtype.c, c_args)
	if sc != C.FFI_OK {
		return nil, fmt.Errorf("error while preparing cif (%s)",
			Status(sc))
	}
	return cif, nil
}

// Call invokes the cif with the provided function pointer and arguments
func (cif *Cif) Call(fct FctPtr, args ...interface{}) (reflect.Value, error) {
	nargs := len(args)
	if nargs != int(cif.c.nargs) {
		return reflect.New(reflect.TypeOf(0)), fmt.Errorf("ffi: invalid number of arguments. expected '%d', got '%s'.",
			int(cif.c.nargs), nargs)
	}
	var c_args *unsafe.Pointer = nil
	if nargs > 0 {
		cargs := make([]unsafe.Pointer, nargs)
		for i, _ := range args {
			var carg unsafe.Pointer
			//fmt.Printf("[%d]: (%v)\n", i, args[i])
			t := reflect.TypeOf(args[i])
			rv := reflect.ValueOf(args[i])
			switch t.Kind() {
			case reflect.String:
				cstr := C.CString(args[i].(string))
				defer C.free(unsafe.Pointer(cstr))
				carg = unsafe.Pointer(&cstr)
			case reflect.Ptr:
				carg = unsafe.Pointer(rv.Elem().UnsafeAddr())
			case reflect.Float32:
				vv := args[i].(float32)
				rv = reflect.ValueOf(&vv)
				carg = unsafe.Pointer(rv.Elem().UnsafeAddr())
			case reflect.Float64:
				vv := args[i].(float64)
				rv = reflect.ValueOf(&vv)
				carg = unsafe.Pointer(rv.Elem().UnsafeAddr())
			case reflect.Int:
				vv := args[i].(int)
				rv = reflect.ValueOf(&vv)
				carg = unsafe.Pointer(rv.Elem().UnsafeAddr())
			case reflect.Int8:
				vv := args[i].(int8)
				rv = reflect.ValueOf(&vv)
				carg = unsafe.Pointer(rv.Elem().UnsafeAddr())
			case reflect.Int16:
				vv := args[i].(int16)
				rv = reflect.ValueOf(&vv)
				carg = unsafe.Pointer(rv.Elem().UnsafeAddr())
			case reflect.Int32:
				vv := args[i].(int32)
				rv = reflect.ValueOf(&vv)
				carg = unsafe.Pointer(rv.Elem().UnsafeAddr())
			case reflect.Int64:
				vv := args[i].(int64)
				rv = reflect.ValueOf(&vv)
				carg = unsafe.Pointer(rv.Elem().UnsafeAddr())
			case reflect.Uint:
				vv := args[i].(uint)
				rv = reflect.ValueOf(&vv)
				carg = unsafe.Pointer(rv.Elem().UnsafeAddr())
			case reflect.Uint8:
				vv := args[i].(uint8)
				rv = reflect.ValueOf(&vv)
				carg = unsafe.Pointer(rv.Elem().UnsafeAddr())
			case reflect.Uint16:
				vv := args[i].(uint16)
				rv = reflect.ValueOf(&vv)
				carg = unsafe.Pointer(rv.Elem().UnsafeAddr())
			case reflect.Uint32:
				vv := args[i].(uint32)
				rv = reflect.ValueOf(&vv)
				carg = unsafe.Pointer(rv.Elem().UnsafeAddr())
			case reflect.Uint64:
				vv := args[i].(uint64)
				rv = reflect.ValueOf(&vv)
				carg = unsafe.Pointer(rv.Elem().UnsafeAddr())
			}
			cargs[i] = carg
		}
		c_args = &cargs[0]
	}
	out := reflect.New(rtype_from_ffi(cif.c.rtype))
	var c_out unsafe.Pointer = unsafe.Pointer(out.Elem().UnsafeAddr())
	//println("...ffi_call...")
	C.ffi_call(&cif.c, fct.c, c_out, c_args)
	//fmt.Printf("...ffi_call...[done] [%v]\n",out.Elem())
	return out.Elem(), nil
}

type go_void struct {}

func rtype_from_ffi(t *C.ffi_type) reflect.Type {
	switch t {
	case &C.ffi_type_void:
		return reflect.TypeOf(go_void{})
	case &C.ffi_type_uint:
		return reflect.TypeOf(uint(0))
	case &C.ffi_type_sint:
		return reflect.TypeOf(int(0))
	case &C.ffi_type_uint8:
		return reflect.TypeOf(uint8(0))
	case &C.ffi_type_sint8:
		return reflect.TypeOf(int8(0))
	case &C.ffi_type_uint16:
		return reflect.TypeOf(uint16(0))
	case &C.ffi_type_sint16:
		return reflect.TypeOf(int16(0))
	case &C.ffi_type_uint32:
		return reflect.TypeOf(uint32(0))
	case &C.ffi_type_sint32:
		return reflect.TypeOf(int32(0))
	case &C.ffi_type_uint64:
		return reflect.TypeOf(uint64(0))
	case &C.ffi_type_sint64:
		return reflect.TypeOf(int64(0))
	case &C.ffi_type_ulong:
		return reflect.TypeOf(uint64(0))
	case &C.ffi_type_slong:
		return reflect.TypeOf(int64(0))
	case &C.ffi_type_float:
		return reflect.TypeOf(float32(0))
	case &C.ffi_type_double:
		return reflect.TypeOf(float64(0))
	case &C.ffi_type_longdouble:
		// FIXME!!
		return reflect.TypeOf(complex128(0))
	}
	panic("unreachable")
}

// void ffi_call(ffi_cif *cif,
// 	      void (*fn)(void),
// 	      void *rvalue,
// 	      void **avalue);

// Closure models a ffi closure
type Closure struct {
	c C.ffi_closure
}

// utils ---

// to_voidptr wraps an interface value into an unsafe.Pointer
func to_voidptr(v interface{}) unsafe.Pointer {
	t := reflect.TypeOf(v)
	rv := reflect.ValueOf(v)
	//println("-->",t.Kind().String())
	switch t.Kind() {
	case reflect.String:
		//fixme: memleak
		cstr := C.CString(rv.String())
		return unsafe.Pointer(&cstr)
	case reflect.Ptr:
		return unsafe.Pointer(rv.Elem().UnsafeAddr())
	case reflect.Float32:
		vv := rv.Float()
		rv = reflect.ValueOf(&vv)
		return unsafe.Pointer(rv.Elem().UnsafeAddr())
	case reflect.Float64:
		vv := rv.Float()
		rv = reflect.ValueOf(&vv)
		return unsafe.Pointer(rv.Elem().UnsafeAddr())
	}
	panic("to-voidptr: unreachable [" + t.Kind().String() + "]")
	return nil
}

// Library is a dl-opened library holding the corresponding dl.Handle
type Library struct {
	handle dl.Handle
}

func NewLibrary(libname string) (lib Library, err error) {
	lib.handle, err = dl.Open(libname, dl.Now)
	return
}

func (lib Library) Close() error {
	return lib.handle.Close()
}

// Function is a dl-loaded function from a dl-opened library
type Function func(args ...interface{}) reflect.Value

type cfct struct {
	addr unsafe.Pointer
}

var nil_fct Function = func(args ...interface{}) reflect.Value {
	panic("ffi: nil_fct called")
}

/*
func (lib Library) Fct(fctname string) (Function, error) {
	println("Fct(",fctname,")...")
	sym, err := lib.handle.Symbol(fctname)
	if err != nil {
		return nil_fct, err
	}

	addr := (C._go_ffi_fctptr_t)(unsafe.Pointer(sym))
	cif, err := NewCif(DefaultAbi, Double, []Type{Double})
	if err != nil {
		return nil_fct, err
	}

	fct := func(args ...interface{}) reflect.Value {
		println("...call.cif...")
		out, err := cif.Call(FctPtr{addr}, args...)
		if err != nil {
			panic(err)
		}
		println("...call.cif...[done]")
		return out
	}
	return Function(fct), nil
}
*/

func (lib Library) Fct(fctname string, rtype Type, argtypes []Type) (Function, error) {
	//println("Fct(",fctname,")...")
	sym, err := lib.handle.Symbol(fctname)
	if err != nil {
		return nil_fct, err
	}

	addr := (C._go_ffi_fctptr_t)(unsafe.Pointer(sym))
	cif, err := NewCif(DefaultAbi, rtype, argtypes)
	if err != nil {
		return nil_fct, err
	}

	fct := func(args ...interface{}) reflect.Value {
		//println("...call.cif...")
		out, err := cif.Call(FctPtr{addr}, args...)
		if err != nil {
			panic(err)
		}
		//println("...call.cif...[done]")
		return out
	}
	return Function(fct), nil
}

// EOF
<|endoftext|>


<commit_before>package rpmdb

import (
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"path/filepath"

	"golang.org/x/xerrors"
)

type PackageInfo struct {
	Epoch           int
	Name            string
	Version         string
	Release         string
	Arch            string
	SourceRpm       string
	Size            int
	License         string
	Vendor          string
	Modularitylabel string

	BaseNames  []string
	DirIndexes []int
	DirNames   []string
}

// ref. https://github.com/rpm-software-management/rpm/blob/rpm-4.14.3-release/lib/tagexts.c#L752
func getNEVRA(indexEntries []indexEntry) (*PackageInfo, error) {
	pkgInfo := &PackageInfo{}
	for _, ie := range indexEntries {
		switch ie.Info.Tag {
		case RPMTAG_DIRINDEXES:
			if ie.Info.Type != RPM_INT32_TYPE {
				return nil, xerrors.New("invalid tag dir indexes")
			}

			indexes, err := int32Array(ie)
			if err != nil {
				return nil, xerrors.Errorf("unable to read dir indexes: %w", err)
			}
			pkgInfo.DirIndexes = indexes
		case RPMTAG_DIRNAMES:
			if ie.Info.Type != RPM_STRING_ARRAY_TYPE {
				return nil, xerrors.New("invalid tag dir names")
			}

			dirNames, err := stringArray(ie)
			if err != nil {
				return nil, xerrors.Errorf("unable to read dir names: %w", err)
			}
			pkgInfo.DirNames = dirNames
		case RPMTAG_BASENAMES:
			if ie.Info.Type != RPM_STRING_ARRAY_TYPE {
				return nil, xerrors.New("invalid tag base names")
			}

			baseNames, err := stringArray(ie)
			if err != nil {
				return nil, xerrors.Errorf("unable to read dir names: %w", err)
			}
			pkgInfo.BaseNames = baseNames
		case RPMTAG_MODULARITYLABEL:
			if ie.Info.Type != RPM_STRING_TYPE {
				return nil, xerrors.New("invalid tag modularitylabel")
			}
			pkgInfo.Modularitylabel = string(bytes.TrimRight(ie.Data, "\x00"))
		case RPMTAG_NAME:
			if ie.Info.Type != RPM_STRING_TYPE {
				return nil, xerrors.New("invalid tag name")
			}
			pkgInfo.Name = string(bytes.TrimRight(ie.Data, "\x00"))
		case RPMTAG_EPOCH:
			if ie.Info.Type != RPM_INT32_TYPE {
				return nil, xerrors.New("invalid tag epoch")
			}

			var epoch int32
			reader := bytes.NewReader(ie.Data)
			if err := binary.Read(reader, binary.BigEndian, &epoch); err != nil {
				return nil, xerrors.Errorf("failed to read binary (epoch): %w", err)
			}
			pkgInfo.Epoch = int(epoch)
		case RPMTAG_VERSION:
			if ie.Info.Type != RPM_STRING_TYPE {
				return nil, xerrors.New("invalid tag version")
			}
			pkgInfo.Version = string(bytes.TrimRight(ie.Data, "\x00"))
		case RPMTAG_RELEASE:
			if ie.Info.Type != RPM_STRING_TYPE {
				return nil, xerrors.New("invalid tag release")
			}
			pkgInfo.Release = string(bytes.TrimRight(ie.Data, "\x00"))
		case RPMTAG_ARCH:
			if ie.Info.Type != RPM_STRING_TYPE {
				return nil, xerrors.New("invalid tag arch")
			}
			pkgInfo.Arch = string(bytes.TrimRight(ie.Data, "\x00"))
		case RPMTAG_SOURCERPM:
			if ie.Info.Type != RPM_STRING_TYPE {
				return nil, xerrors.New("invalid tag sourcerpm")
			}
			pkgInfo.SourceRpm = string(bytes.TrimRight(ie.Data, "\x00"))
			if pkgInfo.SourceRpm == "(none)" {
				pkgInfo.SourceRpm = ""
			}
		case RPMTAG_LICENSE:
			if ie.Info.Type != RPM_STRING_TYPE {
				return nil, xerrors.New("invalid tag license")
			}
			pkgInfo.License = string(bytes.TrimRight(ie.Data, "\x00"))
			if pkgInfo.License == "(none)" {
				pkgInfo.License = ""
			}
		case RPMTAG_VENDOR:
			if ie.Info.Type != RPM_STRING_TYPE {
				return nil, xerrors.New("invalid tag vendor")
			}
			pkgInfo.Vendor = string(bytes.TrimRight(ie.Data, "\x00"))
			if pkgInfo.Vendor == "(none)" {
				pkgInfo.Vendor = ""
			}
		case RPMTAG_SIZE:
			if ie.Info.Type != RPM_INT32_TYPE {
				return nil, xerrors.New("invalid tag size")
			}

			var size int32
			reader := bytes.NewReader(ie.Data)
			if err := binary.Read(reader, binary.BigEndian, &size); err != nil {
				return nil, xerrors.Errorf("failed to read binary (size): %w", err)
			}
			pkgInfo.Size = int(size)
		}

	}
	return pkgInfo, nil
}

func int32Array(ie indexEntry) ([]int, error) {
	var values []int
	reader := bytes.NewReader(ie.Data)
	for i := 0; i < int(ie.Info.Count); i++ {
		var value int32
		err := binary.Read(reader, binary.BigEndian, &value)
		if errors.Is(err, io.EOF) {
			break
		} else if err != nil {
			return nil, xerrors.Errorf("failed to read int32: %w", err)
		}
		values = append(values, int(value))
	}

	return values, nil
}

func stringArray(ie indexEntry) ([]string, error) {
	var values []string
	var data []byte
	for _, b := range ie.Data {
		// NULL terminates the string.
		if b == 0 {
			values = append(values, string(data))
			data = []byte{}
			continue
		}
		data = append(data, b)
	}

	return values, nil
}

func (p *PackageInfo) InstalledFiles() ([]string, error) {
	// ref. https://github.com/rpm-software-management/rpm/blob/rpm-4.14.3-release/lib/tagexts.c#L68-L70
	if len(p.DirIndexes) != len(p.BaseNames) || len(p.DirNames) < 1 || len(p.DirNames) > len(p.BaseNames) {
		return nil, xerrors.Errorf("invalid rpm %s", p.Name)
	}

	var filePaths []string
	for i, baseName := range p.BaseNames {
		dir := p.DirNames[p.DirIndexes[i]]
		filePaths = append(filePaths, filepath.Join(dir, baseName))
	}

	return filePaths, nil

}
<commit_msg>fix(package): skip empty installed files (#8)<commit_after>package rpmdb

import (
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"path/filepath"

	"golang.org/x/xerrors"
)

type PackageInfo struct {
	Epoch           int
	Name            string
	Version         string
	Release         string
	Arch            string
	SourceRpm       string
	Size            int
	License         string
	Vendor          string
	Modularitylabel string

	BaseNames  []string
	DirIndexes []int
	DirNames   []string
}

// ref. https://github.com/rpm-software-management/rpm/blob/rpm-4.14.3-release/lib/tagexts.c#L752
func getNEVRA(indexEntries []indexEntry) (*PackageInfo, error) {
	pkgInfo := &PackageInfo{}
	for _, ie := range indexEntries {
		switch ie.Info.Tag {
		case RPMTAG_DIRINDEXES:
			if ie.Info.Type != RPM_INT32_TYPE {
				return nil, xerrors.New("invalid tag dir indexes")
			}

			indexes, err := int32Array(ie)
			if err != nil {
				return nil, xerrors.Errorf("unable to read dir indexes: %w", err)
			}
			pkgInfo.DirIndexes = indexes
		case RPMTAG_DIRNAMES:
			if ie.Info.Type != RPM_STRING_ARRAY_TYPE {
				return nil, xerrors.New("invalid tag dir names")
			}

			dirNames, err := stringArray(ie)
			if err != nil {
				return nil, xerrors.Errorf("unable to read dir names: %w", err)
			}
			pkgInfo.DirNames = dirNames
		case RPMTAG_BASENAMES:
			if ie.Info.Type != RPM_STRING_ARRAY_TYPE {
				return nil, xerrors.New("invalid tag base names")
			}

			baseNames, err := stringArray(ie)
			if err != nil {
				return nil, xerrors.Errorf("unable to read dir names: %w", err)
			}
			pkgInfo.BaseNames = baseNames
		case RPMTAG_MODULARITYLABEL:
			if ie.Info.Type != RPM_STRING_TYPE {
				return nil, xerrors.New("invalid tag modularitylabel")
			}
			pkgInfo.Modularitylabel = string(bytes.TrimRight(ie.Data, "\x00"))
		case RPMTAG_NAME:
			if ie.Info.Type != RPM_STRING_TYPE {
				return nil, xerrors.New("invalid tag name")
			}
			pkgInfo.Name = string(bytes.TrimRight(ie.Data, "\x00"))
		case RPMTAG_EPOCH:
			if ie.Info.Type != RPM_INT32_TYPE {
				return nil, xerrors.New("invalid tag epoch")
			}

			var epoch int32
			reader := bytes.NewReader(ie.Data)
			if err := binary.Read(reader, binary.BigEndian, &epoch); err != nil {
				return nil, xerrors.Errorf("failed to read binary (epoch): %w", err)
			}
			pkgInfo.Epoch = int(epoch)
		case RPMTAG_VERSION:
			if ie.Info.Type != RPM_STRING_TYPE {
				return nil, xerrors.New("invalid tag version")
			}
			pkgInfo.Version = string(bytes.TrimRight(ie.Data, "\x00"))
		case RPMTAG_RELEASE:
			if ie.Info.Type != RPM_STRING_TYPE {
				return nil, xerrors.New("invalid tag release")
			}
			pkgInfo.Release = string(bytes.TrimRight(ie.Data, "\x00"))
		case RPMTAG_ARCH:
			if ie.Info.Type != RPM_STRING_TYPE {
				return nil, xerrors.New("invalid tag arch")
			}
			pkgInfo.Arch = string(bytes.TrimRight(ie.Data, "\x00"))
		case RPMTAG_SOURCERPM:
			if ie.Info.Type != RPM_STRING_TYPE {
				return nil, xerrors.New("invalid tag sourcerpm")
			}
			pkgInfo.SourceRpm = string(bytes.TrimRight(ie.Data, "\x00"))
			if pkgInfo.SourceRpm == "(none)" {
				pkgInfo.SourceRpm = ""
			}
		case RPMTAG_LICENSE:
			if ie.Info.Type != RPM_STRING_TYPE {
				return nil, xerrors.New("invalid tag license")
			}
			pkgInfo.License = string(bytes.TrimRight(ie.Data, "\x00"))
			if pkgInfo.License == "(none)" {
				pkgInfo.License = ""
			}
		case RPMTAG_VENDOR:
			if ie.Info.Type != RPM_STRING_TYPE {
				return nil, xerrors.New("invalid tag vendor")
			}
			pkgInfo.Vendor = string(bytes.TrimRight(ie.Data, "\x00"))
			if pkgInfo.Vendor == "(none)" {
				pkgInfo.Vendor = ""
			}
		case RPMTAG_SIZE:
			if ie.Info.Type != RPM_INT32_TYPE {
				return nil, xerrors.New("invalid tag size")
			}

			var size int32
			reader := bytes.NewReader(ie.Data)
			if err := binary.Read(reader, binary.BigEndian, &size); err != nil {
				return nil, xerrors.Errorf("failed to read binary (size): %w", err)
			}
			pkgInfo.Size = int(size)
		}

	}
	return pkgInfo, nil
}

func int32Array(ie indexEntry) ([]int, error) {
	var values []int
	reader := bytes.NewReader(ie.Data)
	for i := 0; i < int(ie.Info.Count); i++ {
		var value int32
		err := binary.Read(reader, binary.BigEndian, &value)
		if errors.Is(err, io.EOF) {
			break
		} else if err != nil {
			return nil, xerrors.Errorf("failed to read int32: %w", err)
		}
		values = append(values, int(value))
	}

	return values, nil
}

func stringArray(ie indexEntry) ([]string, error) {
	var values []string
	var data []byte
	for _, b := range ie.Data {
		// NULL terminates the string.
		if b == 0 {
			values = append(values, string(data))
			data = []byte{}
			continue
		}
		data = append(data, b)
	}

	return values, nil
}

func (p *PackageInfo) InstalledFiles() ([]string, error) {
	if len(p.DirNames) == 0 || len(p.DirIndexes) == 0 || len(p.BaseNames) == 0 {
		return nil, nil
	}

	// ref. https://github.com/rpm-software-management/rpm/blob/rpm-4.14.3-release/lib/tagexts.c#L68-L70
	if len(p.DirIndexes) != len(p.BaseNames) || len(p.DirNames) > len(p.BaseNames) {
		return nil, xerrors.Errorf("invalid rpm %s", p.Name)
	}

	var filePaths []string
	for i, baseName := range p.BaseNames {
		dir := p.DirNames[p.DirIndexes[i]]
		filePaths = append(filePaths, filepath.Join(dir, baseName))
	}

	return filePaths, nil

}
<|endoftext|>


<commit_before>package mpb

import (
	"bytes"
	"fmt"
	"io"
	"strings"
	"sync"
	"time"
	"unicode/utf8"

	"github.com/vbauerster/mpb/decor"
)

const (
	rLeft = iota
	rFill
	rTip
	rEmpty
	rRight
)

const (
	formatLen = 5
	etaAlpha  = 0.12
)

type barRunes [formatLen]rune

// Bar represents a progress Bar
type Bar struct {
	priority int
	index    int

	runningBar    *Bar
	cacheState    *bState
	operateState  chan func(*bState)
	frameReaderCh chan io.Reader

	// done is closed by Bar's goroutine, after cacheState is written
	done chan struct{}
	// shutdown is closed from master Progress goroutine only
	shutdown chan struct{}
}

type (
	bState struct {
		id                   int
		width                int
		runes                barRunes
		etaAlpha             float64
		total                int64
		current              int64
		totalAutoIncrTrigger int64
		totalAutoIncrBy      int64
		trimLeftSpace        bool
		trimRightSpace       bool
		toComplete           bool
		dynamic              bool
		removeOnComplete     bool
		barClearOnComplete   bool
		completeFlushed      bool
		startTime            time.Time
		blockStartTime       time.Time
		timeElapsed          time.Duration
		timePerItemEstimate  time.Duration
		timeRemaining        time.Duration
		aDecorators          []decor.DecoratorFunc
		pDecorators          []decor.DecoratorFunc
		refill               *refill
		bufP, bufB, bufA     *bytes.Buffer
		panicMsg             string

		// following options are assigned to the *Bar
		priority   int
		runningBar *Bar
	}
	refill struct {
		char rune
		till int64
	}
	frameReader struct {
		io.Reader
		toShutdown       bool
		removeOnComplete bool
	}
)

func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, options ...BarOption) *Bar {
	dynamic := total <= 0
	if dynamic {
		total = time.Now().Unix()
	}

	s := &bState{
		id:       id,
		priority: id,
		total:    total,
		etaAlpha: etaAlpha,
		dynamic:  dynamic,
	}

	for _, opt := range options {
		if opt != nil {
			opt(s)
		}
	}

	s.bufP = bytes.NewBuffer(make([]byte, 0, s.width))
	s.bufB = bytes.NewBuffer(make([]byte, 0, s.width))
	s.bufA = bytes.NewBuffer(make([]byte, 0, s.width))

	b := &Bar{
		priority:      s.priority,
		runningBar:    s.runningBar,
		operateState:  make(chan func(*bState)),
		frameReaderCh: make(chan io.Reader, 1),
		done:          make(chan struct{}),
		shutdown:      make(chan struct{}),
	}

	if b.runningBar != nil {
		b.priority = b.runningBar.priority
	}

	go b.serve(wg, s, cancel)
	return b
}

// RemoveAllPrependers removes all prepend functions
func (b *Bar) RemoveAllPrependers() {
	select {
	case b.operateState <- func(s *bState) { s.pDecorators = nil }:
	case <-b.done:
	}
}

// RemoveAllAppenders removes all append functions
func (b *Bar) RemoveAllAppenders() {
	select {
	case b.operateState <- func(s *bState) { s.aDecorators = nil }:
	case <-b.done:
	}
}

// ProxyReader wrapper for io operations, like io.Copy
func (b *Bar) ProxyReader(r io.Reader) *Reader {
	return &Reader{r, b}
}

// Increment is a shorthand for b.IncrBy(1)
func (b *Bar) Increment() {
	b.IncrBy(1)
}

// ResumeFill fills bar with different r rune,
// from 0 to till amount of progress.
func (b *Bar) ResumeFill(r rune, till int64) {
	if till < 1 {
		return
	}
	select {
	case b.operateState <- func(s *bState) { s.refill = &refill{r, till} }:
	case <-b.done:
	}
}

// NumOfAppenders returns current number of append decorators
func (b *Bar) NumOfAppenders() int {
	result := make(chan int)
	select {
	case b.operateState <- func(s *bState) { result <- len(s.aDecorators) }:
		return <-result
	case <-b.done:
		return len(b.cacheState.aDecorators)
	}
}

// NumOfPrependers returns current number of prepend decorators
func (b *Bar) NumOfPrependers() int {
	result := make(chan int)
	select {
	case b.operateState <- func(s *bState) { result <- len(s.pDecorators) }:
		return <-result
	case <-b.done:
		return len(b.cacheState.pDecorators)
	}
}

// ID returs id of the bar
func (b *Bar) ID() int {
	result := make(chan int)
	select {
	case b.operateState <- func(s *bState) { result <- s.id }:
		return <-result
	case <-b.done:
		return b.cacheState.id
	}
}

// Current returns bar's current number, in other words sum of all increments.
func (b *Bar) Current() int64 {
	result := make(chan int64)
	select {
	case b.operateState <- func(s *bState) { result <- s.current }:
		return <-result
	case <-b.done:
		return b.cacheState.current
	}
}

// Total returns bar's total number.
func (b *Bar) Total() int64 {
	result := make(chan int64)
	select {
	case b.operateState <- func(s *bState) { result <- s.total }:
		return <-result
	case <-b.done:
		return b.cacheState.total
	}
}

// SetTotal sets total dynamically. The final param indicates the very last set,
// in other words you should set it to true when total is determined.
func (b *Bar) SetTotal(total int64, final bool) {
	select {
	case b.operateState <- func(s *bState) {
		if total != 0 {
			s.total = total
		}
		s.dynamic = !final
		s.toComplete = final
	}:
	case <-b.done:
	}
}

// StartBlock updates start timestamp of the current increment block.
// It is optional to call, unless ETA decorator is used.
// If *bar.ProxyReader is used, it will be called implicitly.
func (b *Bar) StartBlock() {
	now := time.Now()
	select {
	case b.operateState <- func(s *bState) {
		if s.current == 0 {
			s.startTime = now
		}
		s.blockStartTime = now
	}:
	case <-b.done:
	}
}

// IncrBy increments progress bar by amount of n
func (b *Bar) IncrBy(n int) {
	now := time.Now()
	select {
	case b.operateState <- func(s *bState) {
		s.current += int64(n)
		s.timeElapsed = now.Sub(s.startTime)
		s.timeRemaining = s.calcETA(n, now.Sub(s.blockStartTime))
		if s.dynamic {
			curp := decor.CalcPercentage(s.total, s.current, 100)
			if 100-curp <= s.totalAutoIncrTrigger {
				s.total += s.totalAutoIncrBy
			}
		} else if s.current >= s.total {
			s.current = s.total
			s.toComplete = true
		}
	}:
	case <-b.done:
	}
}

// Completed reports whether the bar is in completed state
func (b *Bar) Completed() bool {
	result := make(chan bool)
	select {
	case b.operateState <- func(s *bState) { result <- s.toComplete }:
		return <-result
	case <-b.done:
		return b.cacheState.toComplete
	}
}

func (b *Bar) serve(wg *sync.WaitGroup, s *bState, cancel <-chan struct{}) {
	defer wg.Done()
	s.startTime = time.Now()
	s.blockStartTime = s.startTime
	for {
		select {
		case op := <-b.operateState:
			op(s)
		case <-cancel:
			s.toComplete = true
			cancel = nil
		case <-b.shutdown:
			b.cacheState = s
			close(b.done)
			return
		}
	}
}

func (b *Bar) render(debugOut io.Writer, tw int, pSyncer, aSyncer *widthSyncer) {
	var r io.Reader
	select {
	case b.operateState <- func(s *bState) {
		defer func() {
			// recovering if external decorators panic
			if p := recover(); p != nil {
				s.panicMsg = fmt.Sprintf("panic: %v", p)
				s.pDecorators = nil
				s.aDecorators = nil
				s.toComplete = true
				// truncate panic msg to one tw line, if necessary
				r = strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", tw), s.panicMsg))
				fmt.Fprintf(debugOut, "%s %s bar id %02d %v\n", "[mpb]", time.Now(), s.id, s.panicMsg)
			}
			b.frameReaderCh <- &frameReader{
				Reader:           r,
				toShutdown:       s.toComplete && !s.completeFlushed,
				removeOnComplete: s.removeOnComplete,
			}
			s.completeFlushed = s.toComplete
		}()
		r = s.draw(tw, pSyncer, aSyncer)
	}:
	case <-b.done:
		s := b.cacheState
		if s.panicMsg != "" {
			r = strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", tw), s.panicMsg))
		} else {
			r = s.draw(tw, pSyncer, aSyncer)
		}
		b.frameReaderCh <- &frameReader{
			Reader: r,
		}
	}
}

func (s *bState) draw(termWidth int, pSyncer, aSyncer *widthSyncer) io.Reader {
	defer s.bufA.WriteByte('\n')

	if termWidth <= 0 {
		termWidth = s.width
	}

	stat := newStatistics(s)

	// render prepend functions to the left of the bar
	for i, f := range s.pDecorators {
		s.bufP.WriteString(f(stat, pSyncer.Accumulator[i], pSyncer.Distributor[i]))
	}

	for i, f := range s.aDecorators {
		s.bufA.WriteString(f(stat, aSyncer.Accumulator[i], aSyncer.Distributor[i]))
	}

	prependCount := utf8.RuneCount(s.bufP.Bytes())
	appendCount := utf8.RuneCount(s.bufA.Bytes())

	if s.barClearOnComplete && s.completeFlushed {
		return io.MultiReader(s.bufP, s.bufA)
	}

	s.fillBar(s.width)
	barCount := utf8.RuneCount(s.bufB.Bytes())
	totalCount := prependCount + barCount + appendCount
	if spaceCount := 0; totalCount > termWidth {
		if !s.trimLeftSpace {
			spaceCount++
		}
		if !s.trimRightSpace {
			spaceCount++
		}
		s.fillBar(termWidth - prependCount - appendCount - spaceCount)
	}

	return io.MultiReader(s.bufP, s.bufB, s.bufA)
}

func (s *bState) fillBar(width int) {
	defer func() {
		s.bufB.WriteRune(s.runes[rRight])
		if !s.trimRightSpace {
			s.bufB.WriteByte(' ')
		}
	}()

	s.bufB.Reset()
	if !s.trimLeftSpace {
		s.bufB.WriteByte(' ')
	}
	s.bufB.WriteRune(s.runes[rLeft])
	if width <= 2 {
		return
	}

	// bar s.width without leftEnd and rightEnd runes
	barWidth := width - 2

	completedWidth := decor.CalcPercentage(s.total, s.current, int64(barWidth))

	if s.refill != nil {
		till := decor.CalcPercentage(s.total, s.refill.till, int64(barWidth))
		// append refill rune
		var i int64
		for i = 0; i < till; i++ {
			s.bufB.WriteRune(s.refill.char)
		}
		for i = till; i < completedWidth; i++ {
			s.bufB.WriteRune(s.runes[rFill])
		}
	} else {
		var i int64
		for i = 0; i < completedWidth; i++ {
			s.bufB.WriteRune(s.runes[rFill])
		}
	}

	if completedWidth < int64(barWidth) && completedWidth > 0 {
		_, size := utf8.DecodeLastRune(s.bufB.Bytes())
		s.bufB.Truncate(s.bufB.Len() - size)
		s.bufB.WriteRune(s.runes[rTip])
	}

	for i := completedWidth; i < int64(barWidth); i++ {
		s.bufB.WriteRune(s.runes[rEmpty])
	}
}

func (s *bState) calcETA(n int, lastBlockTime time.Duration) time.Duration {
	lastItemEstimate := float64(lastBlockTime) / float64(n)
	s.timePerItemEstimate = time.Duration((s.etaAlpha * lastItemEstimate) + (1-s.etaAlpha)*float64(s.timePerItemEstimate))
	return time.Duration(s.total-s.current) * s.timePerItemEstimate
}

func newStatistics(s *bState) *decor.Statistics {
	return &decor.Statistics{
		ID:                  s.id,
		Completed:           s.completeFlushed,
		Total:               s.total,
		Current:             s.current,
		StartTime:           s.startTime,
		TimeElapsed:         s.timeElapsed,
		TimeRemaining:       s.timeRemaining,
		TimePerItemEstimate: s.timePerItemEstimate,
	}
}

func strToBarRunes(format string) (array barRunes) {
	for i, n := 0, 0; len(format) > 0; i++ {
		array[i], n = utf8.DecodeRuneInString(format)
		format = format[n:]
	}
	return
}
<commit_msg>dynamic total fix<commit_after>package mpb

import (
	"bytes"
	"fmt"
	"io"
	"strings"
	"sync"
	"time"
	"unicode/utf8"

	"github.com/vbauerster/mpb/decor"
)

const (
	rLeft = iota
	rFill
	rTip
	rEmpty
	rRight
)

const (
	formatLen = 5
	etaAlpha  = 0.12
)

type barRunes [formatLen]rune

// Bar represents a progress Bar
type Bar struct {
	priority int
	index    int

	runningBar    *Bar
	cacheState    *bState
	operateState  chan func(*bState)
	frameReaderCh chan io.Reader

	// done is closed by Bar's goroutine, after cacheState is written
	done chan struct{}
	// shutdown is closed from master Progress goroutine only
	shutdown chan struct{}
}

type (
	bState struct {
		id                   int
		width                int
		runes                barRunes
		etaAlpha             float64
		total                int64
		current              int64
		totalAutoIncrTrigger int64
		totalAutoIncrBy      int64
		trimLeftSpace        bool
		trimRightSpace       bool
		toComplete           bool
		dynamic              bool
		removeOnComplete     bool
		barClearOnComplete   bool
		completeFlushed      bool
		startTime            time.Time
		blockStartTime       time.Time
		timeElapsed          time.Duration
		timePerItemEstimate  time.Duration
		timeRemaining        time.Duration
		aDecorators          []decor.DecoratorFunc
		pDecorators          []decor.DecoratorFunc
		refill               *refill
		bufP, bufB, bufA     *bytes.Buffer
		panicMsg             string

		// following options are assigned to the *Bar
		priority   int
		runningBar *Bar
	}
	refill struct {
		char rune
		till int64
	}
	frameReader struct {
		io.Reader
		toShutdown       bool
		removeOnComplete bool
	}
)

func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, options ...BarOption) *Bar {
	dynamic := total <= 0
	if dynamic {
		total = time.Now().Unix()
	}

	s := &bState{
		id:       id,
		priority: id,
		total:    total,
		etaAlpha: etaAlpha,
		dynamic:  dynamic,
	}

	for _, opt := range options {
		if opt != nil {
			opt(s)
		}
	}

	s.bufP = bytes.NewBuffer(make([]byte, 0, s.width))
	s.bufB = bytes.NewBuffer(make([]byte, 0, s.width))
	s.bufA = bytes.NewBuffer(make([]byte, 0, s.width))

	b := &Bar{
		priority:      s.priority,
		runningBar:    s.runningBar,
		operateState:  make(chan func(*bState)),
		frameReaderCh: make(chan io.Reader, 1),
		done:          make(chan struct{}),
		shutdown:      make(chan struct{}),
	}

	if b.runningBar != nil {
		b.priority = b.runningBar.priority
	}

	go b.serve(wg, s, cancel)
	return b
}

// RemoveAllPrependers removes all prepend functions
func (b *Bar) RemoveAllPrependers() {
	select {
	case b.operateState <- func(s *bState) { s.pDecorators = nil }:
	case <-b.done:
	}
}

// RemoveAllAppenders removes all append functions
func (b *Bar) RemoveAllAppenders() {
	select {
	case b.operateState <- func(s *bState) { s.aDecorators = nil }:
	case <-b.done:
	}
}

// ProxyReader wrapper for io operations, like io.Copy
func (b *Bar) ProxyReader(r io.Reader) *Reader {
	return &Reader{r, b}
}

// Increment is a shorthand for b.IncrBy(1)
func (b *Bar) Increment() {
	b.IncrBy(1)
}

// ResumeFill fills bar with different r rune,
// from 0 to till amount of progress.
func (b *Bar) ResumeFill(r rune, till int64) {
	if till < 1 {
		return
	}
	select {
	case b.operateState <- func(s *bState) { s.refill = &refill{r, till} }:
	case <-b.done:
	}
}

// NumOfAppenders returns current number of append decorators
func (b *Bar) NumOfAppenders() int {
	result := make(chan int)
	select {
	case b.operateState <- func(s *bState) { result <- len(s.aDecorators) }:
		return <-result
	case <-b.done:
		return len(b.cacheState.aDecorators)
	}
}

// NumOfPrependers returns current number of prepend decorators
func (b *Bar) NumOfPrependers() int {
	result := make(chan int)
	select {
	case b.operateState <- func(s *bState) { result <- len(s.pDecorators) }:
		return <-result
	case <-b.done:
		return len(b.cacheState.pDecorators)
	}
}

// ID returs id of the bar
func (b *Bar) ID() int {
	result := make(chan int)
	select {
	case b.operateState <- func(s *bState) { result <- s.id }:
		return <-result
	case <-b.done:
		return b.cacheState.id
	}
}

// Current returns bar's current number, in other words sum of all increments.
func (b *Bar) Current() int64 {
	result := make(chan int64)
	select {
	case b.operateState <- func(s *bState) { result <- s.current }:
		return <-result
	case <-b.done:
		return b.cacheState.current
	}
}

// Total returns bar's total number.
func (b *Bar) Total() int64 {
	result := make(chan int64)
	select {
	case b.operateState <- func(s *bState) { result <- s.total }:
		return <-result
	case <-b.done:
		return b.cacheState.total
	}
}

// SetTotal sets total dynamically. The final param indicates the very last set,
// in other words you should set it to true when total is determined.
func (b *Bar) SetTotal(total int64, final bool) {
	select {
	case b.operateState <- func(s *bState) {
		if total != 0 {
			s.total = total
		}
		s.dynamic = !final
	}:
	case <-b.done:
	}
}

// StartBlock updates start timestamp of the current increment block.
// It is optional to call, unless ETA decorator is used.
// If *bar.ProxyReader is used, it will be called implicitly.
func (b *Bar) StartBlock() {
	now := time.Now()
	select {
	case b.operateState <- func(s *bState) {
		if s.current == 0 {
			s.startTime = now
		}
		s.blockStartTime = now
	}:
	case <-b.done:
	}
}

// IncrBy increments progress bar by amount of n
func (b *Bar) IncrBy(n int) {
	now := time.Now()
	select {
	case b.operateState <- func(s *bState) {
		s.current += int64(n)
		s.timeElapsed = now.Sub(s.startTime)
		s.timeRemaining = s.calcETA(n, now.Sub(s.blockStartTime))
		if s.dynamic {
			curp := decor.CalcPercentage(s.total, s.current, 100)
			if 100-curp <= s.totalAutoIncrTrigger {
				s.total += s.totalAutoIncrBy
			}
		} else if s.current >= s.total {
			s.current = s.total
			s.toComplete = true
		}
	}:
	case <-b.done:
	}
}

// Completed reports whether the bar is in completed state
func (b *Bar) Completed() bool {
	result := make(chan bool)
	select {
	case b.operateState <- func(s *bState) { result <- s.toComplete }:
		return <-result
	case <-b.done:
		return b.cacheState.toComplete
	}
}

func (b *Bar) serve(wg *sync.WaitGroup, s *bState, cancel <-chan struct{}) {
	defer wg.Done()
	s.startTime = time.Now()
	s.blockStartTime = s.startTime
	for {
		select {
		case op := <-b.operateState:
			op(s)
		case <-cancel:
			s.toComplete = true
			cancel = nil
		case <-b.shutdown:
			b.cacheState = s
			close(b.done)
			return
		}
	}
}

func (b *Bar) render(debugOut io.Writer, tw int, pSyncer, aSyncer *widthSyncer) {
	var r io.Reader
	select {
	case b.operateState <- func(s *bState) {
		defer func() {
			// recovering if external decorators panic
			if p := recover(); p != nil {
				s.panicMsg = fmt.Sprintf("panic: %v", p)
				s.pDecorators = nil
				s.aDecorators = nil
				s.toComplete = true
				// truncate panic msg to one tw line, if necessary
				r = strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", tw), s.panicMsg))
				fmt.Fprintf(debugOut, "%s %s bar id %02d %v\n", "[mpb]", time.Now(), s.id, s.panicMsg)
			}
			b.frameReaderCh <- &frameReader{
				Reader:           r,
				toShutdown:       s.toComplete && !s.completeFlushed,
				removeOnComplete: s.removeOnComplete,
			}
			s.completeFlushed = s.toComplete
		}()
		r = s.draw(tw, pSyncer, aSyncer)
	}:
	case <-b.done:
		s := b.cacheState
		if s.panicMsg != "" {
			r = strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", tw), s.panicMsg))
		} else {
			r = s.draw(tw, pSyncer, aSyncer)
		}
		b.frameReaderCh <- &frameReader{
			Reader: r,
		}
	}
}

func (s *bState) draw(termWidth int, pSyncer, aSyncer *widthSyncer) io.Reader {
	defer s.bufA.WriteByte('\n')

	if termWidth <= 0 {
		termWidth = s.width
	}

	stat := newStatistics(s)

	// render prepend functions to the left of the bar
	for i, f := range s.pDecorators {
		s.bufP.WriteString(f(stat, pSyncer.Accumulator[i], pSyncer.Distributor[i]))
	}

	for i, f := range s.aDecorators {
		s.bufA.WriteString(f(stat, aSyncer.Accumulator[i], aSyncer.Distributor[i]))
	}

	prependCount := utf8.RuneCount(s.bufP.Bytes())
	appendCount := utf8.RuneCount(s.bufA.Bytes())

	if s.barClearOnComplete && s.completeFlushed {
		return io.MultiReader(s.bufP, s.bufA)
	}

	s.fillBar(s.width)
	barCount := utf8.RuneCount(s.bufB.Bytes())
	totalCount := prependCount + barCount + appendCount
	if spaceCount := 0; totalCount > termWidth {
		if !s.trimLeftSpace {
			spaceCount++
		}
		if !s.trimRightSpace {
			spaceCount++
		}
		s.fillBar(termWidth - prependCount - appendCount - spaceCount)
	}

	return io.MultiReader(s.bufP, s.bufB, s.bufA)
}

func (s *bState) fillBar(width int) {
	defer func() {
		s.bufB.WriteRune(s.runes[rRight])
		if !s.trimRightSpace {
			s.bufB.WriteByte(' ')
		}
	}()

	s.bufB.Reset()
	if !s.trimLeftSpace {
		s.bufB.WriteByte(' ')
	}
	s.bufB.WriteRune(s.runes[rLeft])
	if width <= 2 {
		return
	}

	// bar s.width without leftEnd and rightEnd runes
	barWidth := width - 2

	completedWidth := decor.CalcPercentage(s.total, s.current, int64(barWidth))

	if s.refill != nil {
		till := decor.CalcPercentage(s.total, s.refill.till, int64(barWidth))
		// append refill rune
		var i int64
		for i = 0; i < till; i++ {
			s.bufB.WriteRune(s.refill.char)
		}
		for i = till; i < completedWidth; i++ {
			s.bufB.WriteRune(s.runes[rFill])
		}
	} else {
		var i int64
		for i = 0; i < completedWidth; i++ {
			s.bufB.WriteRune(s.runes[rFill])
		}
	}

	if completedWidth < int64(barWidth) && completedWidth > 0 {
		_, size := utf8.DecodeLastRune(s.bufB.Bytes())
		s.bufB.Truncate(s.bufB.Len() - size)
		s.bufB.WriteRune(s.runes[rTip])
	}

	for i := completedWidth; i < int64(barWidth); i++ {
		s.bufB.WriteRune(s.runes[rEmpty])
	}
}

func (s *bState) calcETA(n int, lastBlockTime time.Duration) time.Duration {
	lastItemEstimate := float64(lastBlockTime) / float64(n)
	s.timePerItemEstimate = time.Duration((s.etaAlpha * lastItemEstimate) + (1-s.etaAlpha)*float64(s.timePerItemEstimate))
	return time.Duration(s.total-s.current) * s.timePerItemEstimate
}

func newStatistics(s *bState) *decor.Statistics {
	return &decor.Statistics{
		ID:                  s.id,
		Completed:           s.completeFlushed,
		Total:               s.total,
		Current:             s.current,
		StartTime:           s.startTime,
		TimeElapsed:         s.timeElapsed,
		TimeRemaining:       s.timeRemaining,
		TimePerItemEstimate: s.timePerItemEstimate,
	}
}

func strToBarRunes(format string) (array barRunes) {
	for i, n := 0, 0; len(format) > 0; i++ {
		array[i], n = utf8.DecodeRuneInString(format)
		format = format[n:]
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"strings"
)

func main() {
	listAll := flag.Bool("l", false, "-l")
	flag.Parse()

	u, err := user.Current()
	if err != nil {
		fmt.Println("Error getting current user.", err.Error())
		return
	}
	//fmt.Println("HomeDir is:", u.HomeDir)

	gorFile := u.HomeDir + string(os.PathSeparator) + ".gorfile"
	_, err = os.Stat(gorFile)
	var gorList string
	if err != nil {
		if !os.IsNotExist(err) {
			panic(err)
		}
	} else {
		b, err := ioutil.ReadFile(gorFile)
		if err != nil {
			panic(err)
		}
		gorList = string(b)
		//fmt.Println("gorfile contains::", gorList)
	}

	list := strings.Split(gorList, ":")

	if *listAll {
		for i := 0; i < len(list) && len(list) > 0; i++ {
			fmt.Println(i+1, ":", list[i])
		}
		return
	}

	files := flag.Args()
	//fmt.Println("Command line files are:", files)
	if len(files) == 0 && len(gorList) == 0 {
		fmt.Println("Err! One go file needs to be given as there was nothing in the previous list either.")
		return
	}
	if len(files) > 1 {
		fmt.Println("Err! Specify only one file please.")
		return
	}

	var runFile string

	if len(files) != 0 {
		if len(gorList) > 0 {
			gorList += ":"
		}
		gorList += files[0]
		runFile = files[0]
	} else {
		runFile = list[len(list)-1]
	}
	//fmt.Println("Runfile is:", runFile)

	// for now, just write the last one.
	//err = ioutil.WriteFile(gorFile, []byte(gorList), 0644)
	err = ioutil.WriteFile(gorFile, []byte(runFile), 0644)

	fmt.Println("Going to run:", runFile, "...")

	cmd := exec.Command("go", "run", runFile)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Println("Error getting StdoutPipe")
		panic(err)
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		fmt.Println("Error getting StdErrPipe")
		panic(err)
	}

	outPipe := bufio.NewReader(stdout)
	errPipe := bufio.NewReader(stderr)
	outCh := make(chan string)
	errCh := make(chan string)

	err = cmd.Start()
	if err != nil {
		fmt.Println("Error starting cmd")
		panic(err)
	}
	go getStdOutput(outCh, outPipe)
	go getStdOutput(errCh, errPipe)
outside:
	for {
		select {
		case s, ok := <-outCh:
			if !ok {
				break outside
			}
			//fmt.Println("From Out:", s)
			fmt.Println(s)
		case s, ok := <-errCh:
			if !ok {
				break outside
			}
			fmt.Println("Err!", s)
		}
	}
	//fmt.Println("Finished")

	err = cmd.Wait()
	if err != nil {
		fmt.Println("Waiting error", err)
		return
	}
}

func getStdOutput(c chan string, p *bufio.Reader) {
	for {
		line, _, err := p.ReadLine()
		if err != nil {
			if err != io.EOF {
				fmt.Println("ReadLine error:", err)
				//return
			}
			break
		}
		//fmt.Println("Got line:", string(line))
		c <- string(line)
	}
	//fmt.Println("Closing c")
	close(c)
}
<commit_msg>Removed a println<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"strings"
)

func main() {
	listAll := flag.Bool("l", false, "-l")
	flag.Parse()

	u, err := user.Current()
	if err != nil {
		fmt.Println("Error getting current user.", err.Error())
		return
	}
	//fmt.Println("HomeDir is:", u.HomeDir)

	gorFile := u.HomeDir + string(os.PathSeparator) + ".gorfile"
	_, err = os.Stat(gorFile)
	var gorList string
	if err != nil {
		if !os.IsNotExist(err) {
			panic(err)
		}
	} else {
		b, err := ioutil.ReadFile(gorFile)
		if err != nil {
			panic(err)
		}
		gorList = string(b)
		//fmt.Println("gorfile contains::", gorList)
	}

	list := strings.Split(gorList, ":")

	if *listAll {
		for i := 0; i < len(list) && len(list) > 0; i++ {
			fmt.Println(i+1, ":", list[i])
		}
		return
	}

	files := flag.Args()
	//fmt.Println("Command line files are:", files)
	if len(files) == 0 && len(gorList) == 0 {
		fmt.Println("Err! One go file needs to be given as there was nothing in the previous list either.")
		return
	}
	if len(files) > 1 {
		fmt.Println("Err! Specify only one file please.")
		return
	}

	var runFile string

	if len(files) != 0 {
		if len(gorList) > 0 {
			gorList += ":"
		}
		gorList += files[0]
		runFile = files[0]
	} else {
		runFile = list[len(list)-1]
	}
	//fmt.Println("Runfile is:", runFile)

	// for now, just write the last one.
	//err = ioutil.WriteFile(gorFile, []byte(gorList), 0644)
	err = ioutil.WriteFile(gorFile, []byte(runFile), 0644)

	fmt.Println("Going to run:", runFile, "...")

	cmd := exec.Command("go", "run", runFile)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Println("Error getting StdoutPipe")
		panic(err)
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		fmt.Println("Error getting StdErrPipe")
		panic(err)
	}

	outPipe := bufio.NewReader(stdout)
	errPipe := bufio.NewReader(stderr)
	outCh := make(chan string)
	errCh := make(chan string)

	err = cmd.Start()
	if err != nil {
		fmt.Println("Error starting cmd")
		panic(err)
	}
	go getStdOutput(outCh, outPipe)
	go getStdOutput(errCh, errPipe)
outside:
	for {
		select {
		case s, ok := <-outCh:
			if !ok {
				break outside
			}
			//fmt.Println("From Out:", s)
			fmt.Println(s)
		case s, ok := <-errCh:
			if !ok {
				break outside
			}
			//fmt.Println("Err!", s)
			fmt.Println(s)
		}
	}
	//fmt.Println("Finished")

	err = cmd.Wait()
	if err != nil {
		fmt.Println("Waiting error", err)
		return
	}
}

func getStdOutput(c chan string, p *bufio.Reader) {
	for {
		line, _, err := p.ReadLine()
		if err != nil {
			if err != io.EOF {
				fmt.Println("ReadLine error:", err)
				//return
			}
			break
		}
		//fmt.Println("Got line:", string(line))
		c <- string(line)
	}
	//fmt.Println("Closing c")
	close(c)
}
<|endoftext|>


<commit_before>package log

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"time"
)

type StructuredLog struct {
	Duration   time.Duration
	RemoteAddr string
	Referer    string
	Status     int
}

type Logger struct {
	info           *log.Logger
	warning        *log.Logger
	error          *log.Logger
	slack          *log.Logger
	request        *log.Logger
	requestEncoder *json.Encoder
	callDepth      int
}

func New(prefix string, depth int) *Logger {

	requestLogger := log.New(os.Stdout, "", log.Ldate|log.Ltime)
	l := &Logger{
		info:           log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile),
		warning:        log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile),
		error:          log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lshortfile),
		slack:          log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile),
		request:        requestLogger,
		requestEncoder: json.NewEncoder(os.Stdout),
		callDepth:      depth,
	}

	l.SetPrefix(prefix)
	return l
}

var defaultLogger = New("", 3)

type BLogger interface {
	Errorf(format string, v ...interface{})
	Errorln(v ...interface{})
	Warningf(format string, v ...interface{})
	Warningln(v ...interface{})
	Infof(format string, v ...interface{})
	Infoln(v ...interface{})
	Requestln(v ...interface{})
	Panicln(v ...interface{})
	Panicf(format string, v ...interface{})
	Slackf(format string, v ...interface{})
	SlackLn(v ...interface{})
}

func (this *Logger) SetPrefix(prefix string) {

	if prefix != "" {
		prefix = prefix + " "
	}

	this.info.SetPrefix(prefix + "I: ")
	this.warning.SetPrefix(prefix + "W: ")
	this.error.SetPrefix(prefix + "E: ")
	this.slack.SetPrefix(prefix + "SLACK: ")
	this.request.SetPrefix(prefix + "R: ")
}

func (this *Logger) Errorf(format string, v ...interface{}) {
	this.error.Output(this.callDepth, f(format, v...))
}

func (this *Logger) Errorln(v ...interface{}) {
	this.error.Output(this.callDepth, ln(v...))
}

func (this *Logger) Warningf(format string, v ...interface{}) {
	this.warning.Output(this.callDepth, f(format, v...))
}

func (this *Logger) Warningln(v ...interface{}) {
	this.warning.Output(this.callDepth, ln(v...))
}

func (this *Logger) Infof(format string, v ...interface{}) {
	this.info.Output(this.callDepth, f(format, v...))
}

func (this *Logger) Infoln(v ...interface{}) {
	this.info.Output(this.callDepth, ln(v...))
}

func (this *Logger) Requestln(v ...interface{}) {
	this.request.Println(v...)
}

func (this *Logger) RequestEncoder() *json.Encoder {
	return this.requestEncoder
}

func (this *Logger) Panicln(v ...interface{}) {
	string := ln(v...)
	this.error.Output(this.callDepth, string)
	panic(string)
}

func (this *Logger) Panicf(format string, v ...interface{}) {
	string := f(format, v...)
	this.error.Output(this.callDepth, string)
	panic(string)
}

func (this *Logger) Slackf(format string, v ...interface{}) {
	this.slack.Output(this.callDepth, f(format, v...))
}

func (this *Logger) SlackLn(v ...interface{}) {
	this.slack.Output(this.callDepth, ln(v...))
}

//Globals
func Errorf(format string, v ...interface{}) {
	defaultLogger.Errorf(format, v...)
}

func Errorln(v ...interface{}) {
	defaultLogger.Errorln(v...)
}

func Warningf(format string, v ...interface{}) {
	defaultLogger.Warningf(format, v...)
}

func Warningln(v ...interface{}) {
	defaultLogger.Warningln(v...)
}

func Infof(format string, v ...interface{}) {
	defaultLogger.Infof(format, v...)
}

func Infoln(v ...interface{}) {
	defaultLogger.Infoln(v...)
}

func Requestln(v ...interface{}) {
	defaultLogger.Requestln(v...)
}

func RequestObject(obj StructuredLog) {
	defaultLogger.requestEncoder.Encode(obj)
}

func Panicln(v ...interface{}) {
	defaultLogger.Panicln(v...)
}

func Panicf(format string, v ...interface{}) {
	defaultLogger.Panicf(format, v...)
}

func Slackf(format string, v ...interface{}) {
	defaultLogger.Slackf(format, v...)
}

func SlackLn(v ...interface{}) {
	defaultLogger.SlackLn(v...)
}

func SetPrefix(p string) {
	defaultLogger.SetPrefix(p)
}

func f(format string, v ...interface{}) string {
	return ln(fmt.Sprintf(format, v...))
}

func ln(a ...interface{}) string {
	return fmt.Sprintln(a...)
}
<commit_msg>include path in strucutred log<commit_after>package log

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"time"
)

type StructuredLog struct {
	Path       string
	Duration   time.Duration
	RemoteAddr string
	Referer    string
	Status     int
}

type Logger struct {
	info           *log.Logger
	warning        *log.Logger
	error          *log.Logger
	slack          *log.Logger
	request        *log.Logger
	requestEncoder *json.Encoder
	callDepth      int
}

func New(prefix string, depth int) *Logger {

	requestLogger := log.New(os.Stdout, "", log.Ldate|log.Ltime)
	l := &Logger{
		info:           log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile),
		warning:        log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile),
		error:          log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lshortfile),
		slack:          log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile),
		request:        requestLogger,
		requestEncoder: json.NewEncoder(os.Stdout),
		callDepth:      depth,
	}

	l.SetPrefix(prefix)
	return l
}

var defaultLogger = New("", 3)

type BLogger interface {
	Errorf(format string, v ...interface{})
	Errorln(v ...interface{})
	Warningf(format string, v ...interface{})
	Warningln(v ...interface{})
	Infof(format string, v ...interface{})
	Infoln(v ...interface{})
	Requestln(v ...interface{})
	Panicln(v ...interface{})
	Panicf(format string, v ...interface{})
	Slackf(format string, v ...interface{})
	SlackLn(v ...interface{})
}

func (this *Logger) SetPrefix(prefix string) {

	if prefix != "" {
		prefix = prefix + " "
	}

	this.info.SetPrefix(prefix + "I: ")
	this.warning.SetPrefix(prefix + "W: ")
	this.error.SetPrefix(prefix + "E: ")
	this.slack.SetPrefix(prefix + "SLACK: ")
	this.request.SetPrefix(prefix + "R: ")
}

func (this *Logger) Errorf(format string, v ...interface{}) {
	this.error.Output(this.callDepth, f(format, v...))
}

func (this *Logger) Errorln(v ...interface{}) {
	this.error.Output(this.callDepth, ln(v...))
}

func (this *Logger) Warningf(format string, v ...interface{}) {
	this.warning.Output(this.callDepth, f(format, v...))
}

func (this *Logger) Warningln(v ...interface{}) {
	this.warning.Output(this.callDepth, ln(v...))
}

func (this *Logger) Infof(format string, v ...interface{}) {
	this.info.Output(this.callDepth, f(format, v...))
}

func (this *Logger) Infoln(v ...interface{}) {
	this.info.Output(this.callDepth, ln(v...))
}

func (this *Logger) Requestln(v ...interface{}) {
	this.request.Println(v...)
}

func (this *Logger) RequestEncoder() *json.Encoder {
	return this.requestEncoder
}

func (this *Logger) Panicln(v ...interface{}) {
	string := ln(v...)
	this.error.Output(this.callDepth, string)
	panic(string)
}

func (this *Logger) Panicf(format string, v ...interface{}) {
	string := f(format, v...)
	this.error.Output(this.callDepth, string)
	panic(string)
}

func (this *Logger) Slackf(format string, v ...interface{}) {
	this.slack.Output(this.callDepth, f(format, v...))
}

func (this *Logger) SlackLn(v ...interface{}) {
	this.slack.Output(this.callDepth, ln(v...))
}

//Globals
func Errorf(format string, v ...interface{}) {
	defaultLogger.Errorf(format, v...)
}

func Errorln(v ...interface{}) {
	defaultLogger.Errorln(v...)
}

func Warningf(format string, v ...interface{}) {
	defaultLogger.Warningf(format, v...)
}

func Warningln(v ...interface{}) {
	defaultLogger.Warningln(v...)
}

func Infof(format string, v ...interface{}) {
	defaultLogger.Infof(format, v...)
}

func Infoln(v ...interface{}) {
	defaultLogger.Infoln(v...)
}

func Requestln(v ...interface{}) {
	defaultLogger.Requestln(v...)
}

func RequestObject(obj StructuredLog) {
	defaultLogger.requestEncoder.Encode(obj)
}

func Panicln(v ...interface{}) {
	defaultLogger.Panicln(v...)
}

func Panicf(format string, v ...interface{}) {
	defaultLogger.Panicf(format, v...)
}

func Slackf(format string, v ...interface{}) {
	defaultLogger.Slackf(format, v...)
}

func SlackLn(v ...interface{}) {
	defaultLogger.SlackLn(v...)
}

func SetPrefix(p string) {
	defaultLogger.SetPrefix(p)
}

func f(format string, v ...interface{}) string {
	return ln(fmt.Sprintf(format, v...))
}

func ln(a ...interface{}) string {
	return fmt.Sprintln(a...)
}
<|endoftext|>