Skip to main content

iced_test/
instruction.rs

1//! A step in an end-to-end test.
2use crate::core::keyboard;
3use crate::core::mouse;
4use crate::core::{Event, Point};
5use crate::simulator;
6
7use std::fmt;
8
9/// A step in an end-to-end test.
10///
11/// An [`Instruction`] can be run by an [`Emulator`](crate::Emulator).
12#[derive(Debug, Clone, PartialEq)]
13pub enum Instruction {
14    /// A user [`Interaction`].
15    Interact(Interaction),
16    /// A testing [`Expectation`].
17    Expect(Expectation),
18}
19
20impl Instruction {
21    /// Parses an [`Instruction`] from its textual representation.
22    pub fn parse(line: &str) -> Result<Self, ParseError> {
23        parser::run(line)
24    }
25}
26
27impl fmt::Display for Instruction {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        match self {
30            Instruction::Interact(interaction) => interaction.fmt(f),
31            Instruction::Expect(expectation) => expectation.fmt(f),
32        }
33    }
34}
35
36/// A user interaction.
37#[derive(Debug, Clone, PartialEq)]
38pub enum Interaction {
39    /// A mouse interaction.
40    Mouse(Mouse),
41    /// A keyboard interaction.
42    Keyboard(Keyboard),
43}
44
45impl Interaction {
46    /// Creates an [`Interaction`] from a runtime [`Event`].
47    ///
48    /// This can be useful for recording tests during real usage.
49    pub fn from_event(event: &Event) -> Option<Self> {
50        Some(match event {
51            Event::Mouse(mouse) => Self::Mouse(match mouse {
52                mouse::Event::CursorMoved { position } => Mouse::Move(Target::Point(*position)),
53                mouse::Event::ButtonPressed(button) => Mouse::Press {
54                    button: *button,
55                    target: None,
56                },
57                mouse::Event::ButtonReleased(button) => Mouse::Release {
58                    button: *button,
59                    target: None,
60                },
61                _ => None?,
62            }),
63            Event::Keyboard(keyboard) => Self::Keyboard(match keyboard {
64                keyboard::Event::KeyPressed { key, text, .. } => match key {
65                    keyboard::Key::Named(named) => Keyboard::Press(match named {
66                        keyboard::key::Named::Enter => Key::Enter,
67                        keyboard::key::Named::Escape => Key::Escape,
68                        keyboard::key::Named::Tab => Key::Tab,
69                        keyboard::key::Named::Backspace => Key::Backspace,
70                        keyboard::key::Named::Space => Key::Space,
71                        keyboard::key::Named::ArrowUp => Key::ArrowUp,
72                        keyboard::key::Named::ArrowDown => Key::ArrowDown,
73                        keyboard::key::Named::ArrowLeft => Key::ArrowLeft,
74                        keyboard::key::Named::ArrowRight => Key::ArrowRight,
75                        keyboard::key::Named::Home => Key::Home,
76                        keyboard::key::Named::End => Key::End,
77                        keyboard::key::Named::PageUp => Key::PageUp,
78                        keyboard::key::Named::PageDown => Key::PageDown,
79                        _ => None?,
80                    }),
81                    _ => Keyboard::Typewrite(text.as_ref()?.to_string()),
82                },
83                keyboard::Event::KeyReleased { key, .. } => match key {
84                    keyboard::Key::Named(named) => Keyboard::Release(match named {
85                        keyboard::key::Named::Enter => Key::Enter,
86                        keyboard::key::Named::Escape => Key::Escape,
87                        keyboard::key::Named::Tab => Key::Tab,
88                        keyboard::key::Named::Backspace => Key::Backspace,
89                        keyboard::key::Named::Space => Key::Space,
90                        keyboard::key::Named::ArrowUp => Key::ArrowUp,
91                        keyboard::key::Named::ArrowDown => Key::ArrowDown,
92                        keyboard::key::Named::ArrowLeft => Key::ArrowLeft,
93                        keyboard::key::Named::ArrowRight => Key::ArrowRight,
94                        keyboard::key::Named::Home => Key::Home,
95                        keyboard::key::Named::End => Key::End,
96                        keyboard::key::Named::PageUp => Key::PageUp,
97                        keyboard::key::Named::PageDown => Key::PageDown,
98                        _ => None?,
99                    }),
100                    _ => None?,
101                },
102                keyboard::Event::ModifiersChanged(_) => None?,
103            }),
104            _ => None?,
105        })
106    }
107
108    /// Merges two interactions together, if possible.
109    ///
110    /// This method can turn certain sequences of interactions into a single one.
111    /// For instance, a mouse movement, left button press, and left button release
112    /// can all be merged into a single click interaction.
113    ///
114    /// Merging is lossy and, therefore, it is not always desirable if you are recording
115    /// a test and want full reproducibility.
116    ///
117    /// If the interactions cannot be merged, the `next` interaction will be
118    /// returned as the second element of the tuple.
119    pub fn merge(self, next: Self) -> (Self, Option<Self>) {
120        match (self, next) {
121            (Self::Mouse(current), Self::Mouse(next)) => match (current, next) {
122                (Mouse::Move(_), Mouse::Move(to)) => (Self::Mouse(Mouse::Move(to)), None),
123                (
124                    Mouse::Move(to),
125                    Mouse::Press {
126                        button,
127                        target: None,
128                    },
129                ) => (
130                    Self::Mouse(Mouse::Press {
131                        button,
132                        target: Some(to),
133                    }),
134                    None,
135                ),
136                (
137                    Mouse::Move(to),
138                    Mouse::Release {
139                        button,
140                        target: None,
141                    },
142                ) => (
143                    Self::Mouse(Mouse::Release {
144                        button,
145                        target: Some(to),
146                    }),
147                    None,
148                ),
149                (
150                    Mouse::Press {
151                        button: press,
152                        target: press_at,
153                    },
154                    Mouse::Release {
155                        button: release,
156                        target: release_at,
157                    },
158                ) if press == release
159                    && release_at
160                        .as_ref()
161                        .is_none_or(|release_at| Some(release_at) == press_at.as_ref()) =>
162                {
163                    (
164                        Self::Mouse(Mouse::Click {
165                            button: press,
166                            target: press_at,
167                        }),
168                        None,
169                    )
170                }
171                (
172                    Mouse::Press {
173                        button,
174                        target: Some(press_at),
175                    },
176                    Mouse::Move(move_at),
177                ) if press_at == move_at => (
178                    Self::Mouse(Mouse::Press {
179                        button,
180                        target: Some(press_at),
181                    }),
182                    None,
183                ),
184                (
185                    Mouse::Click {
186                        button,
187                        target: Some(click_at),
188                    },
189                    Mouse::Move(move_at),
190                ) if click_at == move_at => (
191                    Self::Mouse(Mouse::Click {
192                        button,
193                        target: Some(click_at),
194                    }),
195                    None,
196                ),
197                (current, next) => (Self::Mouse(current), Some(Self::Mouse(next))),
198            },
199            (Self::Keyboard(current), Self::Keyboard(next)) => match (current, next) {
200                (Keyboard::Typewrite(current), Keyboard::Typewrite(next)) => (
201                    Self::Keyboard(Keyboard::Typewrite(format!("{current}{next}"))),
202                    None,
203                ),
204                (Keyboard::Press(current), Keyboard::Release(next)) if current == next => {
205                    (Self::Keyboard(Keyboard::Type(current)), None)
206                }
207                (current, next) => (Self::Keyboard(current), Some(Self::Keyboard(next))),
208            },
209            (current, next) => (current, Some(next)),
210        }
211    }
212
213    /// Returns a list of runtime events representing the [`Interaction`].
214    ///
215    /// The `find_target` closure must convert a [`Target`] into its screen
216    /// coordinates.
217    pub fn events(&self, find_target: impl FnOnce(&Target) -> Option<Point>) -> Option<Vec<Event>> {
218        let mouse_move_ = |to| Event::Mouse(mouse::Event::CursorMoved { position: to });
219
220        let mouse_press = |button| Event::Mouse(mouse::Event::ButtonPressed(button));
221
222        let mouse_release = |button| Event::Mouse(mouse::Event::ButtonReleased(button));
223
224        let key_press = |key| simulator::press_key(key, None);
225
226        let key_release = |key| simulator::release_key(key);
227
228        Some(match self {
229            Interaction::Mouse(mouse) => match mouse {
230                Mouse::Move(to) => vec![mouse_move_(find_target(to)?)],
231                Mouse::Press {
232                    button,
233                    target: Some(at),
234                } => vec![mouse_move_(find_target(at)?), mouse_press(*button)],
235                Mouse::Press {
236                    button,
237                    target: None,
238                } => {
239                    vec![mouse_press(*button)]
240                }
241                Mouse::Release {
242                    button,
243                    target: Some(at),
244                } => {
245                    vec![mouse_move_(find_target(at)?), mouse_release(*button)]
246                }
247                Mouse::Release {
248                    button,
249                    target: None,
250                } => {
251                    vec![mouse_release(*button)]
252                }
253                Mouse::Click {
254                    button,
255                    target: Some(at),
256                } => {
257                    vec![
258                        mouse_move_(find_target(at)?),
259                        mouse_press(*button),
260                        mouse_release(*button),
261                    ]
262                }
263                Mouse::Click {
264                    button,
265                    target: None,
266                } => {
267                    vec![mouse_press(*button), mouse_release(*button)]
268                }
269            },
270            Interaction::Keyboard(keyboard) => match keyboard {
271                Keyboard::Press(key) => vec![key_press(*key)],
272                Keyboard::Release(key) => vec![key_release(*key)],
273                Keyboard::Type(key) => vec![key_press(*key), key_release(*key)],
274                Keyboard::Typewrite(text) => simulator::typewrite(text).collect(),
275            },
276        })
277    }
278}
279
280impl fmt::Display for Interaction {
281    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
282        match self {
283            Interaction::Mouse(mouse) => mouse.fmt(f),
284            Interaction::Keyboard(keyboard) => keyboard.fmt(f),
285        }
286    }
287}
288
289/// A mouse interaction.
290#[derive(Debug, Clone, PartialEq)]
291pub enum Mouse {
292    /// The mouse was moved.
293    Move(Target),
294    /// A button was pressed.
295    Press {
296        /// The button.
297        button: mouse::Button,
298        /// The location of the press.
299        target: Option<Target>,
300    },
301    /// A button was released.
302    Release {
303        /// The button.
304        button: mouse::Button,
305        /// The location of the release.
306        target: Option<Target>,
307    },
308    /// A button was clicked.
309    Click {
310        /// The button.
311        button: mouse::Button,
312        /// The location of the click.
313        target: Option<Target>,
314    },
315}
316
317impl fmt::Display for Mouse {
318    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
319        match self {
320            Mouse::Move(target) => {
321                write!(f, "move {}", target)
322            }
323            Mouse::Press { button, target } => {
324                write!(f, "press {}", format::button_at(*button, target.as_ref()))
325            }
326            Mouse::Release { button, target } => {
327                write!(f, "release {}", format::button_at(*button, target.as_ref()))
328            }
329            Mouse::Click { button, target } => {
330                write!(f, "click {}", format::button_at(*button, target.as_ref()))
331            }
332        }
333    }
334}
335
336/// The target of an interaction.
337#[derive(Debug, Clone, PartialEq)]
338pub enum Target {
339    /// A widget with the given identifier.
340    Id(String),
341    /// A UI element containing the given text.
342    Text(String),
343    /// A specific point of the viewport.
344    Point(Point),
345}
346
347impl fmt::Display for Target {
348    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
349        match self {
350            Self::Id(id) => f.write_str(&format::id(id)),
351            Self::Point(point) => f.write_str(&format::point(*point)),
352            Self::Text(text) => f.write_str(&format::string(text)),
353        }
354    }
355}
356
357/// A keyboard interaction.
358#[derive(Debug, Clone, PartialEq)]
359pub enum Keyboard {
360    /// A key was pressed.
361    Press(Key),
362    /// A key was released.
363    Release(Key),
364    /// A key was "typed" (press and released).
365    Type(Key),
366    /// A bunch of text was typed.
367    Typewrite(String),
368}
369
370impl fmt::Display for Keyboard {
371    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
372        match self {
373            Keyboard::Press(key) => {
374                write!(f, "press {}", format::key(*key))
375            }
376            Keyboard::Release(key) => {
377                write!(f, "release {}", format::key(*key))
378            }
379            Keyboard::Type(key) => {
380                write!(f, "type {}", format::key(*key))
381            }
382            Keyboard::Typewrite(text) => {
383                write!(f, "type \"{text}\"")
384            }
385        }
386    }
387}
388
389/// A keyboard key.
390#[derive(Debug, Clone, Copy, PartialEq, Eq)]
391#[allow(missing_docs)]
392pub enum Key {
393    Enter,
394    Escape,
395    Tab,
396    Backspace,
397    Space,
398    ArrowUp,
399    ArrowDown,
400    ArrowLeft,
401    ArrowRight,
402    Home,
403    End,
404    PageUp,
405    PageDown,
406}
407
408impl From<Key> for keyboard::Key {
409    fn from(key: Key) -> Self {
410        match key {
411            Key::Enter => Self::Named(keyboard::key::Named::Enter),
412            Key::Escape => Self::Named(keyboard::key::Named::Escape),
413            Key::Tab => Self::Named(keyboard::key::Named::Tab),
414            Key::Backspace => Self::Named(keyboard::key::Named::Backspace),
415            Key::Space => Self::Named(keyboard::key::Named::Space),
416            Key::ArrowUp => Self::Named(keyboard::key::Named::ArrowUp),
417            Key::ArrowDown => Self::Named(keyboard::key::Named::ArrowDown),
418            Key::ArrowLeft => Self::Named(keyboard::key::Named::ArrowLeft),
419            Key::ArrowRight => Self::Named(keyboard::key::Named::ArrowRight),
420            Key::Home => Self::Named(keyboard::key::Named::Home),
421            Key::End => Self::Named(keyboard::key::Named::End),
422            Key::PageUp => Self::Named(keyboard::key::Named::PageUp),
423            Key::PageDown => Self::Named(keyboard::key::Named::PageDown),
424        }
425    }
426}
427
428mod format {
429    use super::*;
430
431    pub fn button_at(button: mouse::Button, at: Option<&Target>) -> String {
432        let button = self::button(button);
433
434        if let Some(at) = at {
435            if button.is_empty() {
436                at.to_string()
437            } else {
438                format!("{} {}", button, at)
439            }
440        } else {
441            button.to_owned()
442        }
443    }
444
445    pub fn button(button: mouse::Button) -> &'static str {
446        match button {
447            mouse::Button::Left => "",
448            mouse::Button::Right => "right",
449            mouse::Button::Middle => "middle",
450            mouse::Button::Back => "back",
451            mouse::Button::Forward => "forward",
452            mouse::Button::Other(_) => "other",
453        }
454    }
455
456    pub fn point(point: Point) -> String {
457        format!("({:.2}, {:.2})", point.x, point.y)
458    }
459
460    pub fn key(key: Key) -> &'static str {
461        match key {
462            Key::Enter => "enter",
463            Key::Escape => "escape",
464            Key::Tab => "tab",
465            Key::Backspace => "backspace",
466            Key::Space => "space",
467            Key::ArrowUp => "up",
468            Key::ArrowDown => "down",
469            Key::ArrowLeft => "left",
470            Key::ArrowRight => "right",
471            Key::Home => "home",
472            Key::End => "end",
473            Key::PageUp => "pageup",
474            Key::PageDown => "pagedown",
475        }
476    }
477
478    pub fn string(text: &str) -> String {
479        format!("\"{}\"", text.escape_default())
480    }
481
482    pub fn id(id: &str) -> String {
483        format!("#{id}")
484    }
485}
486
487/// A testing assertion.
488///
489/// Expectations are instructions that verify the current state of
490/// the user interface of an application.
491#[derive(Debug, Clone, PartialEq)]
492pub enum Expectation {
493    /// Expect some element to contain some text.
494    Text(String),
495    /// Expect a widget with the given text or id to have keyboard focus.
496    Focused(Target),
497}
498
499impl fmt::Display for Expectation {
500    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
501        match self {
502            Expectation::Text(text) => {
503                write!(f, "expect {}", format::string(text))
504            }
505            Expectation::Focused(target) => {
506                write!(f, "expect focused {target}")
507            }
508        }
509    }
510}
511
512pub use parser::Error as ParseError;
513
514mod parser {
515    use super::*;
516
517    use nom::branch::alt;
518    use nom::bytes::complete::tag;
519    use nom::bytes::{is_not, take_while_m_n};
520    use nom::character::complete::{alphanumeric1, char, multispace0, multispace1};
521    use nom::combinator::{map, map_opt, map_res, opt, recognize, success, value, verify};
522    use nom::error::ParseError;
523    use nom::multi::{fold, many1_count};
524    use nom::number::float;
525    use nom::sequence::{delimited, preceded, separated_pair};
526    use nom::{Finish, IResult, Parser};
527
528    /// A parsing error.
529    #[derive(Debug, Clone, thiserror::Error)]
530    #[error("parse error: {0}")]
531    pub struct Error(nom::error::Error<String>);
532
533    pub fn run(input: &str) -> Result<Instruction, Error> {
534        match instruction.parse_complete(input).finish() {
535            Ok((_rest, instruction)) => Ok(instruction),
536            Err(error) => Err(Error(error.cloned())),
537        }
538    }
539
540    fn instruction(input: &str) -> IResult<&str, Instruction> {
541        alt((
542            map(interaction, Instruction::Interact),
543            map(expectation, Instruction::Expect),
544        ))
545        .parse(input)
546    }
547
548    fn interaction(input: &str) -> IResult<&str, Interaction> {
549        alt((
550            map(mouse, Interaction::Mouse),
551            map(keyboard, Interaction::Keyboard),
552        ))
553        .parse(input)
554    }
555
556    fn mouse(input: &str) -> IResult<&str, Mouse> {
557        let mouse_move = preceded(tag("move "), target).map(Mouse::Move);
558
559        alt((mouse_move, mouse_click, mouse_press, mouse_release)).parse(input)
560    }
561
562    fn mouse_click(input: &str) -> IResult<&str, Mouse> {
563        let (input, _) = tag("click ")(input)?;
564        let (input, (button, target)) = mouse_button_at(input)?;
565
566        Ok((input, Mouse::Click { button, target }))
567    }
568
569    fn mouse_press(input: &str) -> IResult<&str, Mouse> {
570        let (input, _) = tag("press ")(input)?;
571        let (input, (button, target)) = mouse_button_at(input)?;
572
573        Ok((input, Mouse::Press { button, target }))
574    }
575
576    fn mouse_release(input: &str) -> IResult<&str, Mouse> {
577        let (input, _) = tag("release ")(input)?;
578        let (input, (button, target)) = mouse_button_at(input)?;
579
580        Ok((input, Mouse::Release { button, target }))
581    }
582
583    fn mouse_button_at(input: &str) -> IResult<&str, (mouse::Button, Option<Target>)> {
584        let (input, button) = mouse_button(input)?;
585        let (input, at) = opt(target).parse(input)?;
586
587        Ok((input, (button, at)))
588    }
589
590    fn target(input: &str) -> IResult<&str, Target> {
591        alt((
592            id.map(String::from).map(Target::Id),
593            string.map(Target::Text),
594            point.map(Target::Point),
595        ))
596        .parse(input)
597    }
598
599    fn mouse_button(input: &str) -> IResult<&str, mouse::Button> {
600        alt((
601            tag("right").map(|_| mouse::Button::Right),
602            success(mouse::Button::Left),
603        ))
604        .parse(input)
605    }
606
607    fn keyboard(input: &str) -> IResult<&str, Keyboard> {
608        alt((
609            map(preceded(tag("type "), string), Keyboard::Typewrite),
610            map(preceded(tag("type "), key), Keyboard::Type),
611        ))
612        .parse(input)
613    }
614
615    fn expectation(input: &str) -> IResult<&str, Expectation> {
616        alt((
617            map(
618                preceded(tag("expect focused "), target),
619                Expectation::Focused,
620            ),
621            map(preceded(tag("expect "), string), Expectation::Text),
622        ))
623        .parse(input)
624    }
625
626    fn key(input: &str) -> IResult<&str, Key> {
627        alt((
628            map(tag("enter"), |_| Key::Enter),
629            map(tag("escape"), |_| Key::Escape),
630            map(tag("tab"), |_| Key::Tab),
631            map(tag("backspace"), |_| Key::Backspace),
632            map(tag("space"), |_| Key::Space),
633            map(tag("up"), |_| Key::ArrowUp),
634            map(tag("down"), |_| Key::ArrowDown),
635            map(tag("left"), |_| Key::ArrowLeft),
636            map(tag("right"), |_| Key::ArrowRight),
637            map(tag("home"), |_| Key::Home),
638            map(tag("end"), |_| Key::End),
639            map(tag("pageup"), |_| Key::PageUp),
640            map(tag("pagedown"), |_| Key::PageDown),
641        ))
642        .parse(input)
643    }
644
645    fn id(input: &str) -> IResult<&str, &str> {
646        preceded(
647            char('#'),
648            recognize(many1_count(alt((alphanumeric1, tag("_"), tag("-"))))),
649        )
650        .parse(input)
651    }
652
653    fn point(input: &str) -> IResult<&str, Point> {
654        let comma = whitespace(char(','));
655
656        map(
657            delimited(
658                char('('),
659                separated_pair(float(), comma, float()),
660                char(')'),
661            ),
662            |(x, y)| Point { x, y },
663        )
664        .parse(input)
665    }
666
667    pub fn whitespace<'a, O, E: ParseError<&'a str>, F>(
668        inner: F,
669    ) -> impl Parser<&'a str, Output = O, Error = E>
670    where
671        F: Parser<&'a str, Output = O, Error = E>,
672    {
673        delimited(multispace0, inner, multispace0)
674    }
675
676    // Taken from https://github.com/rust-bakery/nom/blob/51c3c4e44fa78a8a09b413419372b97b2cc2a787/examples/string.rs
677    //
678    // Copyright (c) 2014-2019 Geoffroy Couprie
679    //
680    // Permission is hereby granted, free of charge, to any person obtaining
681    // a copy of this software and associated documentation files (the
682    // "Software"), to deal in the Software without restriction, including
683    // without limitation the rights to use, copy, modify, merge, publish,
684    // distribute, sublicense, and/or sell copies of the Software, and to
685    // permit persons to whom the Software is furnished to do so, subject to
686    // the following conditions:
687    //
688    // The above copyright notice and this permission notice shall be
689    // included in all copies or substantial portions of the Software.
690    //
691    // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
692    // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
693    // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
694    // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
695    // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
696    // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
697    // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
698    fn string(input: &str) -> IResult<&str, String> {
699        #[derive(Debug, Clone, Copy)]
700        enum Fragment<'a> {
701            Literal(&'a str),
702            EscapedChar(char),
703            EscapedWS,
704        }
705
706        fn fragment(input: &str) -> IResult<&str, Fragment<'_>> {
707            alt((
708                map(string_literal, Fragment::Literal),
709                map(escaped_char, Fragment::EscapedChar),
710                value(Fragment::EscapedWS, escaped_whitespace),
711            ))
712            .parse(input)
713        }
714
715        fn string_literal<'a, E: ParseError<&'a str>>(
716            input: &'a str,
717        ) -> IResult<&'a str, &'a str, E> {
718            let not_quote_slash = is_not("\"\\");
719
720            verify(not_quote_slash, |s: &str| !s.is_empty()).parse(input)
721        }
722
723        fn unicode(input: &str) -> IResult<&str, char> {
724            let parse_hex = take_while_m_n(1, 6, |c: char| c.is_ascii_hexdigit());
725
726            let parse_delimited_hex =
727                preceded(char('u'), delimited(char('{'), parse_hex, char('}')));
728
729            let parse_u32 = map_res(parse_delimited_hex, move |hex| u32::from_str_radix(hex, 16));
730
731            map_opt(parse_u32, std::char::from_u32).parse(input)
732        }
733
734        fn escaped_char(input: &str) -> IResult<&str, char> {
735            preceded(
736                char('\\'),
737                alt((
738                    unicode,
739                    value('\n', char('n')),
740                    value('\r', char('r')),
741                    value('\t', char('t')),
742                    value('\u{08}', char('b')),
743                    value('\u{0C}', char('f')),
744                    value('\\', char('\\')),
745                    value('/', char('/')),
746                    value('"', char('"')),
747                )),
748            )
749            .parse(input)
750        }
751
752        fn escaped_whitespace(input: &str) -> IResult<&str, &str> {
753            preceded(char('\\'), multispace1).parse(input)
754        }
755
756        let build_string = fold(0.., fragment, String::new, |mut string, fragment| {
757            match fragment {
758                Fragment::Literal(s) => string.push_str(s),
759                Fragment::EscapedChar(c) => string.push(c),
760                Fragment::EscapedWS => {}
761            }
762            string
763        });
764
765        delimited(char('"'), build_string, char('"')).parse(input)
766    }
767}