gravitron_macro_utils/
lib.rs

1extern crate proc_macro;
2
3use std::{env, path::PathBuf};
4
5use proc_macro::TokenStream;
6use toml_edit::{DocumentMut, Item};
7
8pub struct Manifest {
9  doc: DocumentMut,
10}
11
12impl Default for Manifest {
13  fn default() -> Self {
14    Self {
15      doc: env::var_os("CARGO_MANIFEST_DIR")
16        .map(PathBuf::from)
17        .map(|mut path| {
18          path.push("Cargo.toml");
19          if !path.exists() {
20            panic!("No Cargo.toml found. Expected: {}", path.display());
21          }
22          let manifest = std::fs::read_to_string(path.clone())
23            .unwrap_or_else(|_| panic!("Unable to read Cargo.toml: {}", path.display()));
24          manifest
25            .parse::<DocumentMut>()
26            .unwrap_or_else(|_| panic!("Failed to parse Cargo.toml: {}", path.display()))
27        })
28        .expect("CARGO_MANIFEST_DIR not defined."),
29    }
30  }
31}
32
33const GRAVITRON: &str = "gravitron";
34
35impl Manifest {
36  pub fn get_path(&self, name: &str) -> syn::Path {
37    self.try_get_path(name).unwrap_or_else(|| parse_str(name))
38  }
39
40  pub fn try_get_path(&self, name: &str) -> Option<syn::Path> {
41    fn dep_package(dep: &Item) -> Option<&str> {
42      if dep.as_str().is_some() {
43        None
44      } else {
45        dep.get("package").map(|name| name.as_str().unwrap())
46      }
47    }
48
49    let find = |d: &Item| {
50      let dep = if let Some(dep) = d.get(name) {
51        return Some(parse_str(dep_package(dep).unwrap_or(name)));
52      } else if let Some(dep) = d.get(GRAVITRON) {
53        dep_package(dep).unwrap_or(GRAVITRON)
54      } else {
55        return None;
56      };
57
58      let mut path = parse_str::<syn::Path>(dep);
59      if let Some(module) = name.strip_prefix("gravitron_") {
60        path.segments.push(parse_str(module));
61      }
62      Some(path)
63    };
64
65    let dependencies = self.doc.get("dependencies");
66    let dev_dependencies = self.doc.get("dev-dependencies");
67
68    dependencies
69      .and_then(find)
70      .or_else(|| dev_dependencies.and_then(find))
71  }
72}
73
74fn try_parse_str<T: syn::parse::Parse>(path: &str) -> Option<T> {
75  syn::parse(path.parse::<TokenStream>().ok()?).ok()
76}
77
78fn parse_str<T: syn::parse::Parse>(path: &str) -> T {
79  try_parse_str(path).unwrap()
80}