3939
4040var regexBetaVersion = regexp .MustCompile ("^v[0-9]+(beta|alpha)[0-9]+" )
4141
42+ // ErrMessageNotFound is returned when a proto message cannot be found in a package.
43+ var ErrMessageNotFound = errors .New ("no message found" )
44+
4245// ModuleName retrieves the single module name of the package.
4346func (p Package ) ModuleName () (name string ) {
4447 names := strings .Split (p .Name , "." )
@@ -53,12 +56,41 @@ func (p Package) ModuleName() (name string) {
5356
5457// MessageByName finds a message by its name inside Package.
5558func (p Package ) MessageByName (name string ) (Message , error ) {
56- for _ , message := range p .Messages {
57- if message .Name == name {
58- return message , nil
59+ message , ok := p .FindMessageByName (name )
60+ if ! ok {
61+ return Message {}, ErrMessageNotFound
62+ }
63+
64+ return message , nil
65+ }
66+
67+ // FindMessageByName finds a message by its name inside Package.
68+ // It accepts plain message names, current-package qualified names and nested message names.
69+ func (p Package ) FindMessageByName (name string ) (Message , bool ) {
70+ for _ , candidate := range candidateMessageNames (p .Name , name ) {
71+ for _ , message := range p .Messages {
72+ if message .Name == candidate {
73+ return message , true
74+ }
75+ }
76+ }
77+
78+ if ! strings .Contains (strings .TrimPrefix (name , "." ), "." ) {
79+ leafName := leafMessageName (name )
80+
81+ var leafMatches []Message
82+ for _ , message := range p .Messages {
83+ if leafMessageName (message .Name ) == leafName {
84+ leafMatches = append (leafMatches , message )
85+ }
86+ }
87+
88+ if len (leafMatches ) == 1 {
89+ return leafMatches [0 ], true
5990 }
6091 }
61- return Message {}, errors .New ("no message found" )
92+
93+ return Message {}, false
6294}
6395
6496// GoImportPath retrieves the Go import path.
@@ -87,6 +119,38 @@ type (
87119 }
88120)
89121
122+ func candidateMessageNames (pkgName , name string ) []string {
123+ candidates := []string {name }
124+
125+ canonical := canonicalMessageName (pkgName , name )
126+ if canonical != "" && canonical != name {
127+ candidates = append (candidates , canonical )
128+ }
129+
130+ return candidates
131+ }
132+
133+ func canonicalMessageName (pkgName , name string ) string {
134+ name = strings .TrimPrefix (name , "." )
135+ if pkgName != "" {
136+ name = strings .TrimPrefix (name , pkgName + "." )
137+ }
138+
139+ return strings .ReplaceAll (name , "." , "_" )
140+ }
141+
142+ func leafMessageName (name string ) string {
143+ if index := strings .LastIndex (name , "_" ); index >= 0 {
144+ return name [index + 1 :]
145+ }
146+
147+ if index := strings .LastIndex (name , "." ); index >= 0 {
148+ return name [index + 1 :]
149+ }
150+
151+ return name
152+ }
153+
90154// Paths retrieves the list of paths from the files.
91155func (f Files ) Paths () []string {
92156 var paths []string
0 commit comments