Repository: ariga/atlas
Pull request #346 (2021-12-28 21:24:04)

sql: add MarshalHCL and UnmarshalHCL helpers to all drivers
As discussed with @a8m and @zeevmoney, adding in each driver helper methods:
```go
var (
	hclState = schemahcl.New(schemahcl.WithTypes(TypeRegistry.Specs()))
	// UnmarshalHCL unmarshals an Atlas HCL DDL document into v.
	UnmarshalHCL = schemaspec.UnmarshalerFunc(func(bytes []byte, i interface{}) error {
		return UnmarshalSpec(bytes, hclState, i)
	})
	// MarshalHCL marshals v into an Atlas HCL DDL document.
	MarshalHCL = schemaspec.MarshalerFunc(func(v interface{}) ([]byte, error) {
		return MarshalSpec(v, hclState)
	})
)
```
This should greatly reduce the appearance of this lovely snippet:
```go
schemahcl.New(schemahcl.WithTypes(mysql.TypeRegistry.Specs()))
```


Patch:
diff --git a/sql/mysql/sqlspec.go b/sql/mysql/sqlspec.go
index 95014a55..b890fbfb 100644
--- a/sql/mysql/sqlspec.go
+++ b/sql/mysql/sqlspec.go
@@ -5,6 +5,7 @@ import (
 	"reflect"
 
 	"ariga.io/atlas/schema/schemaspec"
+	"ariga.io/atlas/schema/schemaspec/schemahcl"
 	"ariga.io/atlas/sql/internal/specutil"
 	"ariga.io/atlas/sql/internal/sqlx"
 	"ariga.io/atlas/sql/schema"
@@ -59,6 +60,18 @@ func MarshalSpec(v interface{}, marshaler schemaspec.Marshaler) ([]byte, error)
 	})
 }
 
+var (
+	hclState = schemahcl.New(schemahcl.WithTypes(TypeRegistry.Specs()))
+	// UnmarshalHCL unmarshals an Atlas HCL DDL document into v.
+	UnmarshalHCL = schemaspec.UnmarshalerFunc(func(bytes []byte, i interface{}) error {
+		return UnmarshalSpec(bytes, hclState, i)
+	})
+	// MarshalHCL marshals v into an Atlas HCL DDL document.
+	MarshalHCL = schemaspec.MarshalerFunc(func(v interface{}) ([]byte, error) {
+		return MarshalSpec(v, hclState)
+	})
+)
+
 // convertTable converts a sqlspec.Table to a schema.Table. Table conversion is done without converting
 // ForeignKeySpecs into ForeignKeys, as the target tables do not necessarily exist in the schema
 // at this point. Instead, the linking is done by the convertSchema function.
diff --git a/sql/postgres/sqlspec.go b/sql/postgres/sqlspec.go
index 4faf7e08..4a09ca54 100644
--- a/sql/postgres/sqlspec.go
+++ b/sql/postgres/sqlspec.go
@@ -8,6 +8,7 @@ import (
 	"unicode"
 
 	"ariga.io/atlas/schema/schemaspec"
+	"ariga.io/atlas/schema/schemaspec/schemahcl"
 	"ariga.io/atlas/sql/internal/specutil"
 	"ariga.io/atlas/sql/internal/sqlx"
 	"ariga.io/atlas/sql/schema"
@@ -226,3 +227,15 @@ var TypeRegistry = specutil.NewRegistry(
 		specutil.TypeSpec("sql", &schemaspec.TypeAttr{Name: "def", Required: true, Kind: reflect.String}),
 	),
 )
+
+var (
+	hclState = schemahcl.New(schemahcl.WithTypes(TypeRegistry.Specs()))
+	// UnmarshalHCL unmarshals an Atlas HCL DDL document into v.
+	UnmarshalHCL = schemaspec.UnmarshalerFunc(func(bytes []byte, i interface{}) error {
+		return UnmarshalSpec(bytes, hclState, i)
+	})
+	// MarshalHCL marshals v into an Atlas HCL DDL document.
+	MarshalHCL = schemaspec.MarshalerFunc(func(v interface{}) ([]byte, error) {
+		return MarshalSpec(v, hclState)
+	})
+)
diff --git a/sql/sqlite/sqlspec.go b/sql/sqlite/sqlspec.go
index 3a706d44..38800ba3 100644
--- a/sql/sqlite/sqlspec.go
+++ b/sql/sqlite/sqlspec.go
@@ -5,6 +5,7 @@ import (
 	"reflect"
 
 	"ariga.io/atlas/schema/schemaspec"
+	"ariga.io/atlas/schema/schemaspec/schemahcl"
 	"ariga.io/atlas/sql/internal/specutil"
 	"ariga.io/atlas/sql/schema"
 	"ariga.io/atlas/sql/sqlspec"
@@ -139,3 +140,15 @@ var TypeRegistry = specutil.NewRegistry(
 		specutil.TypeSpec("uuid"),
 	),
 )
+
+var (
+	hclState = schemahcl.New(schemahcl.WithTypes(TypeRegistry.Specs()))
+	// UnmarshalHCL unmarshals an Atlas HCL DDL document into v.
+	UnmarshalHCL = schemaspec.UnmarshalerFunc(func(bytes []byte, i interface{}) error {
+		return UnmarshalSpec(bytes, hclState, i)
+	})
+	// MarshalHCL marshals v into an Atlas HCL DDL document.
+	MarshalHCL = schemaspec.MarshalerFunc(func(v interface{}) ([]byte, error) {
+		return MarshalSpec(v, hclState)
+	})
+)


Test patch:
diff --git a/internal/integration/mysql_test.go b/internal/integration/mysql_test.go
index 0d8fb158..dd907baf 100644
--- a/internal/integration/mysql_test.go
+++ b/internal/integration/mysql_test.go
@@ -547,11 +547,10 @@ create table atlas_defaults
 		_, err := t.db.Exec(ddl)
 		require.NoError(t, err)
 		realm := t.loadRealm()
-		hcl := schemahcl.New(schemahcl.WithTypes(mysql.TypeRegistry.Specs()))
-		spec, err := mysql.MarshalSpec(realm.Schemas[0], hcl)
+		spec, err := mysql.MarshalHCL(realm.Schemas[0])
 		require.NoError(t, err)
 		var s schema.Schema
-		err = mysql.UnmarshalSpec(spec, hcl, &s)
+		err = mysql.UnmarshalHCL(spec, &s)
 		require.NoError(t, err)
 		t.dropTables(n)
 		t.applyHcl(string(spec))
@@ -942,7 +941,7 @@ func (t *myTest) dsn() string {
 func (t *myTest) applyHcl(spec string) {
 	realm := t.loadRealm()
 	var desired schema.Schema
-	err := mysql.UnmarshalSpec([]byte(spec), schemahcl.New(schemahcl.WithTypes(mysql.TypeRegistry.Specs())), &desired)
+	err := mysql.UnmarshalHCL([]byte(spec), &desired)
 	require.NoError(t, err)
 	existing := realm.Schemas[0]
 	diff, err := t.drv.SchemaDiff(existing, &desired)
diff --git a/internal/integration/postgres_test.go b/internal/integration/postgres_test.go
index c94d17a7..ed730291 100644
--- a/internal/integration/postgres_test.go
+++ b/internal/integration/postgres_test.go
@@ -457,11 +457,10 @@ create table atlas_defaults
 		_, err := t.db.Exec(ddl)
 		require.NoError(t, err)
 		realm := t.loadRealm()
-		hcl := schemahcl.New(schemahcl.WithTypes(postgres.TypeRegistry.Specs()))
-		spec, err := postgres.MarshalSpec(realm.Schemas[0], hcl)
+		spec, err := postgres.MarshalHCL(realm.Schemas[0])
 		require.NoError(t, err)
 		var s schema.Schema
-		err = postgres.UnmarshalSpec(spec, hcl, &s)
+		err = postgres.UnmarshalHCL(spec, &s)
 		require.NoError(t, err)
 		t.dropTables(n)
 		t.applyHcl(string(spec))
@@ -855,7 +854,7 @@ func (t *pgTest) dsn() string {
 func (t *pgTest) applyHcl(spec string) {
 	realm := t.loadRealm()
 	var desired schema.Schema
-	err := postgres.UnmarshalSpec([]byte(spec), schemahcl.New(schemahcl.WithTypes(postgres.TypeRegistry.Specs())), &desired)
+	err := postgres.UnmarshalHCL([]byte(spec), &desired)
 	require.NoError(t, err)
 	existing := realm.Schemas[0]
 	diff, err := t.drv.SchemaDiff(existing, &desired)
diff --git a/internal/integration/sqlite_test.go b/internal/integration/sqlite_test.go
index e979f347..595e5bad 100644
--- a/internal/integration/sqlite_test.go
+++ b/internal/integration/sqlite_test.go
@@ -10,7 +10,6 @@ import (
 	"database/sql/driver"
 	"testing"
 
-	"ariga.io/atlas/schema/schemaspec/schemahcl"
 	"ariga.io/atlas/sql/postgres"
 	"ariga.io/atlas/sql/schema"
 	"ariga.io/atlas/sql/sqlite"
@@ -412,11 +411,10 @@ create table atlas_defaults
 		_, err := t.db.Exec(ddl)
 		require.NoError(t, err)
 		realm := t.loadRealm()
-		hcl := schemahcl.New(schemahcl.WithTypes(sqlite.TypeRegistry.Specs()))
-		spec, err := sqlite.MarshalSpec(realm.Schemas[0], hcl)
+		spec, err := sqlite.MarshalHCL(realm.Schemas[0])
 		require.NoError(t, err)
 		var s schema.Schema
-		err = sqlite.UnmarshalSpec(spec, hcl, &s)
+		err = sqlite.UnmarshalHCL(spec, &s)
 		require.NoError(t, err)
 		t.dropTables(n)
 		t.applyHcl(string(spec))
@@ -669,7 +667,7 @@ create table atlas_types_sanity
 func (t *liteTest) applyHcl(spec string) {
 	realm := t.loadRealm()
 	var desired schema.Schema
-	err := sqlite.UnmarshalSpec([]byte(spec), schemahcl.New(schemahcl.WithTypes(sqlite.TypeRegistry.Specs())), &desired)
+	err := sqlite.UnmarshalHCL([]byte(spec), &desired)
 	require.NoError(t, err)
 	existing := realm.Schemas[0]
 	diff, err := t.drv.SchemaDiff(existing, &desired)
diff --git a/sql/mysql/sqlspec_test.go b/sql/mysql/sqlspec_test.go
index 626a13da..a7b17de1 100644
--- a/sql/mysql/sqlspec_test.go
+++ b/sql/mysql/sqlspec_test.go
@@ -4,13 +4,10 @@ import (
 	"fmt"
 	"testing"
 
-	"ariga.io/atlas/schema/schemaspec/schemahcl"
 	"ariga.io/atlas/sql/schema"
 	"github.com/stretchr/testify/require"
 )
 
-var hclState = schemahcl.New(schemahcl.WithTypes(TypeRegistry.Specs()))
-
 func TestSQLSpec(t *testing.T) {
 	f := `
 schema "schema" {
@@ -57,7 +54,7 @@ table "accounts" {
 }
 `
 	var s schema.Schema
-	err := UnmarshalSpec([]byte(f), hclState, &s)
+	err := UnmarshalHCL([]byte(f), &s)
 	require.NoError(t, err)
 
 	exp := &schema.Schema{
@@ -259,7 +256,7 @@ schema "test" {
 			&schema.Collation{V: "utf8mb4_0900_ai_ci"},
 		}
 	)
-	require.NoError(t, UnmarshalSpec(buf, hclState, &s2))
+	require.NoError(t, UnmarshalHCL(buf, &s2))
 	require.Equal(t, utf8mb4, s2.Attrs)
 	posts, ok := s2.Table("posts")
 	require.True(t, ok)
@@ -532,14 +529,14 @@ schema "test" {
 }
 `, tt.typeExpr, lineIfSet(tt.extraAttr))
 			var test schema.Schema
-			err := UnmarshalSpec([]byte(doc), hclState, &test)
+			err := UnmarshalHCL([]byte(doc), &test)
 			require.NoError(t, err)
 			colspec := test.Tables[0].Columns[0]
 			require.EqualValues(t, tt.expected, colspec.Type.Type)
-			spec, err := MarshalSpec(&test, hclState)
+			spec, err := MarshalHCL(&test)
 			require.NoError(t, err)
 			var after schema.Schema
-			err = UnmarshalSpec(spec, hclState, &after)
+			err = UnmarshalHCL(spec, &after)
 			require.NoError(t, err)
 			require.EqualValues(t, tt.expected, after.Tables[0].Columns[0].Type.Type)
 		})
diff --git a/sql/postgres/sqlspec_test.go b/sql/postgres/sqlspec_test.go
index 6b89b90d..45fefd29 100644
--- a/sql/postgres/sqlspec_test.go
+++ b/sql/postgres/sqlspec_test.go
@@ -4,14 +4,11 @@ import (
 	"fmt"
 	"testing"
 
-	"ariga.io/atlas/schema/schemaspec/schemahcl"
 	"ariga.io/atlas/sql/internal/spectest"
 	"ariga.io/atlas/sql/schema"
 	"github.com/stretchr/testify/require"
 )
 
-var hclState = schemahcl.New(schemahcl.WithTypes(TypeRegistry.Specs()))
-
 func TestSQLSpec(t *testing.T) {
 	f := `
 schema "schema" {
@@ -61,7 +58,7 @@ table "accounts" {
 }
 `
 	var s schema.Schema
-	err := UnmarshalSpec([]byte(f), hclState, &s)
+	err := UnmarshalHCL([]byte(f), &s)
 	require.NoError(t, err)
 	exp := &schema.Schema{
 		Name: "schema",
@@ -383,14 +380,14 @@ func TestTypes(t *testing.T) {
 schema "test" {
 }
 `, tt.typeExpr)
-			err := UnmarshalSpec([]byte(doc), hclState, &test)
+			err := UnmarshalHCL([]byte(doc), &test)
 			require.NoError(t, err)
 			colspec := test.Tables[0].Columns[0]
 			require.EqualValues(t, tt.expected, colspec.Type.Type)
-			spec, err := MarshalSpec(&test, hclState)
+			spec, err := MarshalHCL(&test)
 			require.NoError(t, err)
 			var after schema.Schema
-			err = UnmarshalSpec(spec, hclState, &after)
+			err = UnmarshalHCL(spec, &after)
 			require.NoError(t, err)
 			require.EqualValues(t, tt.expected, after.Tables[0].Columns[0].Type.Type)
 		})
diff --git a/sql/sqlite/sqlspec_test.go b/sql/sqlite/sqlspec_test.go
index 92ad94e5..586d137c 100644
--- a/sql/sqlite/sqlspec_test.go
+++ b/sql/sqlite/sqlspec_test.go
@@ -8,13 +8,10 @@ import (
 	"fmt"
 	"testing"
 
-	"ariga.io/atlas/schema/schemaspec/schemahcl"
 	"ariga.io/atlas/sql/schema"
 	"github.com/stretchr/testify/require"
 )
 
-var hclState = schemahcl.New(schemahcl.WithTypes(TypeRegistry.Specs()))
-
 func TestSQLSpec(t *testing.T) {
 	f := `
 schema "schema" {