Files
theschemagen/main.go
2024-09-30 15:32:17 +00:00

196 lines
4.5 KiB
Go

package theschemagen
import (
"encoding/json"
"fmt"
"regexp"
"strings"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"gopkg.in/yaml.v3"
)
func PrettyPrint(v interface{}, format string) {
var b []byte
var err error
switch format {
case "json":
b, err = json.MarshalIndent(v, "", " ")
case "yaml":
b, err = yaml.Marshal(v)
default:
b, err = yaml.Marshal(v)
}
if err != nil {
panic(err)
}
fmt.Println(string(b))
}
type SchemaObject struct {
Type string `json:"type,omitempty" yaml:"type,omitempty"`
Format string `json:"format,omitempty" yaml:"format,omitempty"`
Items interface{} `json:"items,omitempty" yaml:"items,omitempty"`
Examples []interface{} `json:"examples,omitempty" yaml:"examples,omitempty"`
Properties map[string]SchemaObject `json:"properties,omitempty" yaml:"properties,omitempty"`
}
func ConvertNumber(number float64) SchemaObject {
output := SchemaObject{}
if IsInteger(number) {
output.Type = "integer"
if number < 2147483647 && number > -2147483647 {
output.Format = "int32"
} else if IsSafeInteger(number) {
output.Format = "int64"
}
} else {
output.Type = "number"
}
output.Examples = []interface{}{number}
return output
}
func ConvertArray(array []interface{}) SchemaObject {
output := SchemaObject{Type: "array", Items: nil}
var outputItems []SchemaObject
for _, entry := range array {
objectMap := ConvertObject(entry)
isDuplicate := false
for _, item := range outputItems {
hasSameTypeAndFormat := item.Type == objectMap.Type && item.Format == objectMap.Format
hasSameProperties := item.Properties != nil && objectMap.Properties != nil &&
CompareKeys(item.Properties, objectMap.Properties)
if hasSameTypeAndFormat || hasSameProperties {
isDuplicate = true
break
}
}
if !isDuplicate {
outputItems = append(outputItems, objectMap)
}
}
if len(outputItems) > 1 {
output.Items = map[string]interface{}{"oneOf": outputItems}
} else {
output.Items = outputItems[0]
}
return output
}
func ConvertString(str string) SchemaObject {
output := SchemaObject{Type: "string"}
regxDate := regexp.MustCompile(`^(19|20)\d{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$`)
regxDateTime := regexp.MustCompile(`^(19|20)\d{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([0-1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]Z$`)
if regxDateTime.MatchString(str) {
output.Format = "date-time"
} else if regxDate.MatchString(str) {
output.Format = "date"
}
output.Examples = []interface{}{str}
return output
}
func ConvertObject(input interface{}) SchemaObject {
switch v := input.(type) {
case nil:
return SchemaObject{Type: "null"}
case float64:
return ConvertNumber(v)
case []interface{}:
return ConvertArray(v)
case map[string]interface{}:
output := SchemaObject{Type: "object", Properties: make(map[string]SchemaObject)}
for key, val := range v {
output.Properties[key] = ConvertObject(val)
}
return output
case string:
return ConvertString(v)
case bool:
output := SchemaObject{Type: "boolean"}
output.Examples = []interface{}{v}
return output
default:
panic("Invalid type for conversion")
}
}
func ConvertJSONToOAS(input string) SchemaObject {
var obj map[string]interface{}
err := json.Unmarshal([]byte(input), &obj)
if err != nil {
panic(err)
}
return ConvertObject(obj)
}
func ConvertObjectToOAS(input map[string]interface{}) SchemaObject {
return ConvertObject(input)
}
var ignoredWords = []string{"the", "a", "an", "of", "to", "in", "for", "with", "on", "at", "from", "by", "and"}
func ConvertSummaryToOperationId(summary string) string {
words := strings.Split(summary, " ")
var filteredWords []string
for i, word := range words {
if i == 0 {
filteredWords = append(filteredWords, strings.ToLower(string(word[0]))+word[1:])
} else {
if !Contains(ignoredWords, strings.ToLower(word)) {
filteredWords = append(filteredWords, cases.Title(language.English, cases.NoLower).String(word))
}
}
}
return strings.Join(filteredWords, "")
}
func Contains(arr []string, str string) bool {
for _, v := range arr {
if v == str {
return true
}
}
return false
}
func CompareKeys(m1, m2 map[string]SchemaObject) bool {
if len(m1) != len(m2) {
return false
}
for k := range m1 {
if _, ok := m2[k]; !ok {
return false
}
}
return true
}
func IsInteger(n float64) bool {
return n == float64(int(n))
}
func IsSafeInteger(n float64) bool {
return n <= float64(int64(^uint(0)>>1)) && n >= float64(int64(^uint(0)>>1)*-1)
}