diff --git a/packages/echo/program.ts b/packages/echo/program.ts index 6c5c76df..50602b8c 100644 --- a/packages/echo/program.ts +++ b/packages/echo/program.ts @@ -3,13 +3,13 @@ import * as tdl from '@unmango/tdl'; import * as cmd from './command'; import { name, version } from './package.json'; -const mimeTypeOption = new Option('--type ', 'The media type of the input.') - .choices(tdl.SUPPORTED_MIME_TYPES); +const mediaTypeOption = new Option('--type ', 'The media type of the input.') + .choices(tdl.SUPPORTED_MEDIA_TYPES); export const from = (program: Command): Command => { program.command('from') .description('Write back the protobuf data.') - .addOption(mimeTypeOption) + .addOption(mediaTypeOption) .action(async (_) => { const echo = new cmd.Echo(); const spec = await echo.from(Bun.stdin); @@ -22,7 +22,7 @@ export const from = (program: Command): Command => { export const gen = (program: Command): Command => { program.command('gen') .description('Also, write back the protobuf data.') - .addOption(mimeTypeOption) + .addOption(mediaTypeOption) .action(async (_) => { const echo = new cmd.Echo(); const spec = await echo.from(Bun.stdin); diff --git a/packages/tdl/index.ts b/packages/tdl/index.ts index d2b0c32b..064b9cd0 100644 --- a/packages/tdl/index.ts +++ b/packages/tdl/index.ts @@ -1,3 +1,3 @@ -export * from './mime'; +export * from './mediaType'; export * from './runner'; export * from './types'; diff --git a/packages/tdl/mime.spec.ts b/packages/tdl/mediaType.spec.ts similarity index 90% rename from packages/tdl/mime.spec.ts rename to packages/tdl/mediaType.spec.ts index 271fa2f9..f957f52d 100644 --- a/packages/tdl/mime.spec.ts +++ b/packages/tdl/mediaType.spec.ts @@ -1,7 +1,7 @@ import { Spec } from '@unmango/tdl-es'; import { describe, expect, it } from 'bun:test'; import fc from 'fast-check'; -import { read, type SupportedMimeType } from './mime'; +import { read, type SupportedMediaType } from './mediaType'; const arbSpec = () => fc.gen().map(g => @@ -15,7 +15,7 @@ const arbSpec = () => ); describe('read', () => { - it.each([ + it.each([ 'application/protobuf', 'application/x-protobuf', 'application/vnd.google.protobuf', diff --git a/packages/tdl/mime.ts b/packages/tdl/mediaType.ts similarity index 70% rename from packages/tdl/mime.ts rename to packages/tdl/mediaType.ts index 63bcb795..065eb192 100644 --- a/packages/tdl/mime.ts +++ b/packages/tdl/mediaType.ts @@ -1,16 +1,16 @@ import * as tdl from '@unmango/tdl-es'; -export const SUPPORTED_MIME_TYPES = [ +export const SUPPORTED_MEDIA_TYPES = [ 'application/json', 'application/x-protobuf', 'application/protobuf', 'application/vnd.google.protobuf', ] as const; -export type SupportedMimeTypeTuple = typeof SUPPORTED_MIME_TYPES; -export type SupportedMimeType = SupportedMimeTypeTuple[number]; +export type SupportedMediaTypeTuple = typeof SUPPORTED_MEDIA_TYPES; +export type SupportedMediaType = SupportedMediaTypeTuple[number]; -export function read(data: Uint8Array, type?: SupportedMimeType): tdl.Spec { +export function read(data: Uint8Array, type?: SupportedMediaType): tdl.Spec { switch (type) { case 'application/json': { const decoder = new TextDecoder(); diff --git a/packages/uml2ts/command.spec.ts b/packages/uml2ts/command.spec.ts index 3e0f5c11..55a84cd4 100644 --- a/packages/uml2ts/command.spec.ts +++ b/packages/uml2ts/command.spec.ts @@ -1,4 +1,4 @@ -import type { SupportedMimeType } from '@unmango/tdl'; +import type { SupportedMediaType } from '@unmango/tdl'; import { Spec } from '@unmango/tdl-es'; import { afterAll, beforeAll, describe, expect, it } from 'bun:test'; import fs from 'node:fs/promises'; @@ -32,16 +32,16 @@ beforeAll(async () => { afterAll(ensureClean); describe('gen', () => { - it.each([ + it.each([ 'application/protobuf', 'application/x-protobuf', 'application/vnd.google.protobuf', - ])('should read %s data', async (mime) => { + ])('should read %s data', async (mediaType) => { const name = 'testType'; const spec = new Spec({ types: { [name]: {} } }); const bytes = spec.toBinary(); - const proc = Bun.spawn([binPath, 'gen', '--type', mime], { + const proc = Bun.spawn([binPath, 'gen', '--type', mediaType], { stdin: new Blob([bytes]), }); @@ -53,9 +53,9 @@ describe('gen', () => { const name = 'testType'; const spec = new Spec({ types: { [name]: {} } }); const json = spec.toJsonString(); - const mime: SupportedMimeType = 'application/json'; + const media: SupportedMediaType = 'application/json'; - const proc = Bun.spawn([binPath, 'gen', '--type', mime], { + const proc = Bun.spawn([binPath, 'gen', '--type', media], { stdin: Buffer.from(json, 'utf-8'), }); diff --git a/packages/uml2ts/command.ts b/packages/uml2ts/command.ts index 864e4043..b695a147 100644 --- a/packages/uml2ts/command.ts +++ b/packages/uml2ts/command.ts @@ -2,7 +2,7 @@ import { gen as generate } from '@unmango/2ts'; import * as tdl from '@unmango/tdl'; import { ArrayBufferSink } from 'bun'; -export async function gen(type?: tdl.SupportedMimeType): Promise { +export async function gen(type?: tdl.SupportedMediaType): Promise { const buffer = await Bun.stdin.arrayBuffer(); const spec = tdl.read(new Uint8Array(buffer), type); const sink = new ArrayBufferSink(); diff --git a/packages/uml2ts/program.ts b/packages/uml2ts/program.ts index 836119bb..d7aea643 100644 --- a/packages/uml2ts/program.ts +++ b/packages/uml2ts/program.ts @@ -3,13 +3,13 @@ import * as tdl from '@unmango/tdl'; import * as cmd from './command'; import { name, version } from './package.json'; -const mimeTypeOption = new Option('--type ', 'The media type of the input.') - .choices(tdl.SUPPORTED_MIME_TYPES); +const mediaTypeOption = new Option('--type ', 'The media type of the input.') + .choices(tdl.SUPPORTED_MEDIA_TYPES); export const gen = (program: Command): Command => program.command('gen') .description('Generate typescript.') - .addOption(mimeTypeOption) + .addOption(mediaTypeOption) .action((opts) => cmd.gen(opts.type)); export const program = (): Command => diff --git a/pkg/go.mod b/pkg/go.mod index dbb88e07..d7afe91f 100644 --- a/pkg/go.mod +++ b/pkg/go.mod @@ -11,6 +11,7 @@ require ( github.com/pulumi/pulumi/pkg/v3 v3.121.0 github.com/unstoppablemango/tdl/gen v0.0.18 google.golang.org/protobuf v1.34.2 + gopkg.in/yaml.v3 v3.0.1 ) require ( @@ -125,7 +126,6 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 // indirect google.golang.org/grpc v1.64.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.5.1 // indirect lukechampine.com/frand v1.4.2 // indirect ) diff --git a/pkg/go.sum b/pkg/go.sum index 1091cc2e..2d996798 100644 --- a/pkg/go.sum +++ b/pkg/go.sum @@ -204,12 +204,8 @@ github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 h1:vkHw5I/plNdTr435 github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231/go.mod h1:murToZ2N9hNJzewjHBgfFdXhZKjY3z5cYC1VXk+lbFE= github.com/pulumi/esc v0.9.1 h1:HH5eEv8sgyxSpY5a8yePyqFXzA8cvBvapfH8457+mIs= github.com/pulumi/esc v0.9.1/go.mod h1:oEJ6bOsjYlQUpjf70GiX+CXn3VBmpwFDxUTlmtUN84c= -github.com/pulumi/pulumi/pkg/v3 v3.120.0 h1:L2b8DiI4dQa+iRGXdbrMQl2AXUfnjP3siHEas6xPW84= -github.com/pulumi/pulumi/pkg/v3 v3.120.0/go.mod h1:ZLTGs4I1q5VXzjV5LLBLVIAfE811TMsSDFJaWs7WD4g= github.com/pulumi/pulumi/pkg/v3 v3.121.0 h1:cLUQJYGJKfgCY0ubJo8dVwmsIm2WcgTprb9Orc/yiFg= github.com/pulumi/pulumi/pkg/v3 v3.121.0/go.mod h1:aaRixfKOh4DhGtuDJcI56dTPkb7oJBgRgH1aMF1FzbU= -github.com/pulumi/pulumi/sdk/v3 v3.120.0 h1:KYtMkCmcSg4U+w41/Q0l3llKEodbfdyq6J0VMoEoVmY= -github.com/pulumi/pulumi/sdk/v3 v3.120.0/go.mod h1:/mQJPO+HehhoSJ9O3C6eUKAGeAr+4KSrbDhLsXHKldc= github.com/pulumi/pulumi/sdk/v3 v3.121.0 h1:UsnFKIVOtJN/hQKPkWHL9cZktewPVQRbNUXbXQY/qrk= github.com/pulumi/pulumi/sdk/v3 v3.121.0/go.mod h1:p1U24en3zt51agx+WlNboSOV8eLlPWYAkxMzVEXKbnY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= diff --git a/pkg/uml/converter.go b/pkg/uml/converter.go index 38c55d8a..30a8a2af 100644 --- a/pkg/uml/converter.go +++ b/pkg/uml/converter.go @@ -6,7 +6,7 @@ import ( ) type ConverterOptions struct { - MimeType *string + MediaType *string } type ConverterOption func(*ConverterOptions) error @@ -19,9 +19,9 @@ type NewConverter[T any] interface { RunnerFactory[T, Converter] } -func WithMimeType(t string) ConverterOption { +func WithMediaType(t string) ConverterOption { return func(opts *ConverterOptions) error { - opts.MimeType = &t + opts.MediaType = &t return nil } } diff --git a/pkg/uml/marshal.go b/pkg/uml/marshal.go index 8e726d89..ac14378a 100644 --- a/pkg/uml/marshal.go +++ b/pkg/uml/marshal.go @@ -4,26 +4,80 @@ import ( "encoding/json" "fmt" "mime" + "regexp" "google.golang.org/protobuf/proto" + "gopkg.in/yaml.v3" ) -func Unmarshal(v string, b []byte, spec *Spec) error { - media, _, err := mime.ParseMediaType(v) +var ( + jsonMatcher = regexp.MustCompile(`.*\.json`) + yamlMatcher = regexp.MustCompile(`.*\.ya?ml`) +) + +func Marshal(typ string, spec *Spec) ([]byte, error) { + mediaType, err := parseMediaType(typ) + if err != nil { + return nil, err + } + + switch mediaType { + case "application/json": + case "text/json": + return json.Marshal(spec) + case "application/x-protobuf": + case "application/protobuf": + case "application/vnd.google.protobuf": + return proto.Marshal(spec) + case "application/x-yaml": + case "application/yaml": + case "text/yaml": + return yaml.Marshal(spec) + } + + return nil, fmt.Errorf("unsupported media type: %s", mediaType) +} + +func Unmarshal(typ string, data []byte, spec *Spec) error { + mediaType, err := parseMediaType(typ) if err != nil { return err } - switch media { + switch mediaType { case "application/json": - err = json.Unmarshal(b, spec) + case "text/json": + return json.Unmarshal(data, spec) case "application/x-protobuf": case "application/protobuf": case "application/vnd.google.protobuf": - err = proto.Unmarshal(b, spec) - default: - err = fmt.Errorf("unsupported media type: %s", media) + return proto.Unmarshal(data, spec) + case "application/x-yaml": + case "application/yaml": + case "text/yaml": + return yaml.Unmarshal(data, spec) + } + + return fmt.Errorf("unsupported media type: %s", mediaType) +} + +func GuessMediaType(x string) (string, error) { + if x == "stdin" { + return "application/protobuf", nil } - return err + if yamlMatcher.MatchString(x) { + return "application/yaml", nil + } + + if jsonMatcher.MatchString(x) { + return "application/json", nil + } + + return "", fmt.Errorf("failed to guess media type for: %s", x) +} + +func parseMediaType(x string) (string, error) { + mediaType, _, err := mime.ParseMediaType(x) + return mediaType, err }