context.rs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. use std::collections::BTreeMap;
  2. use std::path::{Path, PathBuf};
  3. use syn::parse::{Error as ParseError, Result as ParseResult};
  4. /// Crate parse context
  5. ///
  6. /// Keeps track of modules defined within a crate.
  7. pub struct CrateContext {
  8. modules: BTreeMap<String, ParsedModule>,
  9. }
  10. impl CrateContext {
  11. pub fn consts(&self) -> impl Iterator<Item = &syn::ItemConst> {
  12. self.modules.iter().flat_map(|(_, ctx)| ctx.consts())
  13. }
  14. pub fn structs(&self) -> impl Iterator<Item = &syn::ItemStruct> {
  15. self.modules.iter().flat_map(|(_, ctx)| ctx.structs())
  16. }
  17. pub fn enums(&self) -> impl Iterator<Item = &syn::ItemEnum> {
  18. self.modules.iter().flat_map(|(_, ctx)| ctx.enums())
  19. }
  20. pub fn modules(&self) -> impl Iterator<Item = ModuleContext> {
  21. self.modules
  22. .iter()
  23. .map(move |(_, detail)| ModuleContext { detail })
  24. }
  25. pub fn root_module(&self) -> ModuleContext {
  26. ModuleContext {
  27. detail: self.modules.get("crate").unwrap(),
  28. }
  29. }
  30. pub fn parse(root: impl AsRef<Path>) -> Result<Self, anyhow::Error> {
  31. Ok(CrateContext {
  32. modules: ParsedModule::parse_recursive(root.as_ref())?,
  33. })
  34. }
  35. }
  36. /// Module parse context
  37. ///
  38. /// Keeps track of items defined within a module.
  39. #[derive(Copy, Clone)]
  40. pub struct ModuleContext<'krate> {
  41. detail: &'krate ParsedModule,
  42. }
  43. impl<'krate> ModuleContext<'krate> {
  44. pub fn items(&self) -> impl Iterator<Item = &syn::Item> {
  45. self.detail.items.iter()
  46. }
  47. }
  48. struct ParsedModule {
  49. name: String,
  50. file: PathBuf,
  51. path: String,
  52. items: Vec<syn::Item>,
  53. }
  54. impl ParsedModule {
  55. fn parse_recursive(root: &Path) -> Result<BTreeMap<String, ParsedModule>, anyhow::Error> {
  56. let mut modules = BTreeMap::new();
  57. let root_content = std::fs::read_to_string(root)?;
  58. let root_file = syn::parse_file(&root_content)?;
  59. let root_mod = Self::new(
  60. String::new(),
  61. root.to_owned(),
  62. "crate".to_owned(),
  63. root_file.items,
  64. );
  65. struct UnparsedModule {
  66. file: PathBuf,
  67. path: String,
  68. name: String,
  69. item: syn::ItemMod,
  70. }
  71. let mut unparsed = root_mod
  72. .submodules()
  73. .map(|item| UnparsedModule {
  74. file: root_mod.file.clone(),
  75. path: root_mod.path.clone(),
  76. name: item.ident.to_string(),
  77. item: item.clone(),
  78. })
  79. .collect::<Vec<_>>();
  80. while let Some(to_parse) = unparsed.pop() {
  81. let path = format!("{}::{}", to_parse.path, to_parse.name);
  82. let name = to_parse.name;
  83. let module = Self::from_item_mod(&to_parse.file, &path, to_parse.item)?;
  84. unparsed.extend(module.submodules().map(|item| UnparsedModule {
  85. item: item.clone(),
  86. file: module.file.clone(),
  87. path: module.path.clone(),
  88. name: item.ident.to_string(),
  89. }));
  90. modules.insert(name.clone(), module);
  91. }
  92. modules.insert(root_mod.name.clone(), root_mod);
  93. Ok(modules)
  94. }
  95. fn from_item_mod(
  96. parent_file: &Path,
  97. parent_path: &str,
  98. item: syn::ItemMod,
  99. ) -> ParseResult<Self> {
  100. Ok(match item.content {
  101. Some((_, items)) => {
  102. // The module content is within the parent file being parsed
  103. Self::new(
  104. parent_path.to_owned(),
  105. parent_file.to_owned(),
  106. item.ident.to_string(),
  107. items,
  108. )
  109. }
  110. None => {
  111. // The module is referencing some other file, so we need to load that
  112. // to parse the items it has.
  113. let parent_dir = parent_file.parent().unwrap();
  114. let parent_filename = parent_file.file_stem().unwrap().to_str().unwrap();
  115. let parent_mod_dir = parent_dir.join(parent_filename);
  116. let possible_file_paths = vec![
  117. parent_dir.join(format!("{}.rs", item.ident)),
  118. parent_dir.join(format!("{}/mod.rs", item.ident)),
  119. parent_mod_dir.join(format!("{}.rs", item.ident)),
  120. parent_mod_dir.join(format!("{}/mod.rs", item.ident)),
  121. ];
  122. let mod_file_path = possible_file_paths
  123. .into_iter()
  124. .find(|p| p.exists())
  125. .ok_or_else(|| ParseError::new_spanned(&item, "could not find file"))?;
  126. let mod_file_content = std::fs::read_to_string(&mod_file_path)
  127. .map_err(|_| ParseError::new_spanned(&item, "could not read file"))?;
  128. let mod_file = syn::parse_file(&mod_file_content)?;
  129. Self::new(
  130. parent_path.to_owned(),
  131. mod_file_path,
  132. item.ident.to_string(),
  133. mod_file.items,
  134. )
  135. }
  136. })
  137. }
  138. fn new(path: String, file: PathBuf, name: String, items: Vec<syn::Item>) -> Self {
  139. Self {
  140. name,
  141. file,
  142. path,
  143. items,
  144. }
  145. }
  146. fn submodules(&self) -> impl Iterator<Item = &syn::ItemMod> {
  147. self.items.iter().filter_map(|i| match i {
  148. syn::Item::Mod(item) => Some(item),
  149. _ => None,
  150. })
  151. }
  152. fn structs(&self) -> impl Iterator<Item = &syn::ItemStruct> {
  153. self.items.iter().filter_map(|i| match i {
  154. syn::Item::Struct(item) => Some(item),
  155. _ => None,
  156. })
  157. }
  158. fn enums(&self) -> impl Iterator<Item = &syn::ItemEnum> {
  159. self.items.iter().filter_map(|i| match i {
  160. syn::Item::Enum(item) => Some(item),
  161. _ => None,
  162. })
  163. }
  164. fn consts(&self) -> impl Iterator<Item = &syn::ItemConst> {
  165. self.items.iter().filter_map(|i| match i {
  166. syn::Item::Const(item) => Some(item),
  167. _ => None,
  168. })
  169. }
  170. }