android_manifest/resources/
mod.rs

1mod any;
2mod mipmap_or_drawable;
3mod res_or_string;
4mod types;
5
6pub use any::*;
7pub use mipmap_or_drawable::*;
8pub use res_or_string::*;
9
10use serde::{
11    Deserialize, Deserializer, Serialize, Serializer,
12    de::{self, Visitor},
13};
14use std::{
15    fmt,
16    io::{Read, Write},
17    marker::PhantomData,
18    str::FromStr,
19};
20pub use types::*;
21use yaserde::{YaDeserialize, YaSerialize};
22
23/// Trait implemented by types that can be used as resource.
24pub trait ResourceType: FromStr {
25    /// Creates new instance of [`Resource`](crate::Resource).
26    fn new(name: &str, package: Option<String>) -> Resource<Self> {
27        Resource {
28            name: name.to_string(),
29            package,
30            phantom: PhantomData,
31        }
32    }
33    /// Returns string representation of the `resource_type`.
34    fn resource_type() -> &'static str;
35}
36
37/// Generic resource type.
38#[derive(Debug, PartialEq, Eq, Clone)]
39pub struct Resource<T: ResourceType> {
40    name: String,
41    package: Option<String>,
42    phantom: PhantomData<T>,
43}
44
45impl<T: ResourceType> Resource<T> {
46    pub fn new(name: &str) -> Self {
47        Self {
48            name: name.to_string(),
49            package: None,
50            phantom: PhantomData,
51        }
52    }
53
54    pub fn new_with_package(name: &str, package: Option<String>) -> Self {
55        Self {
56            name: name.to_string(),
57            package,
58            phantom: PhantomData,
59        }
60    }
61
62    pub fn name(&self) -> &str {
63        &self.name
64    }
65
66    pub fn resource_type(&self) -> &'static str {
67        T::resource_type()
68    }
69}
70
71impl<T: ResourceType> fmt::Display for Resource<T> {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        if let Some(package) = &self.package {
74            write!(f, "@{}:{}/{}", package, T::resource_type(), self.name)
75        } else {
76            write!(f, "@{}/{}", T::resource_type(), self.name)
77        }
78    }
79}
80
81impl<T: ResourceType> Serialize for Resource<T> {
82    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
83    where
84        S: Serializer,
85    {
86        serializer.serialize_str(&self.to_string())
87    }
88}
89
90impl<T: ResourceType> YaSerialize for Resource<T> {
91    fn serialize<W: Write>(&self, writer: &mut yaserde::ser::Serializer<W>) -> Result<(), String> {
92        if let Some(package) = &self.package {
93            let _ret = writer.write(xml::writer::XmlEvent::characters(&format!(
94                "@{}:{}/{}",
95                package,
96                T::resource_type(),
97                self.name
98            )));
99        } else {
100            let _ret = writer.write(xml::writer::XmlEvent::characters(&format!(
101                "@{}/{}",
102                T::resource_type(),
103                self.name
104            )));
105        }
106        Ok(())
107    }
108
109    fn serialize_attributes(
110        &self,
111        attributes: Vec<xml::attribute::OwnedAttribute>,
112        namespace: xml::namespace::Namespace,
113    ) -> Result<
114        (
115            Vec<xml::attribute::OwnedAttribute>,
116            xml::namespace::Namespace,
117        ),
118        String,
119    > {
120        Ok((attributes, namespace))
121    }
122}
123
124struct ResourceVisitor<T: ResourceType> {
125    phantom: PhantomData<T>,
126}
127
128impl<T: ResourceType> ResourceVisitor<T> {
129    pub fn new() -> Self {
130        ResourceVisitor {
131            phantom: PhantomData,
132        }
133    }
134}
135
136impl<'de, T: ResourceType> Visitor<'de> for ResourceVisitor<T> {
137    type Value = Resource<T>;
138
139    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
140        formatter.write_str(&format!(
141            "an {} resource in format @[package:]{}/resource_name",
142            T::resource_type(),
143            T::resource_type()
144        ))
145    }
146
147    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
148    where
149        E: de::Error,
150    {
151        parse_resource_with_type(v).map_err(E::custom)
152    }
153}
154
155impl<'de, T: ResourceType> Deserialize<'de> for Resource<T> {
156    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
157    where
158        D: Deserializer<'de>,
159    {
160        deserializer.deserialize_string(ResourceVisitor::new())
161    }
162}
163
164impl<T: ResourceType> YaDeserialize for Resource<T> {
165    fn deserialize<R: Read>(reader: &mut yaserde::de::Deserializer<R>) -> Result<Self, String> {
166        loop {
167            match reader.next_event()? {
168                xml::reader::XmlEvent::StartElement { .. } => {}
169                xml::reader::XmlEvent::Characters(ref text_content) => {
170                    return parse_resource_with_type(text_content);
171                }
172                _ => {
173                    break;
174                }
175            }
176        }
177        Err("Unable to parse attribute".to_string())
178    }
179}
180
181/// Parses a resource string in format
182/// `@[package:]resource_type/resource_name` into three parts
183fn parse_resource(resource: &str) -> Result<(Option<String>, String, String), String> {
184    if resource.is_empty() {
185        return Err("value of attribute is empty".to_string());
186    };
187    let split_str: Vec<_> = resource.split('/').collect();
188    if split_str.len() != 2 {
189        return Err(
190            "a wrong resource format, expected format @[package:]resource_type/resource_name"
191                .to_string(),
192        );
193    };
194    let first_part = split_str.first().unwrap(); // Can be unwraped because we checked the length.
195    let resource_type = &first_part[1..];
196    let split_type: Vec<_> = resource_type.split(':').collect();
197    let (resource_type, package) = if split_type.len() == 2 {
198        (split_type[1], Some(split_type[0].to_string()))
199    } else {
200        (split_type[0], None)
201    };
202    let resource_name = split_str.get(1).unwrap(); // Can be unwraped because we checked the length.
203    Ok((
204        package,
205        resource_type.to_string(),
206        resource_name.to_string(),
207    ))
208}
209
210/// Parses a resource string into given `Resource<ResourceType>`
211fn parse_resource_with_type<T: ResourceType>(resource: &str) -> Result<Resource<T>, String> {
212    let (package, resource_type, resource_name) = parse_resource(resource)?;
213    if resource_type != T::resource_type() {
214        return Err(format!(
215            "a wrong resource type, expected @[package:]{}/{}, found {}",
216            T::resource_type(),
217            resource_name,
218            resource
219        ));
220    };
221    Ok(Resource {
222        name: resource_name,
223        package,
224        phantom: PhantomData,
225    })
226}