diff --git a/README.md b/README.md index 425ff397..d4a36abf 100644 --- a/README.md +++ b/README.md @@ -271,6 +271,14 @@ SELECT yaml_to_json('hello: world') -- +------------------------------+ ``` +##### `go_mod_to_json` + +Scalar function that parses a `go.mod` file and returns a JSON representation of it. + +```SQL +SELECT go_mod_to_json('') +``` + ##### `str_split` Helper for splitting strings on some separator. diff --git a/cmd/setup.go b/cmd/setup.go index be3ff162..151289a9 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -3,8 +3,9 @@ package cmd import ( "os" + "github.com/askgitdev/askgit/extensions" + "github.com/askgitdev/askgit/extensions/options" "github.com/askgitdev/askgit/pkg/locator" - "github.com/askgitdev/askgit/tables" "go.riyazali.net/sqlite" // bring in sqlite 🙌 @@ -14,14 +15,14 @@ import ( func registerExt() { sqlite.Register( - tables.RegisterFn( - tables.WithExtraFunctions(), - tables.WithRepoLocator(locator.CachedLocator(locator.MultiLocator())), - tables.WithContextValue("defaultRepoPath", repo), - tables.WithGitHub(), - tables.WithContextValue("githubToken", githubToken), - tables.WithContextValue("githubPerPage", os.Getenv("GITHUB_PER_PAGE")), - tables.WithContextValue("githubRateLimit", os.Getenv("GITHUB_RATE_LIMIT")), + extensions.RegisterFn( + options.WithExtraFunctions(), + options.WithRepoLocator(locator.CachedLocator(locator.MultiLocator())), + options.WithContextValue("defaultRepoPath", repo), + options.WithGitHub(), + options.WithContextValue("githubToken", githubToken), + options.WithContextValue("githubPerPage", os.Getenv("GITHUB_PER_PAGE")), + options.WithContextValue("githubRateLimit", os.Getenv("GITHUB_RATE_LIMIT")), ), ) } diff --git a/extensions/extensions.go b/extensions/extensions.go new file mode 100644 index 00000000..c690b944 --- /dev/null +++ b/extensions/extensions.go @@ -0,0 +1,53 @@ +// Package extensions provide implementation of the various underlying sqlite3 virtual tables [https://www.sqlite.org/vtab.html] and user defined functions +// that askgit uses under-the-hood. This module can be side-effect-imported in other modules to include the functionality +// of the sqlite3 extensions there. +package extensions + +import ( + "github.com/askgitdev/askgit/extensions/internal/enry" + "github.com/askgitdev/askgit/extensions/internal/git" + "github.com/askgitdev/askgit/extensions/internal/github" + "github.com/askgitdev/askgit/extensions/internal/golang" + "github.com/askgitdev/askgit/extensions/internal/helpers" + "github.com/askgitdev/askgit/extensions/options" + "go.riyazali.net/sqlite" +) + +func RegisterFn(fns ...options.OptionFn) func(ext *sqlite.ExtensionApi) (_ sqlite.ErrorCode, err error) { + var opt = &options.Options{} + for _, fn := range fns { + fn(opt) + } + + // return an extension function that register modules with sqlite when this package is loaded + return func(ext *sqlite.ExtensionApi) (_ sqlite.ErrorCode, err error) { + // register the git tables + if sqliteErr, err := git.Register(ext, opt); err != nil { + return sqliteErr, err + } + + // only conditionally register the utility functions + if opt.ExtraFunctions { + if sqliteErr, err := helpers.Register(ext, opt); err != nil { + return sqliteErr, err + } + + if sqliteErr, err := enry.Register(ext, opt); err != nil { + return sqliteErr, err + } + + if sqliteErr, err := golang.Register(ext, opt); err != nil { + return sqliteErr, err + } + } + + // conditionally register the GitHub functionality + if opt.GitHub { + if sqliteErr, err := github.Register(ext, opt); err != nil { + return sqliteErr, err + } + } + + return sqlite.SQLITE_OK, nil + } +} diff --git a/extensions/internal/enry/enry.go b/extensions/internal/enry/enry.go new file mode 100644 index 00000000..d9631dac --- /dev/null +++ b/extensions/internal/enry/enry.go @@ -0,0 +1,30 @@ +package enry + +import ( + "github.com/askgitdev/askgit/extensions/options" + "github.com/pkg/errors" + "go.riyazali.net/sqlite" +) + +// Register registers enry related functionality as a SQLite extension +func Register(ext *sqlite.ExtensionApi, _ *options.Options) (_ sqlite.ErrorCode, err error) { + var fns = map[string]sqlite.Function{ + "enry_detect_language": &EnryDetectLanguage{}, + "enry_is_binary": &EnryIsBinary{}, + "enry_is_configuration": &EnryIsConfiguration{}, + "enry_is_documentation": &EnryIsDocumentation{}, + "enry_is_dot_file": &EnryIsDotFile{}, + "enry_is_generated": &EnryIsGenerated{}, + "enry_is_image": &EnryIsImage{}, + "enry_is_test": &EnryIsTest{}, + "enry_is_vendor": &EnryIsVendor{}, + } + + for name, fn := range fns { + if err = ext.CreateFunction(name, fn); err != nil { + return sqlite.SQLITE_ERROR, errors.Wrapf(err, "failed to register %q function", name) + } + } + + return sqlite.SQLITE_OK, nil +} diff --git a/tables/internal/funcs/enry_detect_language.go b/extensions/internal/enry/enry_detect_language.go similarity index 97% rename from tables/internal/funcs/enry_detect_language.go rename to extensions/internal/enry/enry_detect_language.go index 5a93fd5d..591ddcea 100644 --- a/tables/internal/funcs/enry_detect_language.go +++ b/extensions/internal/enry/enry_detect_language.go @@ -1,4 +1,4 @@ -package funcs +package enry import ( "github.com/go-enry/go-enry/v2" diff --git a/tables/internal/funcs/enry_detect_language_test.go b/extensions/internal/enry/enry_detect_language_test.go similarity index 88% rename from tables/internal/funcs/enry_detect_language_test.go rename to extensions/internal/enry/enry_detect_language_test.go index f5d3f498..221c5eed 100644 --- a/tables/internal/funcs/enry_detect_language_test.go +++ b/extensions/internal/enry/enry_detect_language_test.go @@ -1,10 +1,10 @@ -package funcs +package enry import ( "io/ioutil" "testing" - "github.com/askgitdev/askgit/tables/internal/tools" + "github.com/askgitdev/askgit/extensions/internal/tools" ) func TestEnryDetectLanguage(t *testing.T) { diff --git a/tables/internal/funcs/enry_is_binary.go b/extensions/internal/enry/enry_is_binary.go similarity index 96% rename from tables/internal/funcs/enry_is_binary.go rename to extensions/internal/enry/enry_is_binary.go index a7bcc999..669e8415 100644 --- a/tables/internal/funcs/enry_is_binary.go +++ b/extensions/internal/enry/enry_is_binary.go @@ -1,4 +1,4 @@ -package funcs +package enry import ( "github.com/go-enry/go-enry/v2" diff --git a/tables/internal/funcs/enry_is_binary_test.go b/extensions/internal/enry/enry_is_binary_test.go similarity index 87% rename from tables/internal/funcs/enry_is_binary_test.go rename to extensions/internal/enry/enry_is_binary_test.go index 1e5472ed..b2623cfe 100644 --- a/tables/internal/funcs/enry_is_binary_test.go +++ b/extensions/internal/enry/enry_is_binary_test.go @@ -1,10 +1,10 @@ -package funcs +package enry import ( "io/ioutil" "testing" - "github.com/askgitdev/askgit/tables/internal/tools" + "github.com/askgitdev/askgit/extensions/internal/tools" ) func TestEnryIsBinary(t *testing.T) { diff --git a/tables/internal/funcs/enry_is_configuration.go b/extensions/internal/enry/enry_is_configuration.go similarity index 96% rename from tables/internal/funcs/enry_is_configuration.go rename to extensions/internal/enry/enry_is_configuration.go index 4814b0ad..8e94c7f9 100644 --- a/tables/internal/funcs/enry_is_configuration.go +++ b/extensions/internal/enry/enry_is_configuration.go @@ -1,4 +1,4 @@ -package funcs +package enry import ( "github.com/go-enry/go-enry/v2" diff --git a/tables/internal/funcs/enry_is_configuration_test.go b/extensions/internal/enry/enry_is_configuration_test.go similarity index 87% rename from tables/internal/funcs/enry_is_configuration_test.go rename to extensions/internal/enry/enry_is_configuration_test.go index 7c008fc8..1e9fec49 100644 --- a/tables/internal/funcs/enry_is_configuration_test.go +++ b/extensions/internal/enry/enry_is_configuration_test.go @@ -1,9 +1,9 @@ -package funcs +package enry import ( "testing" - "github.com/askgitdev/askgit/tables/internal/tools" + "github.com/askgitdev/askgit/extensions/internal/tools" ) func TestEnryIsConfiguration(t *testing.T) { diff --git a/tables/internal/funcs/enry_is_documentation.go b/extensions/internal/enry/enry_is_documentation.go similarity index 96% rename from tables/internal/funcs/enry_is_documentation.go rename to extensions/internal/enry/enry_is_documentation.go index 344b65a5..06766931 100644 --- a/tables/internal/funcs/enry_is_documentation.go +++ b/extensions/internal/enry/enry_is_documentation.go @@ -1,4 +1,4 @@ -package funcs +package enry import ( "github.com/go-enry/go-enry/v2" diff --git a/tables/internal/funcs/enry_is_documentation_test.go b/extensions/internal/enry/enry_is_documentation_test.go similarity index 85% rename from tables/internal/funcs/enry_is_documentation_test.go rename to extensions/internal/enry/enry_is_documentation_test.go index 373f80c9..63070f94 100644 --- a/tables/internal/funcs/enry_is_documentation_test.go +++ b/extensions/internal/enry/enry_is_documentation_test.go @@ -1,9 +1,9 @@ -package funcs +package enry import ( "testing" - "github.com/askgitdev/askgit/tables/internal/tools" + "github.com/askgitdev/askgit/extensions/internal/tools" ) func TestEnryIsDocumentation(t *testing.T) { diff --git a/tables/internal/funcs/enry_is_dot_file.go b/extensions/internal/enry/enry_is_dot_file.go similarity index 96% rename from tables/internal/funcs/enry_is_dot_file.go rename to extensions/internal/enry/enry_is_dot_file.go index 75606e8e..0fd8a6a5 100644 --- a/tables/internal/funcs/enry_is_dot_file.go +++ b/extensions/internal/enry/enry_is_dot_file.go @@ -1,4 +1,4 @@ -package funcs +package enry import ( "github.com/go-enry/go-enry/v2" diff --git a/tables/internal/funcs/enry_is_dot_file_test.go b/extensions/internal/enry/enry_is_dot_file_test.go similarity index 85% rename from tables/internal/funcs/enry_is_dot_file_test.go rename to extensions/internal/enry/enry_is_dot_file_test.go index 80bcb8e9..35f77911 100644 --- a/tables/internal/funcs/enry_is_dot_file_test.go +++ b/extensions/internal/enry/enry_is_dot_file_test.go @@ -1,9 +1,9 @@ -package funcs +package enry import ( "testing" - "github.com/askgitdev/askgit/tables/internal/tools" + "github.com/askgitdev/askgit/extensions/internal/tools" ) func TestEnryIsDotFile(t *testing.T) { diff --git a/tables/internal/funcs/enry_is_generated.go b/extensions/internal/enry/enry_is_generated.go similarity index 96% rename from tables/internal/funcs/enry_is_generated.go rename to extensions/internal/enry/enry_is_generated.go index b5b45795..5ab5abe4 100644 --- a/tables/internal/funcs/enry_is_generated.go +++ b/extensions/internal/enry/enry_is_generated.go @@ -1,4 +1,4 @@ -package funcs +package enry import ( "github.com/go-enry/go-enry/v2" diff --git a/tables/internal/funcs/enry_is_generated_test.go b/extensions/internal/enry/enry_is_generated_test.go similarity index 85% rename from tables/internal/funcs/enry_is_generated_test.go rename to extensions/internal/enry/enry_is_generated_test.go index 372128f4..6aa364b8 100644 --- a/tables/internal/funcs/enry_is_generated_test.go +++ b/extensions/internal/enry/enry_is_generated_test.go @@ -1,9 +1,9 @@ -package funcs +package enry import ( "testing" - "github.com/askgitdev/askgit/tables/internal/tools" + "github.com/askgitdev/askgit/extensions/internal/tools" ) func TestEnryIsGenerated(t *testing.T) { diff --git a/tables/internal/funcs/enry_is_image.go b/extensions/internal/enry/enry_is_image.go similarity index 96% rename from tables/internal/funcs/enry_is_image.go rename to extensions/internal/enry/enry_is_image.go index 0d35677e..4ce8a320 100644 --- a/tables/internal/funcs/enry_is_image.go +++ b/extensions/internal/enry/enry_is_image.go @@ -1,4 +1,4 @@ -package funcs +package enry import ( "github.com/go-enry/go-enry/v2" diff --git a/tables/internal/funcs/enry_is_image_test.go b/extensions/internal/enry/enry_is_image_test.go similarity index 85% rename from tables/internal/funcs/enry_is_image_test.go rename to extensions/internal/enry/enry_is_image_test.go index deb42447..91800ba8 100644 --- a/tables/internal/funcs/enry_is_image_test.go +++ b/extensions/internal/enry/enry_is_image_test.go @@ -1,9 +1,9 @@ -package funcs +package enry import ( "testing" - "github.com/askgitdev/askgit/tables/internal/tools" + "github.com/askgitdev/askgit/extensions/internal/tools" ) func TestEnryIsImage(t *testing.T) { diff --git a/tables/internal/funcs/enry_is_test_file.go b/extensions/internal/enry/enry_is_test_file.go similarity index 96% rename from tables/internal/funcs/enry_is_test_file.go rename to extensions/internal/enry/enry_is_test_file.go index bba250d4..54d1404d 100644 --- a/tables/internal/funcs/enry_is_test_file.go +++ b/extensions/internal/enry/enry_is_test_file.go @@ -1,4 +1,4 @@ -package funcs +package enry import ( "github.com/go-enry/go-enry/v2" diff --git a/tables/internal/funcs/enry_is_test_file_test.go b/extensions/internal/enry/enry_is_test_file_test.go similarity index 85% rename from tables/internal/funcs/enry_is_test_file_test.go rename to extensions/internal/enry/enry_is_test_file_test.go index fa660bb8..8721ac3b 100644 --- a/tables/internal/funcs/enry_is_test_file_test.go +++ b/extensions/internal/enry/enry_is_test_file_test.go @@ -1,9 +1,9 @@ -package funcs +package enry import ( "testing" - "github.com/askgitdev/askgit/tables/internal/tools" + "github.com/askgitdev/askgit/extensions/internal/tools" ) func TestEnryIsTest(t *testing.T) { diff --git a/tables/internal/funcs/enry_is_vendor.go b/extensions/internal/enry/enry_is_vendor.go similarity index 96% rename from tables/internal/funcs/enry_is_vendor.go rename to extensions/internal/enry/enry_is_vendor.go index 4b65b94b..3c4cb0bc 100644 --- a/tables/internal/funcs/enry_is_vendor.go +++ b/extensions/internal/enry/enry_is_vendor.go @@ -1,4 +1,4 @@ -package funcs +package enry import ( "github.com/go-enry/go-enry/v2" diff --git a/tables/internal/funcs/enry_is_vendor_test.go b/extensions/internal/enry/enry_is_vendor_test.go similarity index 85% rename from tables/internal/funcs/enry_is_vendor_test.go rename to extensions/internal/enry/enry_is_vendor_test.go index b4e57d14..bd8ba1c7 100644 --- a/tables/internal/funcs/enry_is_vendor_test.go +++ b/extensions/internal/enry/enry_is_vendor_test.go @@ -1,9 +1,9 @@ -package funcs +package enry import ( "testing" - "github.com/askgitdev/askgit/tables/internal/tools" + "github.com/askgitdev/askgit/extensions/internal/tools" ) func TestEnryIsVendor(t *testing.T) { diff --git a/extensions/internal/enry/enry_test.go b/extensions/internal/enry/enry_test.go new file mode 100644 index 00000000..0c29bf49 --- /dev/null +++ b/extensions/internal/enry/enry_test.go @@ -0,0 +1,30 @@ +package enry + +import ( + "database/sql" + "log" + "os" + "testing" + + _ "github.com/askgitdev/askgit/pkg/sqlite" + "go.riyazali.net/sqlite" +) + +// FixtureDatabase represents the database connection to run the test against +var FixtureDatabase *sql.DB + +func init() { + // register sqlite extension when this package is loaded + sqlite.Register(func(ext *sqlite.ExtensionApi) (_ sqlite.ErrorCode, err error) { + return Register(ext, nil) + }) +} + +func TestMain(m *testing.M) { + var err error + if FixtureDatabase, err = sql.Open("sqlite3", "file:testing.db?mode=memory"); err != nil { + log.Fatalf("failed to open database connection: %v", err) + } + + os.Exit(m.Run()) +} diff --git a/tables/internal/funcs/testdata/binary b/extensions/internal/enry/testdata/binary similarity index 100% rename from tables/internal/funcs/testdata/binary rename to extensions/internal/enry/testdata/binary diff --git a/tables/internal/funcs/testdata/main.go b/extensions/internal/enry/testdata/main.go similarity index 100% rename from tables/internal/funcs/testdata/main.go rename to extensions/internal/enry/testdata/main.go diff --git a/extensions/internal/git/git.go b/extensions/internal/git/git.go new file mode 100644 index 00000000..bc8dbe33 --- /dev/null +++ b/extensions/internal/git/git.go @@ -0,0 +1,38 @@ +package git + +import ( + "github.com/askgitdev/askgit/extensions/internal/git/native" + "github.com/askgitdev/askgit/extensions/options" + "github.com/pkg/errors" + "go.riyazali.net/sqlite" +) + +// Register registers git related functionality as a SQLite extension +func Register(ext *sqlite.ExtensionApi, opt *options.Options) (_ sqlite.ErrorCode, err error) { + // register virtual table modules + var modules = map[string]sqlite.Module{ + "commits": &LogModule{Locator: opt.Locator, Context: opt.Context}, + "refs": &RefModule{Locator: opt.Locator, Context: opt.Context}, + "stats": native.NewStatsModule(opt.Locator, opt.Context), + "files": native.NewFilesModule(opt.Locator, opt.Context), + "blame": native.NewBlameModule(opt.Locator, opt.Context), + } + + for name, mod := range modules { + if err = ext.CreateModule(name, mod); err != nil { + return sqlite.SQLITE_ERROR, errors.Wrapf(err, "failed to register %q module", name) + } + } + + var fns = map[string]sqlite.Function{ + "commit_from_tag": &CommitFromTagFn{}, + } + + for name, fn := range fns { + if err = ext.CreateFunction(name, fn); err != nil { + return sqlite.SQLITE_ERROR, errors.Wrapf(err, "failed to register %q function", name) + } + } + + return sqlite.SQLITE_OK, nil +} diff --git a/tables/internal/git/git_test.go b/extensions/internal/git/git_test.go similarity index 80% rename from tables/internal/git/git_test.go rename to extensions/internal/git/git_test.go index 7c6aa0e0..8a9c3a40 100644 --- a/tables/internal/git/git_test.go +++ b/extensions/internal/git/git_test.go @@ -2,19 +2,21 @@ package git_test import ( "database/sql" + "os" + "testing" + + "github.com/askgitdev/askgit/extensions" + "github.com/askgitdev/askgit/extensions/options" "github.com/askgitdev/askgit/pkg/locator" _ "github.com/askgitdev/askgit/pkg/sqlite" - "github.com/askgitdev/askgit/tables" _ "github.com/mattn/go-sqlite3" "go.riyazali.net/sqlite" - "os" - "testing" ) func init() { // register sqlite extension when this package is loaded - sqlite.Register(tables.RegisterFn( - tables.WithExtraFunctions(), tables.WithRepoLocator(locator.CachedLocator(locator.MultiLocator())), + sqlite.Register(extensions.RegisterFn( + options.WithExtraFunctions(), options.WithRepoLocator(locator.CachedLocator(locator.MultiLocator())), )) } diff --git a/tables/internal/git/log.go b/extensions/internal/git/log.go similarity index 98% rename from tables/internal/git/log.go rename to extensions/internal/git/log.go index 959f9a4d..503ea58b 100644 --- a/tables/internal/git/log.go +++ b/extensions/internal/git/log.go @@ -4,7 +4,8 @@ import ( "context" "time" - "github.com/askgitdev/askgit/tables/services" + "github.com/askgitdev/askgit/extensions/internal/git/utils" + "github.com/askgitdev/askgit/extensions/services" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" @@ -180,7 +181,7 @@ func (cur *gitLogCursor) Filter(_ int, s string, values ...sqlite.Value) (err er var repo *git.Repository { // open the git repository if path == "" { - path, err = GetDefaultRepoFromCtx(cur.ctx) + path, err = utils.GetDefaultRepoFromCtx(cur.ctx) if err != nil { return err } diff --git a/tables/internal/git/log_test.go b/extensions/internal/git/log_test.go similarity index 100% rename from tables/internal/git/log_test.go rename to extensions/internal/git/log_test.go diff --git a/tables/internal/git/native/blame.go b/extensions/internal/git/native/blame.go similarity index 96% rename from tables/internal/git/native/blame.go rename to extensions/internal/git/native/blame.go index 030d55ca..3d97af38 100644 --- a/tables/internal/git/native/blame.go +++ b/extensions/internal/git/native/blame.go @@ -6,8 +6,8 @@ import ( "io" "os" - "github.com/askgitdev/askgit/tables/internal/git" - "github.com/askgitdev/askgit/tables/services" + "github.com/askgitdev/askgit/extensions/internal/git/utils" + "github.com/askgitdev/askgit/extensions/services" "github.com/augmentable-dev/vtab" "github.com/go-git/go-git/v5/storage/filesystem" libgit2 "github.com/libgit2/git2go/v31" @@ -48,7 +48,7 @@ func NewBlameModule(locator services.RepoLocator, ctx services.Context) sqlite.M if repoPath == "" { var err error - repoPath, err = git.GetDefaultRepoFromCtx(ctx) + repoPath, err = utils.GetDefaultRepoFromCtx(ctx) if err != nil { return nil, err } diff --git a/tables/internal/git/native/blame_test.go b/extensions/internal/git/native/blame_test.go similarity index 100% rename from tables/internal/git/native/blame_test.go rename to extensions/internal/git/native/blame_test.go diff --git a/tables/internal/git/native/doc.go b/extensions/internal/git/native/doc.go similarity index 100% rename from tables/internal/git/native/doc.go rename to extensions/internal/git/native/doc.go diff --git a/tables/internal/git/native/files.go b/extensions/internal/git/native/files.go similarity index 96% rename from tables/internal/git/native/files.go rename to extensions/internal/git/native/files.go index 6ef1c1ab..43c15846 100644 --- a/tables/internal/git/native/files.go +++ b/extensions/internal/git/native/files.go @@ -7,8 +7,8 @@ import ( "os" "path" - "github.com/askgitdev/askgit/tables/internal/git" - "github.com/askgitdev/askgit/tables/services" + "github.com/askgitdev/askgit/extensions/internal/git/utils" + "github.com/askgitdev/askgit/extensions/services" "github.com/augmentable-dev/vtab" "github.com/go-git/go-git/v5/storage/filesystem" libgit2 "github.com/libgit2/git2go/v31" @@ -41,7 +41,7 @@ func NewFilesModule(locator services.RepoLocator, ctx services.Context) sqlite.M if repoPath == "" { var err error - repoPath, err = git.GetDefaultRepoFromCtx(ctx) + repoPath, err = utils.GetDefaultRepoFromCtx(ctx) if err != nil { return nil, err } diff --git a/tables/internal/git/native/files_test.go b/extensions/internal/git/native/files_test.go similarity index 100% rename from tables/internal/git/native/files_test.go rename to extensions/internal/git/native/files_test.go diff --git a/tables/internal/git/native/native_test.go b/extensions/internal/git/native/native_test.go similarity index 80% rename from tables/internal/git/native/native_test.go rename to extensions/internal/git/native/native_test.go index 1370a773..f983fd03 100644 --- a/tables/internal/git/native/native_test.go +++ b/extensions/internal/git/native/native_test.go @@ -5,17 +5,18 @@ import ( "os" "testing" + "github.com/askgitdev/askgit/extensions" + "github.com/askgitdev/askgit/extensions/options" "github.com/askgitdev/askgit/pkg/locator" _ "github.com/askgitdev/askgit/pkg/sqlite" - "github.com/askgitdev/askgit/tables" _ "github.com/mattn/go-sqlite3" "go.riyazali.net/sqlite" ) func init() { // register sqlite extension when this package is loaded - sqlite.Register(tables.RegisterFn( - tables.WithExtraFunctions(), tables.WithRepoLocator(locator.CachedLocator(locator.MultiLocator())), + sqlite.Register(extensions.RegisterFn( + options.WithExtraFunctions(), options.WithRepoLocator(locator.CachedLocator(locator.MultiLocator())), )) } diff --git a/tables/internal/git/native/stats.go b/extensions/internal/git/native/stats.go similarity index 96% rename from tables/internal/git/native/stats.go rename to extensions/internal/git/native/stats.go index bafa353b..eddfe6cd 100644 --- a/tables/internal/git/native/stats.go +++ b/extensions/internal/git/native/stats.go @@ -5,8 +5,8 @@ import ( "fmt" "io" - "github.com/askgitdev/askgit/tables/internal/git" - "github.com/askgitdev/askgit/tables/services" + "github.com/askgitdev/askgit/extensions/internal/git/utils" + "github.com/askgitdev/askgit/extensions/services" "github.com/augmentable-dev/vtab" "github.com/go-git/go-git/v5/storage/filesystem" libgit2 "github.com/libgit2/git2go/v31" @@ -42,7 +42,7 @@ func NewStatsModule(locator services.RepoLocator, ctx services.Context) sqlite.M if repoPath == "" { var err error - repoPath, err = git.GetDefaultRepoFromCtx(ctx) + repoPath, err = utils.GetDefaultRepoFromCtx(ctx) if err != nil { return nil, err } diff --git a/tables/internal/git/native/stats_test.go b/extensions/internal/git/native/stats_test.go similarity index 100% rename from tables/internal/git/native/stats_test.go rename to extensions/internal/git/native/stats_test.go diff --git a/tables/internal/git/native/testdata/2359c9a9ba0ba8aa694601ff12538c4e74b82cd5/Makefile.testdata b/extensions/internal/git/native/testdata/2359c9a9ba0ba8aa694601ff12538c4e74b82cd5/Makefile.testdata similarity index 100% rename from tables/internal/git/native/testdata/2359c9a9ba0ba8aa694601ff12538c4e74b82cd5/Makefile.testdata rename to extensions/internal/git/native/testdata/2359c9a9ba0ba8aa694601ff12538c4e74b82cd5/Makefile.testdata diff --git a/tables/internal/git/native/testdata/2359c9a9ba0ba8aa694601ff12538c4e74b82cd5/README.md.blame.csv.testdata b/extensions/internal/git/native/testdata/2359c9a9ba0ba8aa694601ff12538c4e74b82cd5/README.md.blame.csv.testdata similarity index 100% rename from tables/internal/git/native/testdata/2359c9a9ba0ba8aa694601ff12538c4e74b82cd5/README.md.blame.csv.testdata rename to extensions/internal/git/native/testdata/2359c9a9ba0ba8aa694601ff12538c4e74b82cd5/README.md.blame.csv.testdata diff --git a/tables/internal/git/native/testdata/2359c9a9ba0ba8aa694601ff12538c4e74b82cd5/go.mod.testdata b/extensions/internal/git/native/testdata/2359c9a9ba0ba8aa694601ff12538c4e74b82cd5/go.mod.testdata similarity index 100% rename from tables/internal/git/native/testdata/2359c9a9ba0ba8aa694601ff12538c4e74b82cd5/go.mod.testdata rename to extensions/internal/git/native/testdata/2359c9a9ba0ba8aa694601ff12538c4e74b82cd5/go.mod.testdata diff --git a/tables/internal/git/refs.go b/extensions/internal/git/refs.go similarity index 96% rename from tables/internal/git/refs.go rename to extensions/internal/git/refs.go index 4c0ff236..d863fdaf 100644 --- a/tables/internal/git/refs.go +++ b/extensions/internal/git/refs.go @@ -4,7 +4,8 @@ import ( "context" "regexp" - "github.com/askgitdev/askgit/tables/services" + "github.com/askgitdev/askgit/extensions/internal/git/utils" + "github.com/askgitdev/askgit/extensions/services" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/storer" @@ -94,7 +95,7 @@ func (cur *gitRefCursor) Filter(_ int, s string, values ...sqlite.Value) (err er var repo *git.Repository { // open the git repository if path == "" { - path, err = GetDefaultRepoFromCtx(cur.Context) + path, err = utils.GetDefaultRepoFromCtx(cur.Context) if err != nil { return err } diff --git a/tables/internal/git/refs_func.go b/extensions/internal/git/refs_func.go similarity index 100% rename from tables/internal/git/refs_func.go rename to extensions/internal/git/refs_func.go diff --git a/tables/internal/git/refs_func_test.go b/extensions/internal/git/refs_func_test.go similarity index 100% rename from tables/internal/git/refs_func_test.go rename to extensions/internal/git/refs_func_test.go diff --git a/tables/internal/git/refs_test.go b/extensions/internal/git/refs_test.go similarity index 100% rename from tables/internal/git/refs_test.go rename to extensions/internal/git/refs_test.go diff --git a/tables/internal/git/utils.go b/extensions/internal/git/utils.go similarity index 53% rename from tables/internal/git/utils.go rename to extensions/internal/git/utils.go index 0b8175e3..5fc31435 100644 --- a/tables/internal/git/utils.go +++ b/extensions/internal/git/utils.go @@ -3,10 +3,8 @@ package git import ( "encoding/base64" "io" - "os" "strings" - "github.com/askgitdev/askgit/tables/services" "github.com/go-git/go-git/v5/plumbing" ) @@ -20,17 +18,3 @@ func isRemoteBranch(ref plumbing.ReferenceName) bool { return ref.IsRemote() && plumbing.ReferenceName(strings.Replace(ref.String(), "remotes", "heads", 1)).IsBranch() } - -// GetDefaultRepoFromCtx looks up the defaultRepoPath key in the supplied context and returns it if set, -// otherwise it returns the current working directory -func GetDefaultRepoFromCtx(ctx services.Context) (repoPath string, err error) { - var ok bool - if repoPath, ok = ctx["defaultRepoPath"]; !ok || repoPath == "" { - if wd, err := os.Getwd(); err != nil { - return "", err - } else { - repoPath = wd - } - } - return -} diff --git a/extensions/internal/git/utils/utils.go b/extensions/internal/git/utils/utils.go new file mode 100644 index 00000000..b629af0a --- /dev/null +++ b/extensions/internal/git/utils/utils.go @@ -0,0 +1,21 @@ +package utils + +import ( + "os" + + "github.com/askgitdev/askgit/extensions/services" +) + +// GetDefaultRepoFromCtx looks up the defaultRepoPath key in the supplied context and returns it if set, +// otherwise it returns the current working directory +func GetDefaultRepoFromCtx(ctx services.Context) (repoPath string, err error) { + var ok bool + if repoPath, ok = ctx["defaultRepoPath"]; !ok || repoPath == "" { + if wd, err := os.Getwd(); err != nil { + return "", err + } else { + repoPath = wd + } + } + return +} diff --git a/tables/internal/github/fixtures/TestOrgRepos.yaml b/extensions/internal/github/fixtures/TestOrgRepos.yaml similarity index 100% rename from tables/internal/github/fixtures/TestOrgRepos.yaml rename to extensions/internal/github/fixtures/TestOrgRepos.yaml diff --git a/tables/internal/github/fixtures/TestRepoIssues.yaml b/extensions/internal/github/fixtures/TestRepoIssues.yaml similarity index 100% rename from tables/internal/github/fixtures/TestRepoIssues.yaml rename to extensions/internal/github/fixtures/TestRepoIssues.yaml diff --git a/tables/internal/github/fixtures/TestRepoPRs.yaml b/extensions/internal/github/fixtures/TestRepoPRs.yaml similarity index 100% rename from tables/internal/github/fixtures/TestRepoPRs.yaml rename to extensions/internal/github/fixtures/TestRepoPRs.yaml diff --git a/tables/internal/github/fixtures/TestStargazers.yaml b/extensions/internal/github/fixtures/TestStargazers.yaml similarity index 100% rename from tables/internal/github/fixtures/TestStargazers.yaml rename to extensions/internal/github/fixtures/TestStargazers.yaml diff --git a/tables/internal/github/fixtures/TestStargazersCount.yaml b/extensions/internal/github/fixtures/TestStargazersCount.yaml similarity index 100% rename from tables/internal/github/fixtures/TestStargazersCount.yaml rename to extensions/internal/github/fixtures/TestStargazersCount.yaml diff --git a/tables/internal/github/fixtures/TestStarredRepos.yaml b/extensions/internal/github/fixtures/TestStarredRepos.yaml similarity index 100% rename from tables/internal/github/fixtures/TestStarredRepos.yaml rename to extensions/internal/github/fixtures/TestStarredRepos.yaml diff --git a/tables/internal/github/fixtures/TestUserRepos.yaml b/extensions/internal/github/fixtures/TestUserRepos.yaml similarity index 100% rename from tables/internal/github/fixtures/TestUserRepos.yaml rename to extensions/internal/github/fixtures/TestUserRepos.yaml diff --git a/extensions/internal/github/github.go b/extensions/internal/github/github.go new file mode 100644 index 00000000..974289e6 --- /dev/null +++ b/extensions/internal/github/github.go @@ -0,0 +1,70 @@ +package github + +import ( + "context" + "time" + + "github.com/askgitdev/askgit/extensions/options" + "github.com/pkg/errors" + "github.com/shurcooL/githubv4" + "go.riyazali.net/sqlite" + "golang.org/x/oauth2" + "golang.org/x/time/rate" +) + +// Register registers GitHub related functionality as a SQLite extension +func Register(ext *sqlite.ExtensionApi, opt *options.Options) (_ sqlite.ErrorCode, err error) { + rateLimiter := GetGitHubRateLimitFromCtx(opt.Context) + if rateLimiter == nil { + rateLimiter = rate.NewLimiter(rate.Every(1*time.Second), 2) + } + + githubOpts := &Options{ + RateLimiter: rateLimiter, + Client: func() *githubv4.Client { + httpClient := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: GetGitHubTokenFromCtx(opt.Context)}, + )) + client := githubv4.NewClient(httpClient) + return client + }, + PerPage: GetGitHubPerPageFromCtx(opt.Context), + } + + if opt.GitHubClientGetter != nil { + githubOpts.Client = opt.GitHubClientGetter + } + + var modules = map[string]sqlite.Module{ + "github_stargazers": NewStargazersModule(githubOpts), + "github_starred_repos": NewStarredReposModule(githubOpts), + "github_user_repos": NewUserReposModule(githubOpts), + "github_org_repos": NewOrgReposModule(githubOpts), + "github_repo_issues": NewIssuesModule(githubOpts), + "github_repo_pull_requests": NewPRModule(githubOpts), + } + + modules["github_issues"] = modules["github_repo_issues"] + modules["github_pull_requests"] = modules["github_repo_pull_requests"] + modules["github_prs"] = modules["github_repo_pull_requests"] + modules["github_repo_prs"] = modules["github_repo_pull_requests"] + + // register GitHub tables + for name, mod := range modules { + if err = ext.CreateModule(name, mod); err != nil { + return sqlite.SQLITE_ERROR, errors.Wrapf(err, "failed to register GitHub %q module", name) + } + } + + var fns = map[string]sqlite.Function{ + "github_stargazer_count": NewStarredReposFunc(githubOpts), + } + + // register GitHub funcs + for name, fn := range fns { + if err = ext.CreateFunction(name, fn); err != nil { + return sqlite.SQLITE_ERROR, errors.Wrapf(err, "failed to register GitHub %q function", name) + } + } + return sqlite.SQLITE_OK, nil +} diff --git a/tables/internal/github/github_test.go b/extensions/internal/github/github_test.go similarity index 87% rename from tables/internal/github/github_test.go rename to extensions/internal/github/github_test.go index f5e8bab9..2cfc23d3 100644 --- a/tables/internal/github/github_test.go +++ b/extensions/internal/github/github_test.go @@ -7,8 +7,9 @@ import ( "path" "testing" + "github.com/askgitdev/askgit/extensions" + "github.com/askgitdev/askgit/extensions/options" _ "github.com/askgitdev/askgit/pkg/sqlite" - "github.com/askgitdev/askgit/tables" "github.com/dnaeon/go-vcr/v2/cassette" "github.com/dnaeon/go-vcr/v2/recorder" _ "github.com/mattn/go-sqlite3" @@ -47,10 +48,10 @@ func newRecorder(t *testing.T) func() { // automatically with all loaded database connections func TestMain(m *testing.M) { // register sqlite extension when this package is loaded - sqlite.Register(tables.RegisterFn( - tables.WithExtraFunctions(), - tables.WithGitHub(), - tables.WithGitHubClientGetter(func() *githubv4.Client { + sqlite.Register(extensions.RegisterFn( + options.WithExtraFunctions(), + options.WithGitHub(), + options.WithGitHubClientGetter(func() *githubv4.Client { return githubv4.NewClient(httpClient) }), )) diff --git a/tables/internal/github/org_repos.go b/extensions/internal/github/org_repos.go similarity index 100% rename from tables/internal/github/org_repos.go rename to extensions/internal/github/org_repos.go diff --git a/tables/internal/github/org_repos_test.go b/extensions/internal/github/org_repos_test.go similarity index 91% rename from tables/internal/github/org_repos_test.go rename to extensions/internal/github/org_repos_test.go index 6ddd32c0..3248e9d9 100644 --- a/tables/internal/github/org_repos_test.go +++ b/extensions/internal/github/org_repos_test.go @@ -3,7 +3,7 @@ package github_test import ( "testing" - "github.com/askgitdev/askgit/tables/internal/tools" + "github.com/askgitdev/askgit/extensions/internal/tools" ) func TestOrgRepos(t *testing.T) { diff --git a/tables/internal/github/repo_issues.go b/extensions/internal/github/repo_issues.go similarity index 100% rename from tables/internal/github/repo_issues.go rename to extensions/internal/github/repo_issues.go diff --git a/tables/internal/github/repo_issues_test.go b/extensions/internal/github/repo_issues_test.go similarity index 91% rename from tables/internal/github/repo_issues_test.go rename to extensions/internal/github/repo_issues_test.go index 1498874c..e7e856d3 100644 --- a/tables/internal/github/repo_issues_test.go +++ b/extensions/internal/github/repo_issues_test.go @@ -3,7 +3,7 @@ package github_test import ( "testing" - "github.com/askgitdev/askgit/tables/internal/tools" + "github.com/askgitdev/askgit/extensions/internal/tools" ) func TestRepoIssues(t *testing.T) { diff --git a/tables/internal/github/repo_prs.go b/extensions/internal/github/repo_prs.go similarity index 100% rename from tables/internal/github/repo_prs.go rename to extensions/internal/github/repo_prs.go diff --git a/tables/internal/github/repo_prs_test.go b/extensions/internal/github/repo_prs_test.go similarity index 91% rename from tables/internal/github/repo_prs_test.go rename to extensions/internal/github/repo_prs_test.go index 74bc6edf..975812f9 100644 --- a/tables/internal/github/repo_prs_test.go +++ b/extensions/internal/github/repo_prs_test.go @@ -3,7 +3,7 @@ package github_test import ( "testing" - "github.com/askgitdev/askgit/tables/internal/tools" + "github.com/askgitdev/askgit/extensions/internal/tools" ) func TestRepoPRs(t *testing.T) { diff --git a/tables/internal/github/stargazers.go b/extensions/internal/github/stargazers.go similarity index 100% rename from tables/internal/github/stargazers.go rename to extensions/internal/github/stargazers.go diff --git a/tables/internal/github/stargazers_count.go b/extensions/internal/github/stargazers_count.go similarity index 100% rename from tables/internal/github/stargazers_count.go rename to extensions/internal/github/stargazers_count.go diff --git a/tables/internal/github/stargazers_count_test.go b/extensions/internal/github/stargazers_count_test.go similarity index 100% rename from tables/internal/github/stargazers_count_test.go rename to extensions/internal/github/stargazers_count_test.go diff --git a/tables/internal/github/stargazers_test.go b/extensions/internal/github/stargazers_test.go similarity index 91% rename from tables/internal/github/stargazers_test.go rename to extensions/internal/github/stargazers_test.go index a4a01061..19c57398 100644 --- a/tables/internal/github/stargazers_test.go +++ b/extensions/internal/github/stargazers_test.go @@ -3,7 +3,7 @@ package github_test import ( "testing" - "github.com/askgitdev/askgit/tables/internal/tools" + "github.com/askgitdev/askgit/extensions/internal/tools" ) func TestStargazers(t *testing.T) { diff --git a/tables/internal/github/starred_repos.go b/extensions/internal/github/starred_repos.go similarity index 100% rename from tables/internal/github/starred_repos.go rename to extensions/internal/github/starred_repos.go diff --git a/tables/internal/github/starred_repos_test.go b/extensions/internal/github/starred_repos_test.go similarity index 91% rename from tables/internal/github/starred_repos_test.go rename to extensions/internal/github/starred_repos_test.go index c5b4d6a0..b47c3567 100644 --- a/tables/internal/github/starred_repos_test.go +++ b/extensions/internal/github/starred_repos_test.go @@ -3,7 +3,7 @@ package github_test import ( "testing" - "github.com/askgitdev/askgit/tables/internal/tools" + "github.com/askgitdev/askgit/extensions/internal/tools" ) func TestStarredRepos(t *testing.T) { diff --git a/tables/internal/github/user_repos.go b/extensions/internal/github/user_repos.go similarity index 100% rename from tables/internal/github/user_repos.go rename to extensions/internal/github/user_repos.go diff --git a/tables/internal/github/user_repos_test.go b/extensions/internal/github/user_repos_test.go similarity index 91% rename from tables/internal/github/user_repos_test.go rename to extensions/internal/github/user_repos_test.go index d5ce7a96..d65b8384 100644 --- a/tables/internal/github/user_repos_test.go +++ b/extensions/internal/github/user_repos_test.go @@ -3,7 +3,7 @@ package github_test import ( "testing" - "github.com/askgitdev/askgit/tables/internal/tools" + "github.com/askgitdev/askgit/extensions/internal/tools" ) func TestUserRepos(t *testing.T) { diff --git a/tables/internal/github/utils.go b/extensions/internal/github/utils.go similarity index 98% rename from tables/internal/github/utils.go rename to extensions/internal/github/utils.go index d88b5645..b1716f93 100644 --- a/tables/internal/github/utils.go +++ b/extensions/internal/github/utils.go @@ -6,7 +6,7 @@ import ( "strings" "time" - "github.com/askgitdev/askgit/tables/services" + "github.com/askgitdev/askgit/extensions/services" "github.com/shurcooL/githubv4" "golang.org/x/time/rate" ) diff --git a/extensions/internal/golang/go_mod_to_json.go b/extensions/internal/golang/go_mod_to_json.go new file mode 100644 index 00000000..354f356c --- /dev/null +++ b/extensions/internal/golang/go_mod_to_json.go @@ -0,0 +1,111 @@ +package golang + +import ( + "encoding/json" + + "go.riyazali.net/sqlite" + "golang.org/x/mod/modfile" + "golang.org/x/mod/module" +) + +type GoModToJSON struct{} + +type Version struct { + Path string `json:"path"` + Version string `json:"version,omitempty"` +} + +type VersionInterval struct { + Low string `json:"low"` + High string `json:"high"` +} + +type Require struct { + Mod Version `json:"mod"` + Indirect bool `json:"indirect,omitempty"` +} + +type Exclude struct { + Mod Version +} + +type Replace struct { + Old Version `json:"old"` + New Version `json:"new"` +} + +type Retract struct { + VersionInterval + Rationale string `json:"rationale"` +} + +type GoModFile struct { + Version Version `json:"version"` + Go string `json:"go"` + Require []*Require `json:"require"` + Exclude []*Exclude `json:"exclude"` + Replace []*Replace `json:"replace"` + Retract []*Retract `json:"retract"` +} + +func goModVersionToVersion(v module.Version) Version { + return Version{ + Path: v.Path, + Version: v.Version, + } +} + +func (f *GoModToJSON) Args() int { return 1 } +func (f *GoModToJSON) Deterministic() bool { return true } +func (f *GoModToJSON) Apply(context *sqlite.Context, value ...sqlite.Value) { + parsed, err := modfile.Parse("go.mod", value[0].Blob(), nil) + if err != nil { + context.ResultError(err) + return + } + file := &GoModFile{ + Version: goModVersionToVersion(parsed.Module.Mod), + Go: parsed.Go.Version, + Require: make([]*Require, len(parsed.Require)), + Exclude: make([]*Exclude, len(parsed.Exclude)), + Replace: make([]*Replace, len(parsed.Replace)), + Retract: make([]*Retract, len(parsed.Retract)), + } + + for i, r := range parsed.Require { + file.Require[i] = &Require{ + Mod: goModVersionToVersion(r.Mod), + Indirect: r.Indirect, + } + } + + for i, e := range parsed.Exclude { + file.Exclude[i] = &Exclude{ + Mod: goModVersionToVersion(e.Mod), + } + } + + for i, r := range parsed.Replace { + file.Replace[i] = &Replace{ + Old: goModVersionToVersion(r.Old), + New: goModVersionToVersion(r.New), + } + } + + for i, r := range parsed.Retract { + file.Retract[i] = &Retract{ + VersionInterval: VersionInterval{ + Low: r.Low, High: r.High, + }, + Rationale: r.Rationale, + } + } + + str, err := json.Marshal(file) + if err != nil { + context.ResultError(err) + return + } + + context.ResultText(string(str)) +} diff --git a/extensions/internal/golang/golang.go b/extensions/internal/golang/golang.go new file mode 100644 index 00000000..57132f72 --- /dev/null +++ b/extensions/internal/golang/golang.go @@ -0,0 +1,21 @@ +package golang + +import ( + "github.com/askgitdev/askgit/extensions/options" + "github.com/pkg/errors" + "go.riyazali.net/sqlite" +) + +// Register registers golang related functionality as a SQLite extension +func Register(ext *sqlite.ExtensionApi, opt *options.Options) (_ sqlite.ErrorCode, err error) { + var fns = map[string]sqlite.Function{ + "go_mod_to_json": &GoModToJSON{}, + } + + for name, fn := range fns { + if err = ext.CreateFunction(name, fn); err != nil { + return sqlite.SQLITE_ERROR, errors.Wrapf(err, "failed to register golang %q function", name) + } + } + return sqlite.SQLITE_OK, nil +} diff --git a/extensions/internal/helpers/helpers.go b/extensions/internal/helpers/helpers.go new file mode 100644 index 00000000..2e11cddf --- /dev/null +++ b/extensions/internal/helpers/helpers.go @@ -0,0 +1,28 @@ +package helpers + +import ( + "github.com/askgitdev/askgit/extensions/options" + "github.com/pkg/errors" + "go.riyazali.net/sqlite" +) + +// Register registers helpers as a SQLite extension +func Register(ext *sqlite.ExtensionApi, _ *options.Options) (_ sqlite.ErrorCode, err error) { + var fns = map[string]sqlite.Function{ + "str_split": &StringSplit{}, + "toml_to_json": &TomlToJson{}, + "yaml_to_json": &YamlToJson{}, + "xml_to_json": &XmlToJson{}, + } + + // alias yaml_to_json => yml_to_json + fns["yml_to_json"] = fns["yaml_to_json"] + + for name, fn := range fns { + if err = ext.CreateFunction(name, fn); err != nil { + return sqlite.SQLITE_ERROR, errors.Wrapf(err, "failed to register %q function", name) + } + } + + return sqlite.SQLITE_OK, nil +} diff --git a/extensions/internal/helpers/helpers_test.go b/extensions/internal/helpers/helpers_test.go new file mode 100644 index 00000000..4e1f3ee1 --- /dev/null +++ b/extensions/internal/helpers/helpers_test.go @@ -0,0 +1,30 @@ +package helpers + +import ( + "database/sql" + "log" + "os" + "testing" + + _ "github.com/askgitdev/askgit/pkg/sqlite" + "go.riyazali.net/sqlite" +) + +// FixtureDatabase represents the database connection to run the test against +var FixtureDatabase *sql.DB + +func init() { + // register sqlite extension when this package is loaded + sqlite.Register(func(ext *sqlite.ExtensionApi) (_ sqlite.ErrorCode, err error) { + return Register(ext, nil) + }) +} + +func TestMain(m *testing.M) { + var err error + if FixtureDatabase, err = sql.Open("sqlite3", "file:testing.db?mode=memory"); err != nil { + log.Fatalf("failed to open database connection: %v", err) + } + + os.Exit(m.Run()) +} diff --git a/tables/internal/funcs/str_split.go b/extensions/internal/helpers/str_split.go similarity index 97% rename from tables/internal/funcs/str_split.go rename to extensions/internal/helpers/str_split.go index 441291fb..7b4ee872 100644 --- a/tables/internal/funcs/str_split.go +++ b/extensions/internal/helpers/str_split.go @@ -1,8 +1,9 @@ -package funcs +package helpers import ( - "go.riyazali.net/sqlite" "strings" + + "go.riyazali.net/sqlite" ) // StringSplit implements str_split scalar sql function. diff --git a/tables/internal/funcs/str_split_test.go b/extensions/internal/helpers/str_split_test.go similarity index 90% rename from tables/internal/funcs/str_split_test.go rename to extensions/internal/helpers/str_split_test.go index e92901c1..5c262edb 100644 --- a/tables/internal/funcs/str_split_test.go +++ b/extensions/internal/helpers/str_split_test.go @@ -1,8 +1,9 @@ -package funcs +package helpers import ( - "github.com/askgitdev/askgit/tables/internal/tools" "testing" + + "github.com/askgitdev/askgit/extensions/internal/tools" ) func TestStrSplit(t *testing.T) { diff --git a/tables/internal/funcs/toml_to_json.go b/extensions/internal/helpers/toml_to_json.go similarity index 97% rename from tables/internal/funcs/toml_to_json.go rename to extensions/internal/helpers/toml_to_json.go index 163bfa36..274f3100 100644 --- a/tables/internal/funcs/toml_to_json.go +++ b/extensions/internal/helpers/toml_to_json.go @@ -1,4 +1,4 @@ -package funcs +package helpers import ( "encoding/json" diff --git a/tables/internal/funcs/toml_to_json_test.go b/extensions/internal/helpers/toml_to_json_test.go similarity index 85% rename from tables/internal/funcs/toml_to_json_test.go rename to extensions/internal/helpers/toml_to_json_test.go index 58f169ec..15e9f756 100644 --- a/tables/internal/funcs/toml_to_json_test.go +++ b/extensions/internal/helpers/toml_to_json_test.go @@ -1,8 +1,9 @@ -package funcs +package helpers import ( - "github.com/askgitdev/askgit/tables/internal/tools" "testing" + + "github.com/askgitdev/askgit/extensions/internal/tools" ) func TestTomlToJson(t *testing.T) { diff --git a/tables/internal/funcs/xml_to_json.go b/extensions/internal/helpers/xml_to_json.go similarity index 97% rename from tables/internal/funcs/xml_to_json.go rename to extensions/internal/helpers/xml_to_json.go index b27cc495..763c5d91 100644 --- a/tables/internal/funcs/xml_to_json.go +++ b/extensions/internal/helpers/xml_to_json.go @@ -1,4 +1,4 @@ -package funcs +package helpers import ( "github.com/clbanning/mxj/v2" diff --git a/tables/internal/funcs/xml_to_json_test.go b/extensions/internal/helpers/xml_to_json_test.go similarity index 88% rename from tables/internal/funcs/xml_to_json_test.go rename to extensions/internal/helpers/xml_to_json_test.go index 825361e9..8f3f965b 100644 --- a/tables/internal/funcs/xml_to_json_test.go +++ b/extensions/internal/helpers/xml_to_json_test.go @@ -1,9 +1,9 @@ -package funcs +package helpers import ( "testing" - "github.com/askgitdev/askgit/tables/internal/tools" + "github.com/askgitdev/askgit/extensions/internal/tools" ) func TestXmlToJson(t *testing.T) { diff --git a/tables/internal/funcs/yaml_to_json.go b/extensions/internal/helpers/yaml_to_json.go similarity index 97% rename from tables/internal/funcs/yaml_to_json.go rename to extensions/internal/helpers/yaml_to_json.go index 1aae06da..eb1ed612 100644 --- a/tables/internal/funcs/yaml_to_json.go +++ b/extensions/internal/helpers/yaml_to_json.go @@ -1,4 +1,4 @@ -package funcs +package helpers import ( "github.com/ghodss/yaml" diff --git a/tables/internal/funcs/yaml_to_json_test.go b/extensions/internal/helpers/yaml_to_json_test.go similarity index 85% rename from tables/internal/funcs/yaml_to_json_test.go rename to extensions/internal/helpers/yaml_to_json_test.go index 692a6322..f02b2b30 100644 --- a/tables/internal/funcs/yaml_to_json_test.go +++ b/extensions/internal/helpers/yaml_to_json_test.go @@ -1,9 +1,9 @@ -package funcs +package helpers import ( "testing" - "github.com/askgitdev/askgit/tables/internal/tools" + "github.com/askgitdev/askgit/extensions/internal/tools" ) func TestYmlToJson(t *testing.T) { diff --git a/tables/internal/tools/tools.go b/extensions/internal/tools/tools.go similarity index 100% rename from tables/internal/tools/tools.go rename to extensions/internal/tools/tools.go diff --git a/tables/options.go b/extensions/options/options.go similarity index 97% rename from tables/options.go rename to extensions/options/options.go index dce56be9..bf138c44 100644 --- a/tables/options.go +++ b/extensions/options/options.go @@ -1,9 +1,9 @@ -package tables +package options import ( "context" - "github.com/askgitdev/askgit/tables/services" + "github.com/askgitdev/askgit/extensions/services" "github.com/go-git/go-git/v5" "github.com/shurcooL/githubv4" ) diff --git a/tables/services/context.go b/extensions/services/context.go similarity index 100% rename from tables/services/context.go rename to extensions/services/context.go diff --git a/tables/services/doc.go b/extensions/services/doc.go similarity index 100% rename from tables/services/doc.go rename to extensions/services/doc.go diff --git a/tables/services/locator.go b/extensions/services/locator.go similarity index 100% rename from tables/services/locator.go rename to extensions/services/locator.go diff --git a/go.mod b/go.mod index c88b8f9f..0b9fa16c 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( go.mongodb.org/mongo-driver v1.6.0 // indirect go.riyazali.net/sqlite v0.0.0-20210707161919-414349b4032a golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect + golang.org/x/mod v0.4.2 golang.org/x/net v0.0.0-20210716203947-853a461950ff // indirect golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect diff --git a/go.sum b/go.sum index b53ee1ec..f6a9b030 100644 --- a/go.sum +++ b/go.sum @@ -464,6 +464,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/pkg/locator/locator.go b/pkg/locator/locator.go index d0d3f2eb..03572cae 100644 --- a/pkg/locator/locator.go +++ b/pkg/locator/locator.go @@ -14,8 +14,8 @@ import ( "os" "strings" - "github.com/askgitdev/askgit/tables" - "github.com/askgitdev/askgit/tables/services" + "github.com/askgitdev/askgit/extensions/options" + "github.com/askgitdev/askgit/extensions/services" "github.com/go-git/go-billy/v5/osfs" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing/cache" @@ -25,7 +25,7 @@ import ( // DiskLocator is a repo locator implementation that opens on-disk repository at the specified path. func DiskLocator() services.RepoLocator { - return tables.RepoLocatorFn(func(_ context.Context, path string) (*git.Repository, error) { + return options.RepoLocatorFn(func(_ context.Context, path string) (*git.Repository, error) { return git.PlainOpen(path) }) } @@ -36,7 +36,7 @@ func DiskLocator() services.RepoLocator { func CachedLocator(rl services.RepoLocator) services.RepoLocator { var cache = make(map[string]*git.Repository) - return tables.RepoLocatorFn(func(ctx context.Context, path string) (*git.Repository, error) { + return options.RepoLocatorFn(func(ctx context.Context, path string) (*git.Repository, error) { if cached, ok := cache[path]; ok { return cached, nil } @@ -56,7 +56,7 @@ func CachedLocator(rl services.RepoLocator) services.RepoLocator { // that you club it with something like CachedLocator to improve performance // and remove the need to clone a single repository multiple times. func HttpLocator() services.RepoLocator { - return tables.RepoLocatorFn(func(ctx context.Context, path string) (*git.Repository, error) { + return options.RepoLocatorFn(func(ctx context.Context, path string) (*git.Repository, error) { var err error if _, err = url.ParseRequestURI(path); err != nil { return nil, errors.Wrap(err, "invalid remote url") @@ -80,7 +80,7 @@ func MultiLocator() services.RepoLocator { "file": DiskLocator, } - return tables.RepoLocatorFn(func(ctx context.Context, path string) (*git.Repository, error) { + return options.RepoLocatorFn(func(ctx context.Context, path string) (*git.Repository, error) { var fn = locators["file"] // file is the default locator if strings.HasPrefix(path, "http") || strings.HasPrefix(path, "https") { fn = locators["http"] diff --git a/shared.go b/shared.go index 82922065..e33cabd3 100644 --- a/shared.go +++ b/shared.go @@ -1,25 +1,26 @@ // +build shared // This file provides a build target while building the dynamically loadable shared object library. -// It imports github.com/askgitdev/askgit/tables which provides the actual extension implementation. +// It imports github.com/askgitdev/askgit/extensions which provides the actual extension implementation. package main import ( "os" + "github.com/askgitdev/askgit/extensions" + "github.com/askgitdev/askgit/extensions/options" "github.com/askgitdev/askgit/pkg/locator" - "github.com/askgitdev/askgit/tables" "go.riyazali.net/sqlite" ) func init() { - sqlite.Register(tables.RegisterFn( - tables.WithExtraFunctions(), - tables.WithRepoLocator(locator.CachedLocator(locator.MultiLocator())), - tables.WithGitHub(), - tables.WithContextValue("githubToken", os.Getenv("GITHUB_TOKEN")), - tables.WithContextValue("githubPerPage", os.Getenv("GITHUB_PER_PAGE")), - tables.WithContextValue("githubRateLimit", os.Getenv("GITHUB_RATE_LIMIT")), + sqlite.Register(extensions.RegisterFn( + options.WithExtraFunctions(), + options.WithRepoLocator(locator.CachedLocator(locator.MultiLocator())), + options.WithGitHub(), + options.WithContextValue("githubToken", os.Getenv("GITHUB_TOKEN")), + options.WithContextValue("githubPerPage", os.Getenv("GITHUB_PER_PAGE")), + options.WithContextValue("githubRateLimit", os.Getenv("GITHUB_RATE_LIMIT")), )) } diff --git a/tables/internal/funcs/func_test.go b/tables/internal/funcs/func_test.go deleted file mode 100644 index b40b83ed..00000000 --- a/tables/internal/funcs/func_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package funcs - -import ( - "database/sql" - "log" - "os" - "testing" - - _ "github.com/askgitdev/askgit/pkg/sqlite" - "github.com/pkg/errors" - "go.riyazali.net/sqlite" -) - -// FixtureDatabase represents the database connection to run the test against -var FixtureDatabase *sql.DB - -func init() { - // register sqlite extension when this package is loaded - sqlite.Register(func(ext *sqlite.ExtensionApi) (_ sqlite.ErrorCode, err error) { - var fns = map[string]sqlite.Function{ - "str_split": &StringSplit{}, - "toml_to_json": &TomlToJson{}, - "yaml_to_json": &YamlToJson{}, - "xml_to_json": &XmlToJson{}, - "enry_detect_language": &EnryDetectLanguage{}, - "enry_is_binary": &EnryIsBinary{}, - "enry_is_configuration": &EnryIsConfiguration{}, - "enry_is_documentation": &EnryIsDocumentation{}, - "enry_is_dot_file": &EnryIsDotFile{}, - "enry_is_generated": &EnryIsGenerated{}, - "enry_is_image": &EnryIsImage{}, - "enry_is_test": &EnryIsTest{}, - "enry_is_vendor": &EnryIsVendor{}, - } - - // alias yaml_to_json => yml_to_json - fns["yml_to_json"] = fns["yaml_to_json"] - - for name, fn := range fns { - if err = ext.CreateFunction(name, fn); err != nil { - return sqlite.SQLITE_ERROR, errors.Wrapf(err, "failed to register %q function", name) - } - } - - return sqlite.SQLITE_OK, nil - }) -} - -func TestMain(m *testing.M) { - var err error - if FixtureDatabase, err = sql.Open("sqlite3", "file:testing.db?mode=memory"); err != nil { - log.Fatalf("failed to open database connection: %v", err) - } - - os.Exit(m.Run()) -} diff --git a/tables/tables.go b/tables/tables.go deleted file mode 100644 index 44c09986..00000000 --- a/tables/tables.go +++ /dev/null @@ -1,141 +0,0 @@ -// Package tables provide implementation of the various underlying sqlite3 virtual tables [https://www.sqlite.org/vtab.html] -// that askgit uses under-the-hood. This module can be side-effect-imported in other modules to include the functionality -// of the sqlite3 virtual tables there. -package tables - -import ( - "context" - "time" - - "github.com/askgitdev/askgit/tables/internal/funcs" - "github.com/askgitdev/askgit/tables/internal/git" - "github.com/askgitdev/askgit/tables/internal/git/native" - "github.com/askgitdev/askgit/tables/internal/github" - "github.com/pkg/errors" - "github.com/shurcooL/githubv4" - "go.riyazali.net/sqlite" - "golang.org/x/oauth2" - "golang.org/x/time/rate" -) - -func RegisterFn(fns ...OptionFn) func(ext *sqlite.ExtensionApi) (_ sqlite.ErrorCode, err error) { - var opt = &Options{} - for _, fn := range fns { - fn(opt) - } - - // return an extension function that register modules with sqlite when this package is loaded - return func(ext *sqlite.ExtensionApi) (_ sqlite.ErrorCode, err error) { - // register virtual table modules - var modules = map[string]sqlite.Module{ - "commits": &git.LogModule{Locator: opt.Locator, Context: opt.Context}, - "refs": &git.RefModule{Locator: opt.Locator, Context: opt.Context}, - "stats": native.NewStatsModule(opt.Locator, opt.Context), - "files": native.NewFilesModule(opt.Locator, opt.Context), - "blame": native.NewBlameModule(opt.Locator, opt.Context), - } - - for name, mod := range modules { - if err = ext.CreateModule(name, mod); err != nil { - return sqlite.SQLITE_ERROR, errors.Wrapf(err, "failed to register %q module", name) - } - } - - var fns = map[string]sqlite.Function{ - "commit_from_tag": &git.CommitFromTagFn{}, - } - - for name, fn := range fns { - if err = ext.CreateFunction(name, fn); err != nil { - return sqlite.SQLITE_ERROR, errors.Wrapf(err, "failed to register %q function", name) - } - } - - // only conditionally register the utility functions - if opt.ExtraFunctions { - // register sql functions - var fns = map[string]sqlite.Function{ - "str_split": &funcs.StringSplit{}, - "toml_to_json": &funcs.TomlToJson{}, - "yaml_to_json": &funcs.YamlToJson{}, - "xml_to_json": &funcs.XmlToJson{}, - "enry_detect_language": &funcs.EnryDetectLanguage{}, - "enry_is_binary": &funcs.EnryIsBinary{}, - "enry_is_configuration": &funcs.EnryIsConfiguration{}, - "enry_is_documentation": &funcs.EnryIsDocumentation{}, - "enry_is_dot_file": &funcs.EnryIsDotFile{}, - "enry_is_generated": &funcs.EnryIsGenerated{}, - "enry_is_image": &funcs.EnryIsImage{}, - "enry_is_test": &funcs.EnryIsTest{}, - "enry_is_vendor": &funcs.EnryIsVendor{}, - } - - // alias yaml_to_json => yml_to_json - fns["yml_to_json"] = fns["yaml_to_json"] - - for name, fn := range fns { - if err = ext.CreateFunction(name, fn); err != nil { - return sqlite.SQLITE_ERROR, errors.Wrapf(err, "failed to register %q function", name) - } - } - } - - // conditionally register the GitHub functionality - if opt.GitHub { - rateLimiter := github.GetGitHubRateLimitFromCtx(opt.Context) - if rateLimiter == nil { - rateLimiter = rate.NewLimiter(rate.Every(1*time.Second), 2) - } - - githubOpts := &github.Options{ - RateLimiter: rateLimiter, - Client: func() *githubv4.Client { - httpClient := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: github.GetGitHubTokenFromCtx(opt.Context)}, - )) - client := githubv4.NewClient(httpClient) - return client - }, - PerPage: github.GetGitHubPerPageFromCtx(opt.Context), - } - - if opt.GitHubClientGetter != nil { - githubOpts.Client = opt.GitHubClientGetter - } - - var modules = map[string]sqlite.Module{ - "github_stargazers": github.NewStargazersModule(githubOpts), - "github_starred_repos": github.NewStarredReposModule(githubOpts), - "github_user_repos": github.NewUserReposModule(githubOpts), - "github_org_repos": github.NewOrgReposModule(githubOpts), - "github_repo_issues": github.NewIssuesModule(githubOpts), - "github_repo_pull_requests": github.NewPRModule(githubOpts), - } - - modules["github_issues"] = modules["github_repo_issues"] - modules["github_pull_requests"] = modules["github_repo_pull_requests"] - modules["github_prs"] = modules["github_repo_pull_requests"] - modules["github_repo_prs"] = modules["github_repo_pull_requests"] - - // register GitHub tables - for name, mod := range modules { - if err = ext.CreateModule(name, mod); err != nil { - return sqlite.SQLITE_ERROR, errors.Wrapf(err, "failed to register GitHub %q module", name) - } - } - - var fns = map[string]sqlite.Function{ - "github_stargazer_count": github.NewStarredReposFunc(githubOpts), - } - - // register GitHub funcs - for name, fn := range fns { - if err = ext.CreateFunction(name, fn); err != nil { - return sqlite.SQLITE_ERROR, errors.Wrapf(err, "failed to register GitHub %q function", name) - } - } - } - - return sqlite.SQLITE_OK, nil - } -}