You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

235 lines
4.4 KiB

package mime
import (
"errors"
"fmt"
"go/format"
"os"
"path"
"text/template"
"gopkg.in/yaml.v3"
)
var (
ErrDuplicate = errors.New("duplicate")
ErrNotFound = errors.New("not found")
)
const (
NameExt = "ext"
NameMime = "mime"
)
type Config struct {
Source string
Mimes []Data `yaml:"mime"`
MimePrefix string
MimeTpl string
MimeResult string
MimePackage string
Extensions []Data `yaml:"ext"`
ExtPrefix string
ExtTpl string
ExtResult string
ExtPackage string
}
type Data struct {
Name string `yaml:"name"`
ID int `yaml:"id"`
Value []string `yaml:"value,omitempty"`
}
func (c *Config) Base(name string) string {
switch name {
case NameExt:
return path.Base(c.ExtTpl)
case NameMime:
return path.Base(c.MimeTpl)
}
return ""
}
func (c *Config) Result(name string) string {
switch name {
case NameExt:
return c.ExtResult
case NameMime:
return c.MimeResult
}
return ""
}
func (c *Config) ValidateAndFillExt() error {
mimes := make(map[string]int)
mimeIDs := make(map[int]string)
extIndex := make(map[string]int)
for idx, ext := range c.Extensions {
if _, ok := extIndex[ext.Name]; ok {
return fmt.Errorf("extension %v %w: with id %v", ext.Name, ErrDuplicate, c.Extensions[extIndex[ext.Name]].ID)
}
extIndex[ext.Name] = idx
}
for _, mime := range c.Mimes {
if _, ok := mimes[mime.Name]; ok {
return fmt.Errorf("mime %v %w: with id %v", mime.Name, ErrDuplicate, mimes[mime.Name])
}
if _, ok := mimeIDs[mime.ID]; ok {
return fmt.Errorf("ID %v %w: with name %v", mime.ID, ErrDuplicate, mimeIDs[mime.ID])
}
for _, ext := range mime.Value {
idx, ok := extIndex[ext]
if !ok {
return fmt.Errorf("%w ext by %v", ErrNotFound, mime.Name)
}
c.Extensions[idx].Value = append(c.Extensions[idx].Value, mime.Name)
}
}
return nil
}
func WithExtTpl(name string) Option {
return func(c *Config) {
c.ExtTpl = name
}
}
func WithExtPacakge(name string) Option {
return func(c *Config) {
c.ExtPackage = name
}
}
func WithExtResult(name string) Option {
return func(c *Config) {
c.ExtResult = name
}
}
func WithMimeTpl(name string) Option {
return func(c *Config) {
c.MimeTpl = name
}
}
func WithMimePackage(name string) Option {
return func(c *Config) {
c.MimePackage = name
}
}
func WithMimeResult(name string) Option {
return func(c *Config) {
c.MimeResult = name
}
}
type Option func(*Config)
func funcMap() template.FuncMap {
return template.FuncMap{
"name": VarName,
"value": Value,
}
}
func Generate(fileName string, opts ...Option) error {
cfg := Config{
Source: fileName,
ExtPrefix: "Ext",
ExtTpl: "mime/tpl/extension.text.tmpl",
ExtResult: "extension.go",
ExtPackage: "mime",
MimeTpl: "mime/tpl/mime.text.tmpl",
MimePrefix: "",
MimeResult: "mime.go",
MimePackage: "mime",
}
for _, opt := range opts {
opt(&cfg)
}
data, cerr := os.ReadFile(fileName)
if cerr != nil {
return fmt.Errorf("read file:%w", cerr)
}
if uerr := yaml.Unmarshal(data, &cfg); uerr != nil {
return fmt.Errorf("unmarshal:%w", uerr)
}
if validErr := cfg.ValidateAndFillExt(); validErr != nil {
return fmt.Errorf("config:%w", validErr)
}
template, err := template.New("mimes").Funcs(funcMap()).ParseFiles(cfg.ExtTpl, cfg.MimeTpl)
if err != nil {
return fmt.Errorf("ext template:%w", err)
}
if exErr := Create(template, NameExt, cfg); exErr != nil {
return fmt.Errorf("ext create:%w", exErr)
}
if mimeErr := Create(template, NameMime, cfg); mimeErr != nil {
return fmt.Errorf("mime create:%w", mimeErr)
}
return nil
}
func Create(tpl *template.Template, name string, cfg Config) error {
extFile, err := os.Create(cfg.Result(name))
if err != nil {
return fmt.Errorf("ext file:%w", err)
}
if exErr := tpl.ExecuteTemplate(extFile, cfg.Base(name), cfg); exErr != nil {
return fmt.Errorf("ext execute:%w", exErr)
}
if formatErr := Format(extFile.Name()); formatErr != nil {
return fmt.Errorf("format ext:%w", formatErr)
}
return nil
}
// Format file and write it.
func Format(name string) error {
in, err := os.ReadFile(name)
if err != nil {
return fmt.Errorf("read file:%w", err)
}
out, err := format.Source(in)
if err != nil {
return fmt.Errorf("format source:%w", err)
}
file, err := os.Create(name)
if err != nil {
return fmt.Errorf("ext file:%w", err)
}
if _, err := file.Write(out); err != nil {
return fmt.Errorf("write:%w", err)
}
return nil
}