@@ -5,6 +5,7 @@ use crate::error::ErrMode;
55use crate :: error:: ErrorKind ;
66use crate :: error:: ParserError ;
77use crate :: stream:: Accumulate ;
8+ use crate :: stream:: Offset ;
89use crate :: stream:: Range ;
910use crate :: stream:: Stream ;
1011use crate :: PResult ;
@@ -1307,3 +1308,97 @@ where
13071308
13081309 Ok ( acc)
13091310}
1311+
1312+ /// Call the `repeat` parser until the `end` parser produces a result.
1313+ ///
1314+ /// Then, return the input consumed until the `end` parser was called, and the result of the `end`
1315+ /// parser.
1316+ ///
1317+ /// # Example
1318+ ///
1319+ /// ```rust
1320+ /// # #[cfg(feature = "std")] {
1321+ /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
1322+ /// # use winnow::prelude::*;
1323+ /// use winnow::ascii::digit1;
1324+ /// use winnow::combinator::recognize_till;
1325+ ///
1326+ /// fn parser(s: &str) -> IResult<&str, (&str, char)> {
1327+ /// recognize_till(
1328+ /// digit1,
1329+ /// ' ',
1330+ /// ).parse_peek(s)
1331+ /// }
1332+ ///
1333+ /// assert_eq!(parser("123 "), Ok(("", ("123", ' '))));
1334+ /// assert_eq!(parser("0 a"), Ok(("a", ("0", ' '))));
1335+ /// assert_eq!(parser("123a"), Err(ErrMode::Backtrack(InputError::new("a", ErrorKind::Slice))));
1336+ /// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
1337+ /// # }
1338+ /// ```
1339+ ///
1340+ /// ```rust
1341+ /// # #[cfg(feature = "std")] {
1342+ /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
1343+ /// # use winnow::prelude::*;
1344+ /// use winnow::combinator::alt;
1345+ /// use winnow::combinator::preceded;
1346+ /// use winnow::combinator::recognize_till;
1347+ /// use winnow::token::take_till;
1348+ ///
1349+ /// fn parser(s: &str) -> IResult<&str, &str> {
1350+ /// preceded(
1351+ /// '\'',
1352+ /// recognize_till(
1353+ /// alt((
1354+ /// preceded('\'', take_till(0.., '\'')),
1355+ /// take_till(1.., '\''),
1356+ /// )),
1357+ /// "' ",
1358+ /// ).map(|(name, _end_quote)| name)
1359+ /// ).parse_peek(s)
1360+ /// }
1361+ ///
1362+ /// assert_eq!(parser("'Puppy' "), Ok(("", "Puppy")));
1363+ /// assert_eq!(parser("'Dog'' "), Ok(("", "Dog'")));
1364+ /// assert_eq!(parser("'Isn't' that nice?"), Ok(("that nice?", "Isn't")));
1365+ /// assert_eq!(parser("'' after"), Ok(("after", "")));
1366+ ///
1367+ /// assert_eq!(parser("'Puppy'dog"), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
1368+ /// # }
1369+ /// ```
1370+ pub fn recognize_till < I , Discard , O , E > (
1371+ mut repeat : impl Parser < I , Discard , E > ,
1372+ mut end : impl Parser < I , O , E > ,
1373+ ) -> impl Parser < I , ( <I as Stream >:: Slice , O ) , E >
1374+ where
1375+ I : Stream ,
1376+ E : ParserError < I > ,
1377+ {
1378+ move |input : & mut I | {
1379+ let start = input. checkpoint ( ) ;
1380+
1381+ loop {
1382+ let before_end = input. checkpoint ( ) ;
1383+ match end. parse_next ( input) {
1384+ Ok ( end_parsed) => {
1385+ let after_end = input. checkpoint ( ) ;
1386+
1387+ input. reset ( & start) ;
1388+ let input_until_end = input. next_slice ( before_end. offset_from ( & start) ) ;
1389+ input. reset ( & after_end) ;
1390+
1391+ return Ok ( ( input_until_end, end_parsed) ) ;
1392+ }
1393+ Err ( ErrMode :: Backtrack ( _) ) => {
1394+ input. reset ( & before_end) ;
1395+ match repeat. parse_next ( input) {
1396+ Ok ( _) => { }
1397+ Err ( e) => return Err ( e. append ( input, & before_end, ErrorKind :: Many ) ) ,
1398+ }
1399+ }
1400+ Err ( e) => return Err ( e) ,
1401+ }
1402+ }
1403+ }
1404+ }
0 commit comments