Skip to main content

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 crate::xml::{XmlDeserialize, XmlSerialize};
11use serde::{
12    Deserialize, Deserializer, Serialize, Serializer,
13    de::{self, Visitor},
14};
15use std::{
16    fmt,
17    io::{Read, Write},
18    marker::PhantomData,
19    str::FromStr,
20};
21pub use types::*;
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> XmlSerialize for Resource<T> {
91    fn serialize<W: Write>(
92        &self,
93        writer: &mut crate::xml::ser::Serializer<W>,
94    ) -> Result<(), String> {
95        let value = if let Some(package) = &self.package {
96            format!("@{}:{}/{}", package, T::resource_type(), self.name)
97        } else {
98            format!("@{}/{}", T::resource_type(), self.name)
99        };
100        writer
101            .write(xml::writer::XmlEvent::characters(&value))
102            .map_err(|error| error.to_string())
103    }
104}
105
106struct ResourceVisitor<T: ResourceType> {
107    phantom: PhantomData<T>,
108}
109
110impl<T: ResourceType> ResourceVisitor<T> {
111    pub fn new() -> Self {
112        ResourceVisitor {
113            phantom: PhantomData,
114        }
115    }
116}
117
118impl<'de, T: ResourceType> Visitor<'de> for ResourceVisitor<T> {
119    type Value = Resource<T>;
120
121    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
122        formatter.write_str(&format!(
123            "an {} resource in format @[package:]{}/resource_name",
124            T::resource_type(),
125            T::resource_type()
126        ))
127    }
128
129    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
130    where
131        E: de::Error,
132    {
133        parse_resource_with_type(v).map_err(E::custom)
134    }
135}
136
137impl<'de, T: ResourceType> Deserialize<'de> for Resource<T> {
138    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
139    where
140        D: Deserializer<'de>,
141    {
142        deserializer.deserialize_string(ResourceVisitor::new())
143    }
144}
145
146impl<T: ResourceType> XmlDeserialize for Resource<T> {
147    fn deserialize<R: Read>(reader: &mut crate::xml::de::Deserializer<R>) -> Result<Self, String> {
148        loop {
149            match reader.next_event()? {
150                xml::reader::XmlEvent::StartElement { .. } => {}
151                xml::reader::XmlEvent::Characters(ref text_content) => {
152                    return parse_resource_with_type(text_content);
153                }
154                _ => {
155                    break;
156                }
157            }
158        }
159        Err("Unable to parse attribute".to_string())
160    }
161}
162
163/// Parses a resource string in format
164/// `@[package:]resource_type/resource_name` into three parts
165fn parse_resource(resource: &str) -> Result<(Option<String>, String, String), String> {
166    if resource.is_empty() {
167        return Err("value of attribute is empty".to_string());
168    };
169    let split_str: Vec<_> = resource.split('/').collect();
170    if split_str.len() != 2 {
171        return Err(
172            "a wrong resource format, expected format @[package:]resource_type/resource_name"
173                .to_string(),
174        );
175    };
176    let first_part = split_str.first().unwrap(); // Can be unwraped because we checked the length.
177    let resource_type = &first_part[1..];
178    let split_type: Vec<_> = resource_type.split(':').collect();
179    let (resource_type, package) = if split_type.len() == 2 {
180        (split_type[1], Some(split_type[0].to_string()))
181    } else {
182        (split_type[0], None)
183    };
184    let resource_name = split_str.get(1).unwrap(); // Can be unwraped because we checked the length.
185    Ok((
186        package,
187        resource_type.to_string(),
188        resource_name.to_string(),
189    ))
190}
191
192/// Parses a resource string into given `Resource<ResourceType>`
193fn parse_resource_with_type<T: ResourceType>(resource: &str) -> Result<Resource<T>, String> {
194    let (package, resource_type, resource_name) = parse_resource(resource)?;
195    if resource_type != T::resource_type() {
196        return Err(format!(
197            "a wrong resource type, expected @[package:]{}/{}, found {}",
198            T::resource_type(),
199            resource_name,
200            resource
201        ));
202    };
203    Ok(Resource {
204        name: resource_name,
205        package,
206        phantom: PhantomData,
207    })
208}