android_manifest/resources/
res_or_string.rs

1use super::{parse_resource_with_type, Resource, ResourceType, ResourceVisitor, StringResource};
2use serde::{
3    de::{self, Visitor},
4    Deserialize, Deserializer, Serialize, Serializer,
5};
6use std::fmt;
7use std::io::{Read, Write};
8use yaserde::{YaDeserialize, YaSerialize};
9
10/// Enum used when the value can be string resource or just a row string.
11#[derive(Debug, PartialEq, Eq, Clone)]
12pub enum StringResourceOrString {
13    StringResource(Resource<StringResource>),
14    String(String),
15}
16
17impl StringResourceOrString {
18    pub fn resource(name: &str, package: Option<String>) -> StringResourceOrString {
19        Self::StringResource(StringResource::new(name, package))
20    }
21
22    pub fn string(s: &str) -> StringResourceOrString {
23        Self::String(s.to_string())
24    }
25}
26
27impl ToString for StringResourceOrString {
28    fn to_string(&self) -> String {
29        match self {
30            Self::StringResource(r) => r.to_string(),
31            Self::String(v) => v.clone(),
32        }
33    }
34}
35
36impl Serialize for StringResourceOrString {
37    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
38    where
39        S: Serializer,
40    {
41        match self {
42            StringResourceOrString::StringResource(resource) => {
43                Serialize::serialize(&resource, serializer)
44            }
45            StringResourceOrString::String(value) => serializer.serialize_str(value),
46        }
47    }
48}
49
50impl YaSerialize for StringResourceOrString {
51    fn serialize<W: Write>(&self, writer: &mut yaserde::ser::Serializer<W>) -> Result<(), String> {
52        match self {
53            StringResourceOrString::StringResource(resource) => {
54                YaSerialize::serialize(resource, writer)?;
55            }
56            StringResourceOrString::String(value) => {
57                let _ret = writer.write(xml::writer::XmlEvent::characters(value));
58            }
59        }
60        Ok(())
61    }
62
63    fn serialize_attributes(
64        &self,
65        attributes: Vec<xml::attribute::OwnedAttribute>,
66        namespace: xml::namespace::Namespace,
67    ) -> Result<
68        (
69            Vec<xml::attribute::OwnedAttribute>,
70            xml::namespace::Namespace,
71        ),
72        String,
73    > {
74        Ok((attributes, namespace))
75    }
76}
77
78struct StringResourceOrStringVisitor;
79
80impl<'de> Visitor<'de> for StringResourceOrStringVisitor {
81    type Value = StringResourceOrString;
82
83    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
84        formatter.write_str("an string resource in format @string/resource_name or string")
85    }
86
87    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
88    where
89        E: de::Error,
90    {
91        if v.is_empty() {
92            return Err(E::custom("value of attribute is empty"));
93        };
94        if v.starts_with('@') {
95            Ok(StringResourceOrString::StringResource(
96                ResourceVisitor::<StringResource>::new().visit_str(v)?,
97            ))
98        } else {
99            Ok(StringResourceOrString::String(v.to_owned()))
100        }
101    }
102}
103
104impl<'de> Deserialize<'de> for StringResourceOrString {
105    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
106    where
107        D: Deserializer<'de>,
108    {
109        deserializer.deserialize_string(StringResourceOrStringVisitor)
110    }
111}
112
113impl YaDeserialize for StringResourceOrString {
114    fn deserialize<R: Read>(reader: &mut yaserde::de::Deserializer<R>) -> Result<Self, String> {
115        loop {
116            match reader.next_event()? {
117                xml::reader::XmlEvent::StartElement { .. } => {}
118                xml::reader::XmlEvent::Characters(text_content) => {
119                    if text_content.is_empty() {
120                        return Err("value of attribute is empty".to_string());
121                    };
122                    if text_content.starts_with('@') {
123                        return Ok(StringResourceOrString::StringResource(
124                            parse_resource_with_type(&text_content)?,
125                        ));
126                    } else {
127                        return Ok(StringResourceOrString::String(text_content));
128                    }
129                }
130                _ => {
131                    break;
132                }
133            }
134        }
135        Err("Unable to parse attribute".to_string())
136    }
137}