freedesktop_categories_codegen/
lib.rs1#![deny(missing_debug_implementations)]
11#![forbid(unsafe_code)]
12
13extern crate amxml;
14extern crate chrono;
15extern crate curl;
16extern crate phf_codegen;
17
18pub use error::Error;
19
20use std::env;
21use std::fmt::{Display, Formatter, Result as FmtResult};
22use std::fs::{self, File};
23use std::io::Write;
24use std::path::{Path, PathBuf};
25
26use amxml::dom::new_document;
27use curl::easy::Easy;
28
29use generate::CategoryMap;
30
31mod error;
32mod generate;
33
34const SPEC_URL: &str = "https://specifications.freedesktop.org/menu-spec/";
35
36#[derive(Debug)]
38pub enum Version {
39 V090,
40 V091,
41 V092,
42 V100,
43 V110,
44 Latest,
45}
46
47impl Display for Version {
48 fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
49 match *self {
50 Version::V090 => write!(fmt, "0.9"),
51 Version::V091 => write!(fmt, "0.91"),
52 Version::V092 => write!(fmt, "0.92"),
53 Version::V100 => write!(fmt, "1.0"),
54 Version::V110 => write!(fmt, "1.1"),
55 Version::Latest => write!(fmt, "latest"),
56 }
57 }
58}
59
60#[derive(Debug)]
62pub struct DesktopMenuSpec {
63 xml_cache_dir: Option<PathBuf>,
64 always_download: bool,
65 output_name: &'static str,
66 version: Version,
67}
68
69impl DesktopMenuSpec {
70 pub fn new() -> Self {
72 DesktopMenuSpec {
73 xml_cache_dir: None,
74 always_download: false,
75 output_name: "map.rs",
76 version: Version::Latest,
77 }
78 }
79
80 pub fn xml_cache_dir<P: Into<PathBuf>>(&mut self, path: P) -> &mut Self {
84 self.xml_cache_dir = Some(path.into());
85 self
86 }
87
88 pub fn always_download(&mut self, value: bool) -> &mut Self {
92 self.always_download = value;
93 self
94 }
95
96 pub fn output_file_name(&mut self, name: &'static str) -> &mut Self {
100 self.output_name = name;
101 self
102 }
103
104 pub fn version(&mut self, ver: Version) -> &mut Self {
108 self.version = ver;
109 self
110 }
111
112 pub fn gen_static_map(&self) -> Result<(), Error> {
116 let cache_dir = self
117 .xml_cache_dir
118 .clone()
119 .unwrap_or(env::var("OUT_DIR")?.into());
120
121 let xml = fetch_or_download(&self.version, &cache_dir, self.always_download)?
123 .replace("&version", "version")
124 .replace("&dtd-version", "dtd-version");
125
126 let doc = new_document(&xml)?;
127 let root = doc.root_element();
128
129 let map = CategoryMap::generate(&root)?;
130 let out = Path::new(&env::var("OUT_DIR")?).join(self.output_name);
131 map.write_file(&out)?;
132
133 Ok(())
134 }
135}
136
137fn fetch_or_download(
138 ver: &Version,
139 out_dir: &Path,
140 always_download: bool,
141) -> Result<String, Error> {
142 let file_name = format!("menu-spec-{}.xml", ver);
143 let path = Path::new(&out_dir).join(&file_name);
144
145 if !path.exists() || always_download {
146 let mut file = File::create(&path)?;
147 let mut handle = Easy::new();
148 handle.url(&format!("{}/{}", SPEC_URL, file_name))?;
149
150 let mut transfer = handle.transfer();
151 transfer.write_function(|data| {
152 file.write(data)
153 .expect("Unable to write received data to file");
154 Ok(data.len())
155 })?;
156 transfer.perform()?;
157 }
158
159 fs::read_to_string(&path).map_err(|e| e.into())
160}