Skip to content
This repository was archived by the owner on Dec 18, 2020. It is now read-only.

Commit 42f3786

Browse files
committed
fixed critical bug that made it impossible to push certain patterns that should have been valid, updated specs for more patterns
1 parent 85b9574 commit 42f3786

File tree

3 files changed

+343
-184
lines changed

3 files changed

+343
-184
lines changed

push.go

Lines changed: 175 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -224,24 +224,24 @@ func (source *Source) LocaleFiles() (LocaleFiles, error) {
224224
}
225225
}
226226

227-
parser := Parser{
227+
reducer := Reducer{
228228
SourceFile: source.File,
229229
Extension: source.Extension,
230230
}
231-
parser.Initialize()
231+
reducer.Initialize()
232232

233-
if err := parser.Search(); err != nil {
233+
if err := reducer.Reduce(); err != nil {
234234
return nil, err
235235
}
236236

237237
var localeFiles LocaleFiles
238238
for _, path := range filePaths {
239239

240-
if !parser.MatchesPath(path) {
240+
if !reducer.MatchesPath(path) {
241241
continue
242242
}
243243

244-
temporaryLocaleFile, err := parser.Eval(path)
244+
temporaryLocaleFile, err := reducer.Eval(path)
245245
if err != nil {
246246
return nil, err
247247
}
@@ -254,7 +254,7 @@ func (source *Source) LocaleFiles() (LocaleFiles, error) {
254254
if Debug {
255255
fmt.Println(fmt.Sprintf(
256256
"RFC:'%s', Name:'%s', Tag;'%s', Pattern:'%s'",
257-
localeFile.RFC, localeFile.Name, localeFile.Tag, parser.Matcher.String(),
257+
localeFile.RFC, localeFile.Name, localeFile.Tag, reducer.Matcher.String(),
258258
))
259259
}
260260

@@ -498,155 +498,237 @@ func (source *Source) setUploadParams(localeFile *LocaleFile) (*phraseapp.Upload
498498
return uploadParams, nil
499499
}
500500

501-
// Parser Algorithm, Stateful
502-
// parser := &Parser{ SourceFile: file, Extension: ext }
503-
// parser.Initialize()
504-
// parser.Search()
505-
// parser.Eval( path )
506-
type Parser struct {
501+
// Print out
502+
func printSummary(summary *phraseapp.SummaryType) {
503+
newItems := []int64{
504+
summary.LocalesCreated,
505+
summary.TranslationsUpdated,
506+
summary.TranslationKeysCreated,
507+
summary.TranslationsCreated,
508+
}
509+
var changed bool
510+
for _, item := range newItems {
511+
if item > 0 {
512+
changed = true
513+
}
514+
}
515+
if changed || Debug {
516+
printMessage("Locales created: ", fmt.Sprintf("%d", summary.LocalesCreated))
517+
printMessage(" - Keys created: ", fmt.Sprintf("%d", summary.TranslationKeysCreated))
518+
printMessage(" - Translations created: ", fmt.Sprintf("%d", summary.TranslationsCreated))
519+
printMessage(" - Translations updated: ", fmt.Sprintf("%d", summary.TranslationsUpdated))
520+
fmt.Print("\n")
521+
}
522+
}
523+
524+
func printMessage(msg, stat string) {
525+
fmt.Print(msg)
526+
ct.Foreground(ct.Green, true)
527+
fmt.Print(stat)
528+
ct.ResetColor()
529+
}
530+
531+
// Reducer Algorithm:
532+
// reducer := &Reducer{
533+
// SourceFile: file,
534+
// Extension: extension,
535+
// }
536+
// reducer.Initialize()
537+
// reducer.Reduce()
538+
// if reducer.MatchesPath(path) {
539+
// reducer.Eval(path)
540+
// }
541+
type Reducer struct {
507542
SourceFile string
508543
Extension string
509544
Tokens []string
510-
Buffer []string
545+
Reductions []*Reduction
511546
Matcher *regexp.Regexp
512547
}
513548

514-
func (parser *Parser) Initialize() {
549+
type Reduction struct {
550+
Original string
551+
Matcher *regexp.Regexp
552+
}
553+
554+
func (reducer *Reducer) Initialize() {
515555
tokens := []string{}
516-
for _, token := range strings.Split(parser.SourceFile, "/") {
556+
for _, token := range strings.Split(reducer.SourceFile, "/") {
517557
if token == "." || token == "" {
518558
continue
519559
}
520560
tokens = append(tokens, token)
521561
}
522-
parser.Tokens = tokens
523-
parser.Buffer = []string{}
562+
reducer.Tokens = tokens
524563
}
525564

526-
func (parser *Parser) Search() error {
527-
for _, token := range parser.Tokens {
528-
head := strings.Replace(token, ".", "_PHRASEAPP_REGEXP_DOT_", 1)
529-
// if next is part of the placeholder dsl then it can be expanded and will be
530-
// converted to a regexp, else we found a path part that is already a static string
531-
nextRegexp := parser.ConvertToRegexp(head)
532-
if nextRegexp != "" {
533-
head = nextRegexp
534-
535-
// if it is the end of the path and matching the extension
536-
// e.g. config/.yml we convert it to config/.*.yml
537-
} else if strings.TrimSpace(token) == parser.Extension {
538-
head = ".*" + head
565+
func (reducer *Reducer) Reduce() error {
566+
reducer.Reductions = []*Reduction{}
567+
heads := []string{}
568+
for _, token := range reducer.Tokens {
569+
head, matcher, err := reducer.toRegexp(token)
570+
if err != nil {
571+
return err
539572
}
540-
541-
head = parser.SanitizeRegexp(head)
542-
head = strings.Replace(head, "_PHRASEAPP_REGEXP_DOT_", "[.]", 1)
543-
parser.Buffer = append(parser.Buffer, head)
573+
heads = append(heads, head)
574+
reducer.Reductions = append(reducer.Reductions, &Reduction{
575+
Original: token,
576+
Matcher: matcher,
577+
})
544578
}
545579

546-
fileAsRegexp := strings.Join(parser.Buffer, "[/\\\\]")
547-
matcherString := strings.Trim(fileAsRegexp, "[/\\\\]")
548-
549-
reMatcher, err := regexp.Compile(matcherString)
580+
reMatcher, err := reducer.wholeMatcher(heads)
550581
if err != nil {
551582
return err
552583
}
553-
554-
// valid regular expression
555-
parser.Matcher = reMatcher
584+
reducer.Matcher = reMatcher
556585

557586
return nil
558587
}
559588

560-
func (parser *Parser) ConvertToRegexp(part string) string {
589+
func (reducer *Reducer) MatchesPath(path string) bool {
590+
return reducer.Matcher.MatchString(path)
591+
}
592+
593+
func (reducer *Reducer) Eval(path string) (*LocaleFile, error) {
594+
tagged := reducer.unify(path)
595+
596+
name := tagged["locale_name"]
597+
rfc := tagged["locale_code"]
598+
tag := tagged["tag"]
599+
600+
localeFile := &LocaleFile{}
601+
if name != "" {
602+
localeFile.Name = name
603+
}
604+
605+
if rfc != "" {
606+
localeFile.RFC = rfc
607+
}
608+
609+
if tag != "" {
610+
localeFile.Tag = tag
611+
}
612+
613+
return localeFile, nil
614+
}
615+
616+
// Private Reducer Methods
617+
func (reducer *Reducer) toRegexp(token string) (string, *regexp.Regexp, error) {
618+
head := strings.Replace(token, ".", "_PHRASEAPP_REGEXP_DOT_", 1)
619+
nextRegexp := reducer.convertToGroupRegexp(head)
620+
if nextRegexp != "" {
621+
head = nextRegexp
622+
}
623+
head = reducer.sanitizeRegexp(head)
624+
head = strings.Replace(head, "_PHRASEAPP_REGEXP_DOT_", "[.]", 1)
625+
if strings.HasPrefix(head, "[.]") {
626+
head = ".*" + head
627+
}
628+
matcher, err := regexp.Compile(head)
629+
if err != nil {
630+
return "", nil, err
631+
}
632+
633+
return head, matcher, nil
634+
}
635+
636+
func (reducer *Reducer) convertToGroupRegexp(part string) string {
561637
groups := placeholderRegexp.FindAllString(part, -1)
562638
if len(groups) <= 0 {
563639
return ""
564640
}
641+
565642
for _, group := range groups {
566643
replacer := fmt.Sprintf("(?P%s.+)", group)
567644
part = strings.Replace(part, group, replacer, 1)
568645
}
646+
569647
return part
570648
}
571649

572-
func (parser *Parser) SanitizeRegexp(token string) string {
650+
func (reducer *Reducer) sanitizeRegexp(token string) string {
573651
newToken := strings.Replace(token, "**", "__PHRASE_DOUBLE_STAR__", -1)
574652
newToken = strings.Replace(newToken, "*", ".*", -1)
575653
newToken = strings.Replace(newToken, "..", ".", -1)
576654
newToken = strings.Replace(newToken, "__PHRASE_DOUBLE_STAR__", ".*", -1)
577655
return newToken
578656
}
579657

580-
func (parser *Parser) MatchesPath(path string) bool {
581-
return parser.Matcher.MatchString(path)
658+
func (reducer *Reducer) wholeMatcher(heads []string) (*regexp.Regexp, error) {
659+
fileAsRegexp := strings.Join(heads, "[/\\\\]")
660+
matcherString := strings.Trim(fileAsRegexp, "[/\\\\]")
661+
reMatcher, err := regexp.Compile(matcherString)
662+
if err != nil {
663+
return nil, err
664+
}
665+
return reMatcher, nil
582666
}
583667

584-
func (parser *Parser) Eval(path string) (*LocaleFile, error) {
585-
tagged := parser.TagMatches(path)
586-
name := tagged["locale_name"]
587-
rfc := tagged["locale_code"]
588-
tag := tagged["tag"]
668+
func (reducer *Reducer) unify(path string) map[string]string {
669+
tagged := map[string]string{}
589670

590-
localeFile := &LocaleFile{}
591-
if name != "" {
592-
localeFile.Name = name
593-
}
671+
tokens := strings.Split(path, "/")
672+
reductions := reducer.Reductions
594673

595-
if rfc != "" {
596-
localeFile.RFC = rfc
674+
if strings.Contains(reducer.SourceFile, "**") || reducer.fileContainsStar() {
675+
for idx, reduction := range reductions {
676+
if reduction.Original == "**" {
677+
break
678+
}
679+
partlyTagged := reducer.tagMatches(reduction, tokens[idx])
680+
tagged = reducer.updateTaggedMatches(tagged, partlyTagged)
681+
}
597682
}
598683

599-
if tag != "" {
600-
localeFile.Tag = tag
684+
offset := 0
685+
for i := len(reductions) - 1; i >= 0; i-- {
686+
reduction := reductions[i]
687+
idx := len(tokens) - offset - 1
688+
partlyTagged := reducer.tagMatches(reduction, tokens[idx])
689+
tagged = reducer.updateTaggedMatches(tagged, partlyTagged)
690+
691+
if i == 0 {
692+
break
693+
}
694+
offset += 1
601695
}
602696

603-
return localeFile, nil
697+
return tagged
698+
}
699+
700+
func (reducer *Reducer) fileContainsStar() bool {
701+
last := reducer.Reductions[len(reducer.Reductions)-1]
702+
return strings.Contains(reducer.SourceFile, "*") && !strings.Contains(last.Original, "*")
604703
}
605704

606-
// @return named matches for the given path matching the file pattern
607-
// example: {"locale_name" : "English", "locale_code" : "en-Gb", "tag" : "my_tag"}
608-
func (parser *Parser) TagMatches(path string) map[string]string {
705+
func (reducer *Reducer) tagMatches(reduction *Reduction, token string) map[string]string {
609706
tagged := map[string]string{}
610-
namedMatches := parser.Matcher.SubexpNames()
611-
subMatches := parser.Matcher.FindStringSubmatch(path)
707+
namedMatches := reduction.Matcher.SubexpNames()
708+
subMatches := reduction.Matcher.FindStringSubmatch(token)
612709
for i, subMatch := range subMatches {
613710
if subMatch != "" {
614-
if strings.Contains(subMatch, separator) {
615-
subSlice := strings.Split(subMatch, separator)
616-
subMatch = subSlice[len(subSlice)-1]
617-
}
618711
tagged[namedMatches[i]] = subMatch
619712
}
620713
}
621714
return tagged
622715
}
623716

624-
// print out
625-
func printSummary(summary *phraseapp.SummaryType) {
626-
newItems := []int64{
627-
summary.LocalesCreated,
628-
summary.TranslationsUpdated,
629-
summary.TranslationKeysCreated,
630-
summary.TranslationsCreated,
631-
}
632-
var changed bool
633-
for _, item := range newItems {
634-
if item > 0 {
635-
changed = true
636-
}
717+
func (reducer *Reducer) updateTaggedMatches(original, updater map[string]string) map[string]string {
718+
localeCode := updater["locale_code"]
719+
localeName := updater["locale_name"]
720+
tag := updater["tag"]
721+
722+
if original["locale_code"] == "" {
723+
original["locale_code"] = strings.Trim(localeCode, "/")
637724
}
638-
if changed || Debug {
639-
printMessage("Locales created: ", fmt.Sprintf("%d", summary.LocalesCreated))
640-
printMessage(" - Keys created: ", fmt.Sprintf("%d", summary.TranslationKeysCreated))
641-
printMessage(" - Translations created: ", fmt.Sprintf("%d", summary.TranslationsCreated))
642-
printMessage(" - Translations updated: ", fmt.Sprintf("%d", summary.TranslationsUpdated))
643-
fmt.Print("\n")
725+
726+
if original["locale_name"] == "" {
727+
original["locale_name"] = strings.Trim(localeName, "/")
644728
}
645-
}
646729

647-
func printMessage(msg, stat string) {
648-
fmt.Print(msg)
649-
ct.Foreground(ct.Green, true)
650-
fmt.Print(stat)
651-
ct.ResetColor()
730+
if original["tag"] == "" {
731+
original["tag"] = strings.Trim(tag, "/")
732+
}
733+
return original
652734
}

0 commit comments

Comments
 (0)