Skip to content

Commit

Permalink
Moved fq_std from bevy_reflect_derive to bevy_macro_utils (bevy…
Browse files Browse the repository at this point in the history
…engine#9956)

# Objective

- Fixes bevyengine#9363

## Solution

Moved `fq_std` from `bevy_reflect_derive` to `bevy_macro_utils`. This
does make the `FQ*` types public where they were previously private,
which is a change to the public-facing API, but I don't believe a
breaking one. Additionally, I've done a basic QA pass over the
`bevy_macro_utils` crate, adding `deny(unsafe)`, `warn(missing_docs)`,
and documentation where required.
  • Loading branch information
bushrat011899 authored and Ray Redondo committed Jan 9, 2024
1 parent 25964bd commit ee3001b
Show file tree
Hide file tree
Showing 18 changed files with 384 additions and 337 deletions.
1 change: 1 addition & 0 deletions crates/bevy_macro_utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ toml_edit = "0.19"
syn = "2.0"
quote = "1.0"
rustc-hash = "1.0"
proc-macro2 = "1.0"
2 changes: 2 additions & 0 deletions crates/bevy_macro_utils/src/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use syn::{Expr, ExprLit, Lit};

use crate::symbol::Symbol;

/// Get a [literal string](struct@syn::LitStr) from the provided [expression](Expr).
pub fn get_lit_str(attr_name: Symbol, value: &Expr) -> syn::Result<&syn::LitStr> {
if let Expr::Lit(ExprLit {
lit: Lit::Str(lit), ..
Expand All @@ -16,6 +17,7 @@ pub fn get_lit_str(attr_name: Symbol, value: &Expr) -> syn::Result<&syn::LitStr>
}
}

/// Get a [literal boolean](struct@syn::LitBool) from the provided [expression](Expr) as a [`bool`].
pub fn get_lit_bool(attr_name: Symbol, value: &Expr) -> syn::Result<bool> {
if let Expr::Lit(ExprLit {
lit: Lit::Bool(lit),
Expand Down
126 changes: 126 additions & 0 deletions crates/bevy_macro_utils/src/bevy_manifest.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
extern crate proc_macro;

use proc_macro::TokenStream;
use std::{env, path::PathBuf};
use toml_edit::{Document, Item};

/// The path to the `Cargo.toml` file for the Bevy project.
pub struct BevyManifest {
manifest: Document,
}

impl Default for BevyManifest {
fn default() -> Self {
Self {
manifest: env::var_os("CARGO_MANIFEST_DIR")
.map(PathBuf::from)
.map(|mut path| {
path.push("Cargo.toml");
if !path.exists() {
panic!(
"No Cargo manifest found for crate. Expected: {}",
path.display()
);
}
let manifest = std::fs::read_to_string(path.clone()).unwrap_or_else(|_| {
panic!("Unable to read cargo manifest: {}", path.display())
});
manifest.parse::<Document>().unwrap_or_else(|_| {
panic!("Failed to parse cargo manifest: {}", path.display())
})
})
.expect("CARGO_MANIFEST_DIR is not defined."),
}
}
}
const BEVY: &str = "bevy";
const BEVY_INTERNAL: &str = "bevy_internal";

impl BevyManifest {
/// Attempt to retrieve the [path](syn::Path) of a particular package in
/// the [manifest](BevyManifest) by [name](str).
pub fn maybe_get_path(&self, name: &str) -> Option<syn::Path> {
fn dep_package(dep: &Item) -> Option<&str> {
if dep.as_str().is_some() {
None
} else {
dep.get("package").map(|name| name.as_str().unwrap())
}
}

let find_in_deps = |deps: &Item| -> Option<syn::Path> {
let package = if let Some(dep) = deps.get(name) {
return Some(Self::parse_str(dep_package(dep).unwrap_or(name)));
} else if let Some(dep) = deps.get(BEVY) {
dep_package(dep).unwrap_or(BEVY)
} else if let Some(dep) = deps.get(BEVY_INTERNAL) {
dep_package(dep).unwrap_or(BEVY_INTERNAL)
} else {
return None;
};

let mut path = Self::parse_str::<syn::Path>(package);
if let Some(module) = name.strip_prefix("bevy_") {
path.segments.push(Self::parse_str(module));
}
Some(path)
};

let deps = self.manifest.get("dependencies");
let deps_dev = self.manifest.get("dev-dependencies");

deps.and_then(find_in_deps)
.or_else(|| deps_dev.and_then(find_in_deps))
}

/// Returns the path for the crate with the given name.
///
/// This is a convenience method for constructing a [manifest] and
/// calling the [`get_path`] method.
///
/// This method should only be used where you just need the path and can't
/// cache the [manifest]. If caching is possible, it's recommended to create
/// the [manifest] yourself and use the [`get_path`] method.
///
/// [`get_path`]: Self::get_path
/// [manifest]: Self
pub fn get_path_direct(name: &str) -> syn::Path {
Self::default().get_path(name)
}

/// Returns the path for the crate with the given name.
pub fn get_path(&self, name: &str) -> syn::Path {
self.maybe_get_path(name)
.unwrap_or_else(|| Self::parse_str(name))
}

/// Attempt to parse the provided [path](str) as a [syntax tree node](syn::parse::Parse)
pub fn try_parse_str<T: syn::parse::Parse>(path: &str) -> Option<T> {
syn::parse(path.parse::<TokenStream>().ok()?).ok()
}

/// Attempt to parse provided [path](str) as a [syntax tree node](syn::parse::Parse).
///
/// # Panics
///
/// Will panic if the path is not able to be parsed. For a non-panicing option, see [`try_parse_str`]
///
/// [`try_parse_str`]: Self::try_parse_str
pub fn parse_str<T: syn::parse::Parse>(path: &str) -> T {
Self::try_parse_str(path).unwrap()
}

/// Attempt to get a subcrate [path](syn::Path) under Bevy by [name](str)
pub fn get_subcrate(&self, subcrate: &str) -> Option<syn::Path> {
self.maybe_get_path(BEVY)
.map(|bevy_path| {
let mut segments = bevy_path.segments;
segments.push(BevyManifest::parse_str(subcrate));
syn::Path {
leading_colon: None,
segments,
}
})
.or_else(|| self.maybe_get_path(&format!("bevy_{subcrate}")))
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! This module contains unit structs that should be used inside `quote!` and `spanned_quote!` using the variable interpolation syntax in place of their equivalent structs and traits present in `std`.
//
//! To create hygienic proc macros, all the names must be its fully qualified form. These unit structs help us to not specify the fully qualified name every single time.
//! This module contains unit structs that should be used inside `quote!` and `spanned_quote!`
//! using the variable interpolation syntax in place of their equivalent structs and traits
//! present in `std`.
//!
//! To create hygienic proc macros, all the names must be its fully qualified form. These
//! unit structs help us to not specify the fully qualified name every single time.
//!
//! # Example
//! Instead of writing this:
Expand Down Expand Up @@ -33,14 +36,22 @@
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};

pub(crate) struct FQAny;
pub(crate) struct FQBox;
pub(crate) struct FQClone;
pub(crate) struct FQDefault;
pub(crate) struct FQOption;
pub(crate) struct FQResult;
pub(crate) struct FQSend;
pub(crate) struct FQSync;
/// Fully Qualified (FQ) short name for [`::core::any::Any`]
pub struct FQAny;
/// Fully Qualified (FQ) short name for [`::std::boxed::Box`]
pub struct FQBox;
/// Fully Qualified (FQ) short name for [`::core::clone::Clone`]
pub struct FQClone;
/// Fully Qualified (FQ) short name for [`::core::default::Default`]
pub struct FQDefault;
/// Fully Qualified (FQ) short name for [`::core::option::Option`]
pub struct FQOption;
/// Fully Qualified (FQ) short name for [`::core::result::Result`]
pub struct FQResult;
/// Fully Qualified (FQ) short name for [`::core::marker::Send`]
pub struct FQSend;
/// Fully Qualified (FQ) short name for [`::core::marker::Sync`]
pub struct FQSync;

impl ToTokens for FQAny {
fn to_tokens(&self, tokens: &mut TokenStream) {
Expand Down
Loading

0 comments on commit ee3001b

Please sign in to comment.