Skip to content

Commit

Permalink
feat: added resolving for consts defined in other files of package
Browse files Browse the repository at this point in the history
  • Loading branch information
dmji committed Jan 1, 2025
1 parent d096e3f commit 284febf
Showing 1 changed file with 91 additions and 6 deletions.
97 changes: 91 additions & 6 deletions goi18n/extract_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,41 @@ func (ec *extractCommand) parse(args []string) error {
return nil
}

func resolveMessageFieldWithConsts(f *string, consts []*constObj) {
s := strings.Split(*f, unresolvedConstIdentifier)
if len(s) != 2 {
return
}

name, pkg := s[0], s[1]
for _, c := range consts {
if c.name == name && c.packageName == pkg {
*f = c.value
return
}

Check warning on line 80 in goi18n/extract_command.go

View check run for this annotation

Codecov / codecov/patch

goi18n/extract_command.go#L75-L80

Added lines #L75 - L80 were not covered by tests
}
*f = name

Check warning on line 82 in goi18n/extract_command.go

View check run for this annotation

Codecov / codecov/patch

goi18n/extract_command.go#L82

Added line #L82 was not covered by tests
}

func resolveMessageWithConsts(m *i18n.Message, consts []*constObj) {
resolveMessageFieldWithConsts(&m.ID, consts)
resolveMessageFieldWithConsts(&m.Hash, consts)
resolveMessageFieldWithConsts(&m.Description, consts)
resolveMessageFieldWithConsts(&m.LeftDelim, consts)
resolveMessageFieldWithConsts(&m.RightDelim, consts)
resolveMessageFieldWithConsts(&m.Zero, consts)
resolveMessageFieldWithConsts(&m.One, consts)
resolveMessageFieldWithConsts(&m.Two, consts)
resolveMessageFieldWithConsts(&m.Few, consts)
resolveMessageFieldWithConsts(&m.Many, consts)
resolveMessageFieldWithConsts(&m.Other, consts)
}

func (ec *extractCommand) execute() error {
if len(ec.paths) == 0 {
ec.paths = []string{"."}
}
consts := []*constObj{}
messages := []*i18n.Message{}
for _, path := range ec.paths {
if err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
Expand All @@ -92,18 +123,24 @@ func (ec *extractCommand) execute() error {
if err != nil {
return err
}
msgs, err := extractMessages(buf)
msgs, cnsts, err := extractMessages(buf)
if err != nil {
return err
}
messages = append(messages, msgs...)
consts = append(consts, cnsts...)
return nil
}); err != nil {
return err
}
}

messageTemplates := map[string]*i18n.MessageTemplate{}
for _, m := range messages {
// resolve message consts
resolveMessageWithConsts(m, consts)

// create template
if mt := i18n.NewMessageTemplate(m); mt != nil {
messageTemplates[m.ID] = mt
}
Expand All @@ -116,32 +153,75 @@ func (ec *extractCommand) execute() error {
}

// extractMessages extracts messages from the bytes of a Go source file.
func extractMessages(buf []byte) ([]*i18n.Message, error) {
func extractMessages(buf []byte) ([]*i18n.Message, []*constObj, error) {
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "", buf, parser.AllErrors)
if err != nil {
return nil, err
return nil, nil, err

Check warning on line 160 in goi18n/extract_command.go

View check run for this annotation

Codecov / codecov/patch

goi18n/extract_command.go#L160

Added line #L160 was not covered by tests
}
extractor := newExtractor(file)
ast.Walk(extractor, file)
return extractor.messages, nil
return extractor.messages, extractor.consts, nil
}

func newExtractor(file *ast.File) *extractor {
return &extractor{i18nPackageName: i18nPackageName(file)}
return &extractor{
i18nPackageName: i18nPackageName(file),
packageName: file.Name.Name,
}
}

const unresolvedConstIdentifier = ":::unresolved:::"

type constObj struct {
name string
value string
packageName string
}

type extractor struct {
i18nPackageName string
packageName string
messages []*i18n.Message
consts []*constObj
}

func (e *extractor) Visit(node ast.Node) ast.Visitor {
e.extractMessages(node)
return e
}

func (e *extractor) extractConsts(node ast.GenDecl) {
if node.Tok != token.CONST {
return
}

for _, s := range node.Specs {
if vs, ok := s.(*ast.ValueSpec); ok {
for i, n := range vs.Names {

if bl, ok := vs.Values[i].(*ast.BasicLit); ok {
v, err := strconv.Unquote(bl.Value)
if err != nil {
continue

Check warning on line 206 in goi18n/extract_command.go

View check run for this annotation

Codecov / codecov/patch

goi18n/extract_command.go#L206

Added line #L206 was not covered by tests
}
e.consts = append(e.consts, &constObj{
name: n.Name,
value: v,
packageName: e.packageName,
})
}
}
}
}
}

func (e *extractor) extractMessages(node ast.Node) {
// Collect consts from all files to resolve nil objects recived after extract message
if gd, ok := node.(*ast.GenDecl); ok {
e.extractConsts(*gd)
}

cl, ok := node.(*ast.CompositeLit)
if !ok {
return
Expand Down Expand Up @@ -221,6 +301,11 @@ func (e *extractor) extractMessage(cl *ast.CompositeLit) {
}
v, ok := extractStringLiteral(kve.Value)
if !ok {
if len(v) > 0 {
// v might be not empty if const cannot be r (placed other file) so we should try to resolve it later
data[key.Name] = v + unresolvedConstIdentifier + e.packageName
}

continue
}
data[key.Name] = v
Expand Down Expand Up @@ -260,7 +345,7 @@ func extractStringLiteral(expr ast.Expr) (string, bool) {
return x + y, true
case *ast.Ident:
if v.Obj == nil {
return "", false
return v.Name, false
}
switch z := v.Obj.Decl.(type) {
case *ast.ValueSpec:
Expand Down

0 comments on commit 284febf

Please sign in to comment.