badness_parser/semantic/
completion.rs1use std::collections::HashMap;
4use std::sync::LazyLock;
5
6use serde::Deserialize;
7
8const PACKAGE_NAMES_TXT: &str = include_str!("../../data/package_names.txt");
12const CLASS_NAMES_TXT: &str = include_str!("../../data/class_names.txt");
13
14static PACKAGE_NAMES: LazyLock<Vec<&'static str>> =
15 LazyLock::new(|| parse_name_list(PACKAGE_NAMES_TXT));
16static CLASS_NAMES: LazyLock<Vec<&'static str>> =
17 LazyLock::new(|| parse_name_list(CLASS_NAMES_TXT));
18
19fn parse_name_list(text: &'static str) -> Vec<&'static str> {
20 text.lines()
21 .filter(|line| !line.is_empty() && !line.starts_with('#') && *line != "---")
22 .collect()
23}
24
25pub fn package_names() -> &'static [&'static str] {
27 &PACKAGE_NAMES
28}
29
30pub fn class_names() -> &'static [&'static str] {
32 &CLASS_NAMES
33}
34
35const COLORS_JSON: &str = include_str!("../../data/colors.json");
36const TIKZ_LIBRARIES_JSON: &str = include_str!("../../data/tikz_libraries.json");
37const ARG_ENUMS_JSON: &str = include_str!("../../data/arg_enums.json");
38
39#[derive(Deserialize)]
40struct ColorsData {
41 names: Vec<String>,
42 models: Vec<String>,
43}
44
45#[derive(Deserialize)]
46struct TikzLibrariesData {
47 tikz: Vec<String>,
48 pgf: Vec<String>,
49}
50
51static COLORS: LazyLock<ColorsData> = LazyLock::new(|| {
52 serde_json::from_str(COLORS_JSON).expect("bundled data/colors.json must be valid")
53});
54static TIKZ_LIBRARIES: LazyLock<TikzLibrariesData> = LazyLock::new(|| {
55 serde_json::from_str(TIKZ_LIBRARIES_JSON)
56 .expect("bundled data/tikz_libraries.json must be valid")
57});
58static ARG_ENUMS: LazyLock<HashMap<String, HashMap<usize, Vec<String>>>> = LazyLock::new(|| {
59 serde_json::from_str(ARG_ENUMS_JSON).expect("bundled data/arg_enums.json must be valid")
60});
61
62fn as_static_slice(names: &'static [String]) -> Vec<&'static str> {
63 names.iter().map(String::as_str).collect()
64}
65
66pub fn color_names() -> &'static [&'static str] {
68 static NAMES: LazyLock<Vec<&'static str>> = LazyLock::new(|| as_static_slice(&COLORS.names));
69 &NAMES
70}
71
72pub fn color_models() -> &'static [&'static str] {
74 static MODELS: LazyLock<Vec<&'static str>> = LazyLock::new(|| as_static_slice(&COLORS.models));
75 &MODELS
76}
77
78pub fn tikz_libraries() -> &'static [&'static str] {
80 static LIBS: LazyLock<Vec<&'static str>> =
81 LazyLock::new(|| as_static_slice(&TIKZ_LIBRARIES.tikz));
82 &LIBS
83}
84
85pub fn pgf_libraries() -> &'static [&'static str] {
87 static LIBS: LazyLock<Vec<&'static str>> =
88 LazyLock::new(|| as_static_slice(&TIKZ_LIBRARIES.pgf));
89 &LIBS
90}
91
92pub fn arg_enum_values(name: &str, index: usize) -> Option<&'static [String]> {
94 ARG_ENUMS.get(name)?.get(&index).map(Vec::as_slice)
95}
96
97type PackageMetaMap = phf::Map<&'static str, PackageMeta>;
98
99#[derive(Debug, Clone, Copy)]
101pub struct PackageMeta {
102 pub desc: Option<&'static str>,
103 pub ctan: Option<&'static str>,
104}
105
106impl PackageMeta {
107 pub fn ctan_url(&self) -> Option<String> {
109 self.ctan.map(|id| format!("https://ctan.org/pkg/{id}"))
110 }
111}
112
113const fn meta(desc: Option<&'static str>, ctan: Option<&'static str>) -> PackageMeta {
114 PackageMeta { desc, ctan }
115}
116
117include!(concat!(env!("OUT_DIR"), "/package_metadata.rs"));
118
119pub fn package_metadata(name: &str) -> Option<&'static PackageMeta> {
121 PACKAGE_METADATA.get(name)
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn arg_enums_load_and_resolve() {
130 assert_eq!(
131 arg_enum_values("pagenumbering", 0),
132 Some(
133 ["arabic", "roman", "Roman", "alph", "Alph"]
134 .map(String::from)
135 .as_slice()
136 )
137 );
138 assert!(arg_enum_values("pagestyle", 0).is_some());
139 assert!(arg_enum_values("pagestyle", 1).is_none());
140 assert!(arg_enum_values("definitelynotacommand", 0).is_none());
141 }
142
143 #[test]
144 fn package_metadata_resolves_stem_to_ctan_facts() {
145 let meta = package_metadata("amsmath").expect("amsmath in metadata DB");
146 assert_eq!(meta.desc, Some("AMS mathematical facilities for LaTeX"));
147 assert_eq!(
148 meta.ctan_url().as_deref(),
149 Some("https://ctan.org/pkg/latex-amsmath")
150 );
151 assert!(package_metadata("definitely-not-a-real-package").is_none());
152 }
153}