@@ -1012,6 +1012,97 @@ impl SqlBuilderExt for SqlBuilder {
10121012 }
10131013}
10141014
1015+ pub struct QueryTokenizer < ' a > {
1016+ query : & ' a str ,
1017+ last_pos : usize ,
1018+ }
1019+
1020+ pub enum QueryToken < ' a > {
1021+ Match ( & ' a str , bool ) ,
1022+ MatchStart ( & ' a str , bool ) ,
1023+ MatchEnd ( & ' a str , bool ) ,
1024+ MatchFull ( & ' a str , bool ) ,
1025+ Or ,
1026+ Regex ( & ' a str ) ,
1027+ }
1028+
1029+ impl < ' a > QueryToken < ' a > {
1030+ pub fn has_uppercase ( & self ) -> bool {
1031+ match self {
1032+ Self :: Match ( term, _)
1033+ | Self :: MatchStart ( term, _)
1034+ | Self :: MatchEnd ( term, _)
1035+ | Self :: MatchFull ( term, _) => term. contains ( char:: is_uppercase) ,
1036+ _ => false ,
1037+ }
1038+ }
1039+
1040+ pub fn is_inverse ( & self ) -> bool {
1041+ match self {
1042+ Self :: Match ( _, inv)
1043+ | Self :: MatchStart ( _, inv)
1044+ | Self :: MatchEnd ( _, inv)
1045+ | Self :: MatchFull ( _, inv) => * inv,
1046+ _ => false ,
1047+ }
1048+ }
1049+ }
1050+
1051+ impl < ' a > QueryTokenizer < ' a > {
1052+ pub fn new ( query : & ' a str ) -> Self {
1053+ Self { query, last_pos : 0 }
1054+ }
1055+ }
1056+
1057+ impl < ' a > Iterator for QueryTokenizer < ' a > {
1058+ type Item = QueryToken < ' a > ;
1059+ fn next ( & mut self ) -> Option < Self :: Item > {
1060+ let remaining = & self . query [ self . last_pos ..] ;
1061+ if remaining. is_empty ( ) {
1062+ return None ;
1063+ }
1064+
1065+ if let Some ( remaining) = remaining. strip_prefix ( "r/" ) {
1066+ let ( regex, next_pos) = if let Some ( end) = remaining. find ( "/ " ) {
1067+ ( & remaining[ ..end] , self . last_pos + 2 + end + 2 )
1068+ } else if let Some ( remaining) = remaining. strip_suffix ( '/' ) {
1069+ ( remaining, self . query . len ( ) )
1070+ } else {
1071+ ( remaining, self . query . len ( ) )
1072+ } ;
1073+ self . last_pos = next_pos;
1074+ Some ( QueryToken :: Regex ( regex) )
1075+ } else {
1076+ let ( mut part, next_pos) = if let Some ( sp) = remaining. find ( ' ' ) {
1077+ ( & remaining[ ..sp] , self . last_pos + sp + 1 )
1078+ } else {
1079+ ( remaining, self . query . len ( ) )
1080+ } ;
1081+ self . last_pos = next_pos;
1082+
1083+ if part == "|" {
1084+ return Some ( QueryToken :: Or ) ;
1085+ }
1086+
1087+ let mut is_inverse = false ;
1088+ if let Some ( s) = part. strip_prefix ( '!' ) {
1089+ part = s;
1090+ is_inverse = true ;
1091+ }
1092+ let token = if let Some ( s) = part. strip_prefix ( '^' ) {
1093+ QueryToken :: MatchStart ( s, is_inverse)
1094+ } else if let Some ( s) = part. strip_suffix ( '$' ) {
1095+ QueryToken :: MatchEnd ( s, is_inverse)
1096+ } else if let Some ( s) = part. strip_prefix ( '\'' ) {
1097+ QueryToken :: MatchFull ( s, is_inverse)
1098+ } else {
1099+ QueryToken :: Match ( part, is_inverse)
1100+ } ;
1101+ Some ( token)
1102+ }
1103+ }
1104+ }
1105+
10151106#[ cfg( test) ]
10161107mod test {
10171108 use crate :: settings:: test_local_timeout;
@@ -1477,94 +1568,3 @@ mod test {
14771568 assert ! ( duration < Duration :: from_secs( 15 ) ) ;
14781569 }
14791570}
1480-
1481- pub struct QueryTokenizer < ' a > {
1482- query : & ' a str ,
1483- last_pos : usize ,
1484- }
1485-
1486- pub enum QueryToken < ' a > {
1487- Match ( & ' a str , bool ) ,
1488- MatchStart ( & ' a str , bool ) ,
1489- MatchEnd ( & ' a str , bool ) ,
1490- MatchFull ( & ' a str , bool ) ,
1491- Or ,
1492- Regex ( & ' a str ) ,
1493- }
1494-
1495- impl < ' a > QueryToken < ' a > {
1496- pub fn has_uppercase ( & self ) -> bool {
1497- match self {
1498- Self :: Match ( term, _)
1499- | Self :: MatchStart ( term, _)
1500- | Self :: MatchEnd ( term, _)
1501- | Self :: MatchFull ( term, _) => term. contains ( char:: is_uppercase) ,
1502- _ => false ,
1503- }
1504- }
1505-
1506- pub fn is_inverse ( & self ) -> bool {
1507- match self {
1508- Self :: Match ( _, inv)
1509- | Self :: MatchStart ( _, inv)
1510- | Self :: MatchEnd ( _, inv)
1511- | Self :: MatchFull ( _, inv) => * inv,
1512- _ => false ,
1513- }
1514- }
1515- }
1516-
1517- impl < ' a > QueryTokenizer < ' a > {
1518- pub fn new ( query : & ' a str ) -> Self {
1519- Self { query, last_pos : 0 }
1520- }
1521- }
1522-
1523- impl < ' a > Iterator for QueryTokenizer < ' a > {
1524- type Item = QueryToken < ' a > ;
1525- fn next ( & mut self ) -> Option < Self :: Item > {
1526- let remaining = & self . query [ self . last_pos ..] ;
1527- if remaining. is_empty ( ) {
1528- return None ;
1529- }
1530-
1531- if let Some ( remaining) = remaining. strip_prefix ( "r/" ) {
1532- let ( regex, next_pos) = if let Some ( end) = remaining. find ( "/ " ) {
1533- ( & remaining[ ..end] , self . last_pos + 2 + end + 2 )
1534- } else if let Some ( remaining) = remaining. strip_suffix ( '/' ) {
1535- ( remaining, self . query . len ( ) )
1536- } else {
1537- ( remaining, self . query . len ( ) )
1538- } ;
1539- self . last_pos = next_pos;
1540- Some ( QueryToken :: Regex ( regex) )
1541- } else {
1542- let ( mut part, next_pos) = if let Some ( sp) = remaining. find ( ' ' ) {
1543- ( & remaining[ ..sp] , self . last_pos + sp + 1 )
1544- } else {
1545- ( remaining, self . query . len ( ) )
1546- } ;
1547- self . last_pos = next_pos;
1548-
1549- if part == "|" {
1550- return Some ( QueryToken :: Or ) ;
1551- }
1552-
1553- let mut is_inverse = false ;
1554- if let Some ( s) = part. strip_prefix ( '!' ) {
1555- part = s;
1556- is_inverse = true ;
1557- }
1558- let token = if let Some ( s) = part. strip_prefix ( '^' ) {
1559- QueryToken :: MatchStart ( s, is_inverse)
1560- } else if let Some ( s) = part. strip_suffix ( '$' ) {
1561- QueryToken :: MatchEnd ( s, is_inverse)
1562- } else if let Some ( s) = part. strip_prefix ( '\'' ) {
1563- QueryToken :: MatchFull ( s, is_inverse)
1564- } else {
1565- QueryToken :: Match ( part, is_inverse)
1566- } ;
1567- Some ( token)
1568- }
1569- }
1570- }
0 commit comments