pavex_reflection/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
/// A set of coordinates to identify a precise spot in a source file.
///
/// # Implementation Notes
///
/// `Location` is an owned version of [`std::panic::Location`].
/// You can build a `Location` instance starting from a [`std::panic::Location`]:
///
/// ```rust
/// use pavex_reflection::Location;
///
/// let location: Location = std::panic::Location::caller().into();
/// ```
pub struct Location {
/// The line number.
///
/// Lines are 1-indexed (i.e. the first line is numbered as 1, not 0).
pub line: u32,
/// The column number.
///
/// Columns are 1-indexed (i.e. the first column is numbered as 1, not 0).
pub column: u32,
/// The name of the source file.
///
/// Check out [`std::panic::Location::file`] for more details.
pub file: String,
}
impl<'a> From<&'a std::panic::Location<'a>> for Location {
fn from(l: &'a std::panic::Location<'a>) -> Self {
Self {
line: l.line(),
column: l.column(),
file: l.file().into(),
}
}
}
impl Location {
#[track_caller]
pub fn caller() -> Self {
std::panic::Location::caller().into()
}
}
#[derive(Debug, Hash, Eq, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
/// All the information required to identify a component registered against a [`Blueprint`].
///
/// It is an implementation detail of the builder.
///
/// [`Blueprint`]: crate::blueprint::Blueprint
pub struct RawIdentifiers {
/// Information on the callsite where the component was registered with the [`Blueprint`].
pub registered_at: RegisteredAt,
/// An unambiguous path to the type/callable.
pub import_path: String,
}
#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
/// Information on the callsite where the component was registered.
pub struct RegisteredAt {
/// The name of the crate that registered the component against the blueprint builder,
/// as it appears in the `package` section of its `Cargo.toml`.
/// In particular,
/// it has *not* been normalised—e.g. hyphens are not replaced with underscores.
///
/// This information is needed to resolve the import path unambiguously.
///
/// E.g. `my_crate::module_1::type_2`—which crate is `my_crate`?
/// This is not obvious due to the possibility of [renaming dependencies in `Cargo.toml`](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html?highlight=rename,depende#renaming-dependencies-in-cargotoml):
///
/// ```toml
/// [package]
/// name = "mypackage"
/// version = "0.0.1"
///
/// [dependencies]
/// my_crate = { version = "0.1", registry = "custom", package = "their_crate" }
/// ```
pub crate_name: String,
/// The path to the module where the component was registered, obtained via [`module_path!`].
pub module_path: String,
}
impl RawIdentifiers {
#[track_caller]
pub fn from_raw_parts(import_path: String, registered_at: RegisteredAt) -> Self {
Self {
registered_at,
import_path,
}
}
/// Return an unambiguous fully-qualified path pointing at the callable.
///
/// The returned path can be used to import the callable.
pub fn fully_qualified_path(&self) -> Vec<String> {
let mut segments: Vec<_> = self
.import_path
.split("::")
.map(|s| s.trim())
.map(ToOwned::to_owned)
.collect();
// Replace the relative portion of the path (`crate`) with the actual crate name.
if segments[0] == "crate" {
// Hyphens are allowed in crate names, but the Rust compiler doesn't
// allow them in actual import paths.
// They are "transparently" replaced with underscores.
segments[0] = self.registered_at.crate_name.replace('-', "_");
segments
} else if segments[0] == "self" {
// The path is relative to the current module.
// We "rebase" it to get an absolute path.
let mut new_segments: Vec<_> = self
.registered_at
.module_path
.split("::")
.map(|s| s.trim())
.map(ToOwned::to_owned)
.collect();
new_segments.extend(segments.into_iter().skip(1));
new_segments
} else if segments[0] == "super" {
let n_super: usize = {
let mut n_super = 0;
let iter = segments.iter();
for p in iter {
if p == "super" {
n_super += 1;
} else {
break;
}
}
n_super
};
// The path is relative to the current module.
// We "rebase" it to get an absolute path.
let module_segments: Vec<_> = self
.registered_at
.module_path
.split("::")
.map(|s| s.trim())
.map(ToOwned::to_owned)
.collect();
let n_module_segments = module_segments.len();
module_segments
.into_iter()
.take(n_module_segments - n_super)
.chain(segments.into_iter().skip(n_super))
.collect()
} else {
segments
}
}
/// The path provided by the user, unaltered.
pub fn raw_path(&self) -> &str {
&self.import_path
}
pub fn registered_at(&self) -> &RegisteredAt {
&self.registered_at
}
}