crystal_cif_io/grammar/strings_textfields/
unquoted_string.rs1use std::fmt::Display;
2
3use winnow::{
4 combinator::{alt, repeat},
5 error::StrContext,
6 stream::AsChar,
7 PResult, Parser,
8};
9
10use crate::grammar::{
11 character_sets::{Eol, NonBlankChar, NotEol, OrdinaryChar},
12 SyntacticUnit,
13};
14
15#[derive(Debug, Clone)]
16pub struct UnquotedString {
17 content: String,
18}
19
20impl AsRef<str> for UnquotedString {
21 #[inline]
22 fn as_ref(&self) -> &str {
23 <String as AsRef<str>>::as_ref(&self.content)
24 }
25}
26
27impl UnquotedString {
28 pub fn new(content: String) -> Self {
29 Self { content }
30 }
31}
32
33fn not_eol_unquoted(input: &mut &str) -> PResult<UnquotedString> {
34 (
35 NotEol::parser
36 .verify(|c| c.char() != '_')
37 .context(winnow::error::StrContext::Expected(
38 winnow::error::StrContextValue::Description("Unquoted string without '_' prefix"),
39 )),
40 alt((OrdinaryChar::parser.map(|oc| oc.as_char()), ';')),
41 repeat::<_, _, String, _, _>(0.., NonBlankChar::parser),
42 )
43 .map(|(_not_eol, c, content)| UnquotedString::new(format!("{c}{content}")))
44 .parse_next(input)
45}
46
47fn eol_unquoted(input: &mut &str) -> PResult<UnquotedString> {
48 (
49 Eol::parser.context(StrContext::Label("Leading <EOL> for an <UnquotedString>")),
50 OrdinaryChar::parser.context(StrContext::Label("<OrdinaryChar>")),
51 repeat::<_, _, String, _, _>(0.., NonBlankChar::parser),
52 )
53 .map(|(_eol, oc, content)| UnquotedString::new(format!("{oc}{content}")))
54 .parse_next(input)
55}
56
57pub fn pure_unquoted(input: &mut &str) -> PResult<UnquotedString> {
58 (
59 OrdinaryChar::parser.context(StrContext::Label("<OrdinaryChar>")),
60 repeat::<_, _, String, _, _>(0.., NonBlankChar::parser),
61 )
62 .map(|(oc, content)| UnquotedString::new(format!("{oc}{content}")))
63 .parse_next(input)
64}
65
66impl SyntacticUnit for UnquotedString {
67 type ParseResult = Self;
68
69 type FormatOutput = String;
70
71 fn parser(input: &mut &str) -> winnow::prelude::PResult<Self::ParseResult> {
72 alt((not_eol_unquoted, eol_unquoted)).parse_next(input)
73 }
74
75 fn formatted_output(&self) -> Self::FormatOutput {
76 self.content.to_owned()
77 }
78}
79
80impl Display for UnquotedString {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 write!(f, "{}", self.formatted_output())
83 }
84}
85
86#[cfg(test)]
87mod test {
88 use crate::grammar::{whitespace_comments::WhiteSpace, SyntacticUnit};
89
90 use super::UnquotedString;
91
92 #[test]
93 fn unquoted_string() {
94 let mut input = " rm # known chiral centre
95";
96 let value = UnquotedString::parser(&mut input).unwrap();
97 let white_space = WhiteSpace::parser(&mut input).unwrap();
98 println!("{value}{white_space}");
99 }
100}