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
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License in the LICENSE-APACHE file or at:
// https://www.apache.org/licenses/LICENSE-2.0
//! # Impl-tools-lib
//!
//! To implement the proc-macros, copy and modify the
//! [`impl-tools`](https://github.com/kas-gui/impl-tools/) crate, which is
//! merely documentation plus wrappers around this crate.
#![deny(missing_docs)]
// Lint advocates use of bool::then_some, stablizied in rustc 1.62.0
#![allow(clippy::unnecessary_lazy_evaluations)]
#![allow(clippy::style)]
pub mod autoimpl;
mod default;
pub mod fields;
mod for_deref;
pub mod generics;
mod scope;
pub use default::{AttrImplDefault, ImplDefault};
pub use for_deref::ForDeref;
pub use scope::{Scope, ScopeAttr, ScopeItem};
/// Simple, allocation-free path representation
#[derive(PartialEq, Eq)]
pub struct SimplePath(&'static [&'static str]);
impl SimplePath {
/// Construct, verifying validity
///
/// If the first component is an empty string, this is treated as a leading
/// colon (e.g. `["", "abc", "Def"] == `::abc::Def`). No other component may
/// be empty. At least one non-empty component is required.
///
/// Panics if requirements are not met.
pub fn new(path: &'static [&'static str]) -> Self {
let mut is_empty = false;
for (i, s) in path.iter().enumerate() {
is_empty = is_empty && s.is_empty();
if i > 0 && s.is_empty() {
panic!("empty component");
}
}
if is_empty {
panic!("empty path");
}
SimplePath(path)
}
/// True if this matches a [`syn::Path`]
///
/// This must match the path exactly, with two exceptions:
///
/// - if `path` has no leading colon but `self` does (empty first
/// component), the paths may still match
/// - if the first component of `self` is `core` or `alloc` but the first
/// component of `path` is `std`, the paths may still match
pub fn matches(&self, path: &syn::Path) -> bool {
let mut q = self.0;
assert!(!q.is_empty());
if path.leading_colon.is_some() && !q[0].is_empty() {
return false;
}
if q[0].is_empty() {
q = &q[1..];
}
if path.segments.len() != q.len() {
return false;
}
let mut first = true;
for (x, y) in path.segments.iter().zip(q.iter()) {
if !x.arguments.is_empty() {
return false;
}
#[allow(clippy::if_same_then_else)]
if x.ident == y {
} else if first && (*y == "core" || *y == "alloc") && x.ident == "std" {
} else {
return false;
}
first = false;
}
true
}
/// True if the last component matches a [`syn::Ident`](struct@syn::Ident)
pub fn matches_ident(&self, ident: &syn::Ident) -> bool {
assert!(!self.0.is_empty());
self.0.iter().last().map(|s| ident == s).unwrap_or(false)
}
/// If input `path` has a single component with no leading colon, then
/// match via [`Self::matches_ident`]; otherwise match via
/// [`Self::matches`].
pub fn matches_ident_or_path(&self, path: &syn::Path) -> bool {
if path.leading_colon.is_none() && path.segments.len() == 1 {
let seg = &path.segments[0];
seg.arguments.is_empty() && self.matches_ident(&seg.ident)
} else {
self.matches(path)
}
}
}