@@ -95,35 +95,39 @@ func resolveOne(defs []Definition, name, folder string) (Definition, error) {
9595 return Definition {}, fmt .Errorf ("no rule matched %q: no rule has this uid (run 'grafana-alertcheck list' to see uids)" , name )
9696 }
9797
98- // A datasource rule's name can contain "/", so the full input may be a title,
99- // not a segmented form: try an exact title match before splitting.
100- if def , found , err := pickCandidate (defs , name , func (d Definition ) bool {
101- return titleMatches (d , name , folder )
102- }); found {
103- return def , err
104- }
105-
106- parts , err := parseNameForm (name )
107- if err != nil {
108- return Definition {}, err
98+ // Two interpretations are possible: the whole input as one rule's exact
99+ // title (a datasource rule's name can itself contain "/"), and the
100+ // /-separated forms. Collect candidates from BOTH — a selector that is
101+ // ambiguous between them must be reported, never silently resolved to one.
102+ parts , formErr := parseNameForm (name )
103+ var candidates []Definition
104+ seen := make (map [string ]bool , len (defs ))
105+ for _ , d := range defs {
106+ exact := titleMatches (d , name , folder )
107+ segmented := formErr == nil && matchesName (d , parts , folder )
108+ if ! exact && ! segmented {
109+ continue
110+ }
111+ if key := defKey (d ); ! seen [key ] {
112+ seen [key ] = true
113+ candidates = append (candidates , d )
114+ }
109115 }
110- if def , found , err := pickCandidate (defs , name , func (d Definition ) bool {
111- return matchesName (d , parts , folder )
112- }); found {
116+ if def , found , err := pickCandidate (candidates , name ); found {
113117 return def , err
114118 }
119+ if formErr != nil {
120+ return Definition {}, formErr
121+ }
115122 return Definition {}, noMatchError (supportedDefs (defs ), name , parts [len (parts )- 1 ])
116123}
117124
118- // pickCandidate applies the shared one-match/ambiguous/unsupported/no-match
119- // policy to a candidate predicate . found is false when nothing matched, so the
120- // caller can try the next interpretation .
121- func pickCandidate (defs []Definition , name string , match func ( Definition ) bool ) (Definition , bool , error ) {
125+ // pickCandidate applies the shared one-match/ambiguous/unsupported policy to the
126+ // collected candidates . found is false when nothing matched, so the caller can
127+ // fall through to the no-match surface .
128+ func pickCandidate (candidates []Definition , name string ) (Definition , bool , error ) {
122129 var supported , unsupported []Definition
123- for _ , d := range defs {
124- if ! match (d ) {
125- continue
126- }
130+ for _ , d := range candidates {
127131 if isSupported (d ) {
128132 supported = append (supported , d )
129133 } else {
0 commit comments