mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
554 lines
19 KiB
Rust
554 lines
19 KiB
Rust
/*
|
|
Things this doesn't currently support that it might need to later:
|
|
|
|
- Import parsing is unfinished and so is currently disabled
|
|
- When import parsing is enabled:
|
|
- Import renames (use a::b as c) - these are silently ignored
|
|
- Imports that start with two colons (use ::a::b) - these are also silently ignored
|
|
*/
|
|
|
|
use std::{collections::HashMap, fmt::Debug, fs, path::PathBuf};
|
|
|
|
use cargo_metadata::MetadataCommand;
|
|
use log::{debug, warn};
|
|
use syn::{Attribute, Ident, ItemEnum, ItemStruct, UseTree};
|
|
|
|
use crate::markers;
|
|
|
|
/// Represents a crate, including a map of its modules, imports, structs and
|
|
/// enums.
|
|
#[derive(Debug, Clone)]
|
|
pub struct Crate {
|
|
pub name: String,
|
|
pub manifest_path: PathBuf,
|
|
pub root_src_file: PathBuf,
|
|
pub root_module: Module,
|
|
}
|
|
|
|
impl Crate {
|
|
pub fn new(manifest_path: &str) -> Self {
|
|
let mut cmd = MetadataCommand::new();
|
|
cmd.manifest_path(&manifest_path);
|
|
|
|
let metadata = cmd.exec().unwrap();
|
|
|
|
let root_package = metadata.root_package().unwrap();
|
|
let root_src_file = {
|
|
let lib_file = root_package
|
|
.manifest_path
|
|
.parent()
|
|
.unwrap()
|
|
.join("src/lib.rs");
|
|
let main_file = root_package
|
|
.manifest_path
|
|
.parent()
|
|
.unwrap()
|
|
.join("src/main.rs");
|
|
|
|
if lib_file.exists() {
|
|
fs::canonicalize(lib_file).unwrap()
|
|
} else if main_file.exists() {
|
|
fs::canonicalize(main_file).unwrap()
|
|
} else {
|
|
panic!("No src/lib.rs or src/main.rs found for this Cargo.toml file");
|
|
}
|
|
};
|
|
|
|
let source_rust_content = fs::read_to_string(&root_src_file).unwrap();
|
|
let file_ast = syn::parse_file(&source_rust_content).unwrap();
|
|
|
|
let mut result = Crate {
|
|
name: root_package.name.clone(),
|
|
manifest_path: fs::canonicalize(manifest_path).unwrap(),
|
|
root_src_file: root_src_file.clone(),
|
|
root_module: Module {
|
|
visibility: Visibility::Public,
|
|
file_path: root_src_file,
|
|
module_path: vec!["crate".to_string()],
|
|
source: Some(ModuleSource::File(file_ast)),
|
|
scope: None,
|
|
},
|
|
};
|
|
|
|
result.resolve();
|
|
|
|
result
|
|
}
|
|
|
|
/// Create a map of the modules for this crate
|
|
pub fn resolve(&mut self) {
|
|
self.root_module.resolve();
|
|
}
|
|
}
|
|
|
|
/// Mirrors syn::Visibility, but can be created without a token
|
|
#[derive(Debug, Clone)]
|
|
pub enum Visibility {
|
|
Public,
|
|
Crate,
|
|
Restricted, // Not supported
|
|
Inherited, // Usually means private
|
|
}
|
|
|
|
fn syn_vis_to_visibility(vis: &syn::Visibility) -> Visibility {
|
|
match vis {
|
|
syn::Visibility::Public(_) => Visibility::Public,
|
|
syn::Visibility::Crate(_) => Visibility::Crate,
|
|
syn::Visibility::Restricted(_) => Visibility::Restricted,
|
|
syn::Visibility::Inherited => Visibility::Inherited,
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Import {
|
|
pub path: Vec<String>,
|
|
pub visibility: Visibility,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum ModuleSource {
|
|
File(syn::File),
|
|
ModuleInFile(Vec<syn::Item>),
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct Struct {
|
|
pub ident: Ident,
|
|
pub src: ItemStruct,
|
|
pub visibility: Visibility,
|
|
pub path: Vec<String>,
|
|
pub mirror: bool,
|
|
}
|
|
|
|
impl Debug for Struct {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("Struct")
|
|
.field("ident", &self.ident)
|
|
.field("src", &"omitted")
|
|
.field("visibility", &self.visibility)
|
|
.field("path", &self.path)
|
|
.field("mirror", &self.mirror)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct Enum {
|
|
pub ident: Ident,
|
|
pub src: ItemEnum,
|
|
pub visibility: Visibility,
|
|
pub path: Vec<String>,
|
|
pub mirror: bool,
|
|
}
|
|
|
|
impl Debug for Enum {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("Enum")
|
|
.field("ident", &self.ident)
|
|
.field("src", &"omitted")
|
|
.field("visibility", &self.visibility)
|
|
.field("path", &self.path)
|
|
.field("mirror", &self.mirror)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct ModuleScope {
|
|
pub modules: Vec<Module>,
|
|
pub enums: Vec<Enum>,
|
|
pub structs: Vec<Struct>,
|
|
pub imports: Vec<Import>,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct Module {
|
|
pub visibility: Visibility,
|
|
pub file_path: PathBuf,
|
|
pub module_path: Vec<String>,
|
|
pub source: Option<ModuleSource>,
|
|
pub scope: Option<ModuleScope>,
|
|
}
|
|
|
|
impl Debug for Module {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("Module")
|
|
.field("visibility", &self.visibility)
|
|
.field("module_path", &self.module_path)
|
|
.field("file_path", &self.file_path)
|
|
.field("source", &"omitted")
|
|
.field("scope", &self.scope)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
/// Get a struct or enum ident, possibly remapped by a mirror marker
|
|
fn get_ident(ident: &Ident, attrs: &[Attribute]) -> (Ident, bool) {
|
|
markers::extract_mirror_marker(attrs)
|
|
.and_then(|path| path.get_ident().map(|ident| (ident.clone(), true)))
|
|
.unwrap_or_else(|| (ident.clone(), false))
|
|
}
|
|
|
|
impl Module {
|
|
pub fn resolve(&mut self) {
|
|
self.resolve_modules();
|
|
// self.resolve_imports();
|
|
}
|
|
|
|
/// Maps out modules, structs and enums within the scope of this module
|
|
fn resolve_modules(&mut self) {
|
|
let mut scope_modules = Vec::new();
|
|
let mut scope_structs = Vec::new();
|
|
let mut scope_enums = Vec::new();
|
|
|
|
let items = match self.source.as_ref().unwrap() {
|
|
ModuleSource::File(file) => &file.items,
|
|
ModuleSource::ModuleInFile(items) => items,
|
|
};
|
|
|
|
for item in items.iter() {
|
|
match item {
|
|
syn::Item::Struct(item_struct) => {
|
|
let (ident, mirror) = get_ident(&item_struct.ident, &item_struct.attrs);
|
|
let ident_str = ident.to_string();
|
|
scope_structs.push(Struct {
|
|
ident,
|
|
src: item_struct.clone(),
|
|
visibility: syn_vis_to_visibility(&item_struct.vis),
|
|
path: {
|
|
let mut path = self.module_path.clone();
|
|
path.push(ident_str);
|
|
path
|
|
},
|
|
mirror,
|
|
});
|
|
}
|
|
syn::Item::Enum(item_enum) => {
|
|
let (ident, mirror) = get_ident(&item_enum.ident, &item_enum.attrs);
|
|
let ident_str = ident.to_string();
|
|
scope_enums.push(Enum {
|
|
ident,
|
|
src: item_enum.clone(),
|
|
visibility: syn_vis_to_visibility(&item_enum.vis),
|
|
path: {
|
|
let mut path = self.module_path.clone();
|
|
path.push(ident_str);
|
|
path
|
|
},
|
|
mirror,
|
|
});
|
|
}
|
|
syn::Item::Mod(item_mod) => {
|
|
let ident = item_mod.ident.clone();
|
|
|
|
let mut module_path = self.module_path.clone();
|
|
module_path.push(ident.to_string());
|
|
|
|
scope_modules.push(match &item_mod.content {
|
|
Some(content) => {
|
|
let mut child_module = Module {
|
|
visibility: syn_vis_to_visibility(&item_mod.vis),
|
|
file_path: self.file_path.clone(),
|
|
module_path,
|
|
source: Some(ModuleSource::ModuleInFile(content.1.clone())),
|
|
scope: None,
|
|
};
|
|
|
|
child_module.resolve();
|
|
|
|
child_module
|
|
}
|
|
None => {
|
|
let folder_path =
|
|
self.file_path.parent().unwrap().join(ident.to_string());
|
|
let folder_exists = folder_path.exists();
|
|
|
|
let file_path = if folder_exists {
|
|
folder_path.join("mod.rs")
|
|
} else {
|
|
self.file_path
|
|
.parent()
|
|
.unwrap()
|
|
.join(ident.to_string() + ".rs")
|
|
};
|
|
|
|
let file_exists = file_path.exists();
|
|
|
|
if !file_exists {
|
|
warn!(
|
|
"Skipping unresolvable module {} (tried {})",
|
|
&ident,
|
|
file_path.to_string_lossy()
|
|
);
|
|
continue;
|
|
}
|
|
|
|
let source = if file_exists {
|
|
let source_rust_content = fs::read_to_string(&file_path).unwrap();
|
|
debug!("Trying to parse {:?}", file_path);
|
|
Some(ModuleSource::File(
|
|
syn::parse_file(&source_rust_content).unwrap(),
|
|
))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let mut child_module = Module {
|
|
visibility: syn_vis_to_visibility(&item_mod.vis),
|
|
file_path,
|
|
module_path,
|
|
source,
|
|
scope: None,
|
|
};
|
|
|
|
if file_exists {
|
|
child_module.resolve();
|
|
}
|
|
|
|
child_module
|
|
}
|
|
});
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
self.scope = Some(ModuleScope {
|
|
modules: scope_modules,
|
|
enums: scope_enums,
|
|
structs: scope_structs,
|
|
imports: vec![], // Will be filled in by resolve_imports()
|
|
});
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
fn resolve_imports(&mut self) {
|
|
let imports = &mut self.scope.as_mut().unwrap().imports;
|
|
|
|
let items = match self.source.as_ref().unwrap() {
|
|
ModuleSource::File(file) => &file.items,
|
|
ModuleSource::ModuleInFile(items) => items,
|
|
};
|
|
|
|
for item in items.iter() {
|
|
if let syn::Item::Use(item_use) = item {
|
|
let flattened_imports = flatten_use_tree(&item_use.tree);
|
|
|
|
for import in flattened_imports {
|
|
imports.push(Import {
|
|
path: import,
|
|
visibility: syn_vis_to_visibility(&item_use.vis),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn collect_structs<'a>(&'a self, container: &mut HashMap<String, &'a Struct>) {
|
|
let scope = self.scope.as_ref().unwrap();
|
|
for scope_struct in &scope.structs {
|
|
container.insert(scope_struct.ident.to_string(), scope_struct);
|
|
}
|
|
for scope_module in &scope.modules {
|
|
scope_module.collect_structs(container);
|
|
}
|
|
}
|
|
|
|
pub fn collect_structs_to_vec(&self) -> HashMap<String, &Struct> {
|
|
let mut ans = HashMap::new();
|
|
self.collect_structs(&mut ans);
|
|
ans
|
|
}
|
|
|
|
pub fn collect_enums<'a>(&'a self, container: &mut HashMap<String, &'a Enum>) {
|
|
let scope = self.scope.as_ref().unwrap();
|
|
for scope_enum in &scope.enums {
|
|
container.insert(scope_enum.ident.to_string(), scope_enum);
|
|
}
|
|
for scope_module in &scope.modules {
|
|
scope_module.collect_enums(container);
|
|
}
|
|
}
|
|
|
|
pub fn collect_enums_to_vec(&self) -> HashMap<String, &Enum> {
|
|
let mut ans = HashMap::new();
|
|
self.collect_enums(&mut ans);
|
|
ans
|
|
}
|
|
}
|
|
|
|
fn flatten_use_tree_rename_abort_warning(use_tree: &UseTree) {
|
|
debug!("WARNING: flatten_use_tree() found an import rename (use a::b as c). flatten_use_tree() will now abort.");
|
|
debug!("WARNING: This happened while parsing {:?}", use_tree);
|
|
debug!("WARNING: This use statement will be ignored.");
|
|
}
|
|
|
|
/// Takes a use tree and returns a flat list of use paths (list of string tokens)
|
|
///
|
|
/// Example:
|
|
/// use a::{b::c, d::e};
|
|
/// becomes
|
|
/// [
|
|
/// ["a", "b", "c"],
|
|
/// ["a", "d", "e"]
|
|
/// ]
|
|
///
|
|
/// Warning: As of writing, import renames (import a::b as c) are silently
|
|
/// ignored.
|
|
fn flatten_use_tree(use_tree: &UseTree) -> Vec<Vec<String>> {
|
|
// Vec<(path, is_complete)>
|
|
let mut result = vec![(vec![], false)];
|
|
|
|
let mut counter: usize = 0;
|
|
|
|
loop {
|
|
counter += 1;
|
|
|
|
if counter > 10000 {
|
|
panic!("flatten_use_tree: Use statement complexity limit exceeded. This is probably a bug.");
|
|
}
|
|
|
|
// If all paths are complete, break from the loop
|
|
if result.iter().all(|result_item| result_item.1) {
|
|
break;
|
|
}
|
|
|
|
let mut items_to_push = Vec::new();
|
|
|
|
for path_tuple in &mut result {
|
|
let path = &mut path_tuple.0;
|
|
let is_complete = &mut path_tuple.1;
|
|
|
|
if *is_complete {
|
|
continue;
|
|
}
|
|
|
|
let mut tree_cursor = use_tree;
|
|
|
|
for path_item in path.iter() {
|
|
match tree_cursor {
|
|
UseTree::Path(use_path) => {
|
|
let ident = use_path.ident.to_string();
|
|
if *path_item != ident {
|
|
panic!("This ident did not match the one we already collected. This is a bug.");
|
|
}
|
|
tree_cursor = use_path.tree.as_ref();
|
|
}
|
|
UseTree::Group(use_group) => {
|
|
let mut moved_tree_cursor = false;
|
|
|
|
for tree in use_group.items.iter() {
|
|
match tree {
|
|
UseTree::Path(use_path) => {
|
|
if path_item == &use_path.ident.to_string() {
|
|
tree_cursor = use_path.tree.as_ref();
|
|
moved_tree_cursor = true;
|
|
break;
|
|
}
|
|
}
|
|
// Since we're not matching UseTree::Group here, a::b::{{c}, {d}} might
|
|
// break. But also why would anybody do that
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
if !moved_tree_cursor {
|
|
unreachable!();
|
|
}
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
match tree_cursor {
|
|
UseTree::Name(use_name) => {
|
|
path.push(use_name.ident.to_string());
|
|
*is_complete = true;
|
|
}
|
|
UseTree::Path(use_path) => {
|
|
path.push(use_path.ident.to_string());
|
|
}
|
|
UseTree::Glob(_) => {
|
|
path.push("*".to_string());
|
|
*is_complete = true;
|
|
}
|
|
UseTree::Group(use_group) => {
|
|
// We'll modify the first one in-place, and make clones for
|
|
// all subsequent ones
|
|
let mut first: bool = true;
|
|
// Capture the path in this state, since we're about to
|
|
// modify it
|
|
let path_copy = path.clone();
|
|
for tree in use_group.items.iter() {
|
|
let mut new_path_tuple = if first {
|
|
None
|
|
} else {
|
|
let new_path = path_copy.clone();
|
|
items_to_push.push((new_path, false));
|
|
Some(items_to_push.iter_mut().last().unwrap())
|
|
};
|
|
|
|
match tree {
|
|
UseTree::Path(use_path) => {
|
|
let ident = use_path.ident.to_string();
|
|
|
|
if first {
|
|
path.push(ident);
|
|
} else {
|
|
new_path_tuple.unwrap().0.push(ident);
|
|
}
|
|
}
|
|
UseTree::Name(use_name) => {
|
|
let ident = use_name.ident.to_string();
|
|
|
|
if first {
|
|
path.push(ident);
|
|
*is_complete = true;
|
|
} else {
|
|
let path_tuple = new_path_tuple.as_mut().unwrap();
|
|
path_tuple.0.push(ident);
|
|
path_tuple.1 = true;
|
|
}
|
|
}
|
|
UseTree::Glob(_) => {
|
|
if first {
|
|
path.push("*".to_string());
|
|
*is_complete = true;
|
|
} else {
|
|
let path_tuple = new_path_tuple.as_mut().unwrap();
|
|
path_tuple.0.push("*".to_string());
|
|
path_tuple.1 = true;
|
|
}
|
|
}
|
|
UseTree::Group(_) => {
|
|
panic!(
|
|
"Directly-nested use groups ({}) are not supported by flutter_rust_bridge. Use {} instead.",
|
|
"use a::{{b}, c}",
|
|
"a::{b, c}"
|
|
);
|
|
}
|
|
// UseTree::Group(_) => panic!(),
|
|
UseTree::Rename(_) => {
|
|
flatten_use_tree_rename_abort_warning(use_tree);
|
|
return vec![];
|
|
}
|
|
}
|
|
|
|
first = false;
|
|
}
|
|
}
|
|
UseTree::Rename(_) => {
|
|
flatten_use_tree_rename_abort_warning(use_tree);
|
|
return vec![];
|
|
}
|
|
}
|
|
}
|
|
|
|
for item in items_to_push {
|
|
result.push(item);
|
|
}
|
|
}
|
|
|
|
result.into_iter().map(|val| val.0).collect()
|
|
}
|