zuicon_util/
lib.rs

1// Copyright (c) 2021 Xu Shaohua <shaohua@biofan.org>. All rights reserved.
2// Use of this source is governed by Apache-2.0 License that can be found
3// in the LICENSE file.
4
5#![deny(
6    warnings,
7    clippy::all,
8    clippy::cargo,
9    clippy::nursery,
10    clippy::pedantic
11)]
12
13use roxmltree::{Document, Error, Node, NodeType};
14
15pub const UPDATE_KEY: &str = "ZUICON_UPDATE";
16pub const TEMPLATE_FILE: &str = include_str!("template.rs");
17
18/// Check whether ZUICON modules shall be refreshed.
19///
20/// Current `ZUICON_UPDATE=1` environment is used.
21#[must_use]
22pub fn need_update() -> bool {
23    std::env::var_os(UPDATE_KEY).map_or(false, |val| !val.is_empty())
24}
25
26/// Get inner html of an svg file, without `<svg>` root tag.
27#[must_use]
28pub fn get_svg_inner(s: &str) -> Option<&str> {
29    let Some(start_index) = s.find("<svg") else {
30        return None
31    };
32    let Some(end_index) = s.find("</svg>") else {
33        return None
34    };
35
36    let mut start_index_end = start_index;
37    for (index, c) in s[start_index..].chars().enumerate() {
38        if c == '>' {
39            start_index_end = start_index + index + 1;
40            break;
41        }
42    }
43    if start_index == start_index_end || start_index_end >= end_index {
44        return None;
45    }
46    Some(s[start_index_end..end_index].trim())
47}
48
49/// Get all `<path />` nodes in an svg file.
50///
51/// # Errors
52/// Returns error if s is not a valid svg file.
53pub fn get_svg_path_data(s: &str) -> Result<String, Error> {
54    let doc = Document::parse(s)?;
55    let nodes: Vec<Node> = doc
56        .descendants()
57        .filter(|n| n.node_type() == NodeType::Element && n.has_tag_name("path"))
58        .collect();
59    let mut s = Vec::new();
60    for node in nodes {
61        let mut parts = vec!["<path".to_owned()];
62        for attr in node.attributes() {
63            parts.push(format!(" {}=\"{}\"", attr.name(), attr.value()));
64        }
65        parts.push("/>".to_owned());
66        s.push(parts.join(""));
67    }
68    Ok(s.join(""))
69}
70
71#[cfg(test)]
72mod tests {
73    use super::get_svg_path_data;
74
75    #[test]
76    fn test_path() {
77        let svg_file = include_str!("../tests/2k_two_tone_24px.svg");
78        let ret = get_svg_path_data(svg_file);
79        assert!(ret.is_ok());
80        const REAL_PATH: &str = r#"<path d="M5,19h14V5H5V19z M13,9h1.5v2.25L16.25,9H18l-2.25,3L18,15h-1.75l-1.75-2.25V15H13V9z M6.5,12.5 c0-0.55,0.45-1,1-1h2v-1h-3V9H10c0.55,0,1,0.45,1,1v1.5c0,0.55-0.45,1-1,1H8v1h3V15H6.5V12.5z" opacity=".3"/><path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M19,19H5V5h14V19z"/><path d="M11,13.5H8v-1h2c0.55,0,1-0.45,1-1V10c0-0.55-0.45-1-1-1H6.5v1.5h3v1h-2c-0.55,0-1,0.45-1,1V15H11V13.5z"/>"#;
81        assert_eq!(ret.unwrap().as_str(), REAL_PATH);
82    }
83}