nom_xml/
reference.rs

1// reference.rs
2
3use crate::{
4    attribute::AttributeValue,
5    parse::Parse,
6    prolog::subset::entity::{entity_value::EntityValue, EntitySource},
7    transcode::Decode,
8    Document, IResult, Name,
9};
10use nom::{
11    branch::alt,
12    bytes::complete::tag,
13    character::complete::{char, digit1, hex_digit1},
14    combinator::map,
15    sequence::tuple,
16};
17use std::{cell::RefCell, collections::HashMap, rc::Rc};
18
19#[derive(Clone, PartialEq, Eq)]
20pub enum Reference {
21    EntityRef(Name),
22    CharRef(String),
23}
24
25impl<'a> Parse<'a> for Reference {
26    type Args = EntitySource;
27    //);
28    type Output = IResult<&'a str, Self>;
29    //[67] Reference ::= EntityRef | CharRef
30    fn parse(input: &'a str, args: Self::Args) -> Self::Output {
31        alt((
32            move |i| Self::parse_entity_ref(i, args.clone()),
33            Self::parse_char_reference,
34        ))(input)
35    }
36}
37impl Reference {
38    pub(crate) fn normalize_entity(
39        &self,
40        entity_references: Rc<RefCell<HashMap<(Name, EntitySource), EntityValue>>>,
41    ) -> EntityValue {
42        match self {
43            Reference::EntityRef(name) => {
44                let refs_map = entity_references.borrow();
45
46                // Try to find the most appropriate source based on available references
47                let possible_sources = [EntitySource::External, EntitySource::Internal];
48                let entity_value = possible_sources
49                    .iter()
50                    .filter_map(|source| refs_map.get(&(name.clone(), source.clone())).cloned())
51                    .next()
52                    .unwrap_or_else(|| EntityValue::Value(name.local_part.clone())); // Default to just returning the name if no entity is found
53
54                match entity_value {
55                    EntityValue::Value(val) => {
56                        if refs_map.contains_key(&(
57                            Name {
58                                prefix: None,
59                                local_part: val.clone(),
60                            },
61                            EntitySource::Internal,
62                        )) {
63                            // This value is another reference, recurse
64                            let reference_name = Name {
65                                prefix: None,
66                                local_part: val,
67                            };
68                            Reference::EntityRef(reference_name)
69                                .normalize_entity(entity_references.clone())
70                        } else {
71                            EntityValue::Value(val)
72                        }
73                    }
74                    EntityValue::Reference(ref next_ref) => {
75                        // Recursively resolve the next reference
76                        next_ref.normalize_entity(entity_references.clone())
77                    }
78                    _ => entity_value,
79                }
80            }
81            Reference::CharRef(value) => EntityValue::Value(value.clone()),
82        }
83    }
84
85    pub(crate) fn normalize_attribute(
86        &self,
87        entity_references: Rc<RefCell<HashMap<(Name, EntitySource), EntityValue>>>,
88        entity_source: EntitySource,
89    ) -> AttributeValue {
90        match self {
91            Reference::EntityRef(name) => {
92                let refs_map = entity_references.borrow();
93                match refs_map
94                    .get(&(name.clone(), entity_source.clone()))
95                    .cloned()
96                {
97                    Some(EntityValue::Value(val))
98                        if refs_map.contains_key(&(
99                            Name {
100                                prefix: None,
101                                local_part: val.clone(),
102                            },
103                            entity_source.clone(),
104                        )) =>
105                    {
106                        let reference_name = Name {
107                            prefix: None,
108                            local_part: val,
109                        };
110                        Reference::EntityRef(reference_name)
111                            .normalize_attribute(entity_references.clone(), entity_source.clone())
112                    }
113                    Some(EntityValue::Reference(Reference::EntityRef(entity))) => {
114                        if let Some(EntityValue::Value(val)) = refs_map
115                            .get(&(entity.clone(), EntitySource::Internal))
116                            .cloned()
117                        {
118                            AttributeValue::Value(val)
119                        } else {
120                            Reference::EntityRef(entity.clone()).normalize_attribute(
121                                entity_references.clone(),
122                                EntitySource::External,
123                            )
124                        }
125                    }
126                    Some(entity_value) => {
127                        // Convert EntityValue to AttributeValue
128                        match entity_value {
129                            EntityValue::Value(val) => AttributeValue::Value(val),
130                            EntityValue::Reference(reference) => reference.normalize_attribute(
131                                entity_references.clone(),
132                                entity_source.clone(),
133                            ),
134                            EntityValue::Document(doc) => {
135                                if let Document::Empty = doc {
136                                    AttributeValue::EmptyExternalReference
137                                } else {
138                                    unimplemented!(
139                                        "Unexpected Document variant to convert to AttributeValue"
140                                    )
141                                }
142                            }
143                            _ => panic!("Unexpected EntityValue variant"),
144                        }
145                    }
146                    None => {
147                        if entity_source == EntitySource::External {
148                            if let Reference::EntityRef(_name) = &self {
149                                AttributeValue::Reference(self.clone())
150                            } else {
151                                AttributeValue::Value(name.local_part.clone())
152                            }
153                        } else {
154                            AttributeValue::Value(name.local_part.clone())
155                        }
156                    }
157                }
158            }
159            Reference::CharRef(value) => AttributeValue::Value(value.clone()),
160        }
161    }
162}
163
164impl<'a> ParseReference<'a> for Reference {}
165impl Decode for Reference {
166    fn as_str(&self) -> &str {
167        match self {
168            Reference::EntityRef(name) => &name.local_part,
169            Reference::CharRef(value) => value,
170        }
171    }
172}
173
174pub trait ParseReference<'a>: Parse<'a> + Decode {
175    //[68] EntityRef ::= '&' Name ';'
176    fn parse_entity_ref(input: &str, _entity_source: EntitySource) -> IResult<&str, Reference> {
177        let (input, reference) = map(
178            tuple((char('&'), Self::parse_name, char(';'))),
179            |(_, name, _)| Reference::EntityRef(name),
180        )(input)?;
181        Ok((input, reference))
182    }
183
184    //[69] PEReference ::= '%' Name ';'
185    fn parse_parameter_reference(input: &str) -> IResult<&str, Reference> {
186        let (input, output) = map(
187            tuple((char('%'), Self::parse_name, char(';'))),
188            |(_, name, _)| Reference::EntityRef(name),
189        )(input)?;
190        Ok((input, output))
191    }
192
193    //[66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
194    fn parse_char_reference(input: &str) -> IResult<&str, Reference> {
195        //TODO: remove reconstruction if possible
196        alt((
197            map(
198                tuple((tag("&#"), digit1, tag(";"))),
199                |(start, digits, end): (&str, &str, &str)| {
200                    let reconstructed = format!("{}{}{}", start, digits, end);
201                    let decoded = reconstructed.decode().unwrap().into_owned();
202                    Reference::CharRef(decoded)
203                },
204            ),
205            map(
206                tuple((tag("&#x"), hex_digit1, tag(";"))),
207                |(start, hex, end): (&str, &str, &str)| {
208                    let reconstructed = format!("{}{}{}", start, hex, end);
209                    let decoded = reconstructed.decode().unwrap().into_owned();
210                    Reference::CharRef(decoded)
211                },
212            ),
213        ))(input)
214    }
215}