@@ -3356,9 +3356,8 @@ fn build_keyword_choice_sub_ability(
33563356 // "gains your choice of ...". `parse_keyword_choice_grant` anchors on the
33573357 // bare "gain ..." form, so deconjugate the remainder here first.
33583358 let normalized = deconjugate_verb ( remainder) ;
3359- let ( keywords, duration) = match parse_keyword_choice_grant ( & normalized) {
3360- KeywordChoiceGrant :: Parsed { keywords, duration } => ( keywords, duration) ,
3361- KeywordChoiceGrant :: NoMatch | KeywordChoiceGrant :: UnsupportedNestedChoice => return None ,
3359+ let Ok ( Some ( ( keywords, duration) ) ) = parse_keyword_choice_grant ( & normalized) else {
3360+ return None ;
33623361 } ;
33633362 let affected = static_affected_for_application ( application) ;
33643363 let choice_duration = duration. clone ( ) ;
@@ -3378,16 +3377,9 @@ fn build_keyword_choice_sub_ability(
33783377 ) )
33793378}
33803379
3381- pub ( super ) enum KeywordChoiceGrant {
3382- NoMatch ,
3383- Parsed {
3384- keywords : Vec < Keyword > ,
3385- duration : Option < Duration > ,
3386- } ,
3387- UnsupportedNestedChoice ,
3388- }
3389-
3390- pub ( super ) fn parse_keyword_choice_grant ( predicate : & str ) -> KeywordChoiceGrant {
3380+ fn parse_keyword_choice_grant (
3381+ predicate : & str ,
3382+ ) -> Result < Option < ( Vec < Keyword > , Option < Duration > ) > , ( ) > {
33913383 let lower = predicate. to_lowercase ( ) ;
33923384
33933385 // CR 608.2d: "gain your choice of X, Y, or Z" offers a resolution-time choice.
@@ -3401,12 +3393,12 @@ pub(super) fn parse_keyword_choice_grant(predicate: &str) -> KeywordChoiceGrant
34013393 {
34023394 let ( keyword_text, duration) = super :: strip_trailing_duration ( choice_text) ;
34033395 let Some ( items) = super :: split_choice_list_items ( keyword_text. trim ( ) ) else {
3404- return KeywordChoiceGrant :: NoMatch ;
3396+ return Ok ( None ) ;
34053397 } ;
34063398 // `separated_list1` succeeds on a single item when there is no separator
34073399 // at all; require ≥2 so a lone keyword is not mistaken for a "choice".
34083400 if items. len ( ) < 2 {
3409- return KeywordChoiceGrant :: NoMatch ;
3401+ return Ok ( None ) ;
34103402 }
34113403 // An item such as "landwalk of your choice" or "protection from a color
34123404 // of your choice" needs an additional typed choice after the outer menu.
@@ -3417,19 +3409,19 @@ pub(super) fn parse_keyword_choice_grant(predicate: &str) -> KeywordChoiceGrant
34173409 . iter ( )
34183410 . any ( |item| nom_primitives:: scan_contains ( item, "of your choice" ) )
34193411 {
3420- return KeywordChoiceGrant :: UnsupportedNestedChoice ;
3412+ return Err ( ( ) ) ;
34213413 }
34223414 let Some ( keywords) = items
34233415 . iter ( )
34243416 . map ( |item| parse_granted_keyword_fragment ( item. trim ( ) ) )
34253417 . collect :: < Option < Vec < Keyword > > > ( )
34263418 else {
3427- return KeywordChoiceGrant :: NoMatch ;
3419+ return Ok ( None ) ;
34283420 } ;
3429- return KeywordChoiceGrant :: Parsed {
3421+ return Ok ( Some ( (
34303422 keywords,
3431- duration : duration . or ( Some ( Duration :: UntilEndOfTurn ) ) ,
3432- } ;
3423+ duration. or ( Some ( Duration :: UntilEndOfTurn ) ) ,
3424+ ) ) ) ;
34333425 }
34343426
34353427 // Shape 2: "gain/have protection from X or from the color of your choice"
@@ -3445,7 +3437,7 @@ pub(super) fn parse_keyword_choice_grant(predicate: &str) -> KeywordChoiceGrant
34453437 ) )
34463438 . parse ( lower. as_str ( ) )
34473439 . ok ( ) else {
3448- return KeywordChoiceGrant :: NoMatch ;
3440+ return Ok ( None ) ;
34493441 } ;
34503442 let ( quality_text, duration) = super :: strip_trailing_duration ( remainder) ;
34513443 // GUARDRAIL: split on the literal " or from " (NOT " or "). Splitting on
@@ -3457,7 +3449,7 @@ pub(super) fn parse_keyword_choice_grant(predicate: &str) -> KeywordChoiceGrant
34573449 let Some ( ( _, ( left, right) ) ) =
34583450 nom_primitives:: split_once_on ( quality_text. trim ( ) , " or from " ) . ok ( )
34593451 else {
3460- return KeywordChoiceGrant :: NoMatch ;
3452+ return Ok ( None ) ;
34613453 } ;
34623454 // The halves are bare qualities (the "protection from " prefix is already
34633455 // stripped), so map each with parse_protection_target — NOT
@@ -3466,10 +3458,10 @@ pub(super) fn parse_keyword_choice_grant(predicate: &str) -> KeywordChoiceGrant
34663458 let second = Keyword :: Protection ( crate :: types:: keywords:: parse_protection_target (
34673459 right. trim ( ) ,
34683460 ) ) ;
3469- KeywordChoiceGrant :: Parsed {
3470- keywords : vec ! [ first, second] ,
3471- duration : duration . or ( Some ( Duration :: UntilEndOfTurn ) ) ,
3472- }
3461+ Ok ( Some ( (
3462+ vec ! [ first, second] ,
3463+ duration. or ( Some ( Duration :: UntilEndOfTurn ) ) ,
3464+ ) ) )
34733465}
34743466
34753467fn keyword_choice_branch (
@@ -3499,10 +3491,8 @@ fn build_keyword_choice_clause(
34993491 application : & SubjectApplication ,
35003492 predicate : & str ,
35013493) -> Result < Option < ParsedEffectClause > , ( ) > {
3502- let ( keywords, duration) = match parse_keyword_choice_grant ( predicate) {
3503- KeywordChoiceGrant :: NoMatch => return Ok ( None ) ,
3504- KeywordChoiceGrant :: Parsed { keywords, duration } => ( keywords, duration) ,
3505- KeywordChoiceGrant :: UnsupportedNestedChoice => return Err ( ( ) ) ,
3494+ let Some ( ( keywords, duration) ) = parse_keyword_choice_grant ( predicate) ? else {
3495+ return Ok ( None ) ;
35063496 } ;
35073497 let affected = static_affected_for_application ( application) ;
35083498 let branches = keywords
0 commit comments