pub enum CodeSigningSlot {
Show 17 variants
CodeDirectory,
Info,
RequirementSet,
ResourceDir,
Application,
Entitlements,
RepSpecific,
EntitlementsDer,
AlternateCodeDirectory0,
AlternateCodeDirectory1,
AlternateCodeDirectory2,
AlternateCodeDirectory3,
AlternateCodeDirectory4,
Signature,
Identification,
Ticket,
Unknown(u32),
}Expand description
A well-known slot within code signing data.
Variants§
CodeDirectory
Info
Info.plist.
RequirementSet
Designated requirements.
ResourceDir
Digest of CodeRequirements file (used in bundles).
Application
Application specific slot.
Entitlements
Entitlements XML plist.
RepSpecific
Reserved for disk images.
EntitlementsDer
Entitlements DER encoded plist.
AlternateCodeDirectory0
Alternative code directory slot #0.
Used for expressing a code directory using an alternate digest type.
AlternateCodeDirectory1
AlternateCodeDirectory2
AlternateCodeDirectory3
AlternateCodeDirectory4
Signature
CMS signature.
Identification
Ticket
Notarization ticket.
Unknown(u32)
Implementations§
source§impl CodeSigningSlot
impl CodeSigningSlot
sourcepub fn has_external_content(&self) -> bool
pub fn has_external_content(&self) -> bool
Whether this slot has external data (as opposed to provided via a blob).
Examples found in repository?
src/verify.rs (line 526)
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
fn verify_code_directory(
macho: &MachOBinary,
signature: &EmbeddedSignature,
cd: &CodeDirectoryBlob,
context: VerificationContext,
) -> Vec<VerificationProblem> {
let mut problems = vec![];
match cd.digest_type {
DigestType::Sha256 | DigestType::Sha384 => {}
hash_type => problems.push(VerificationProblem {
context: context.clone(),
problem: VerificationProblemType::CodeDirectoryOldDigestAlgorithm(hash_type),
}),
}
match macho.code_digests(cd.digest_type, cd.page_size as _) {
Ok(digests) => {
let mut cd_iter = cd.code_digests.iter().enumerate();
let mut actual_iter = digests.iter().enumerate();
loop {
match (cd_iter.next(), actual_iter.next()) {
(None, None) => {
break;
}
(Some((cd_index, cd_digest)), Some((_, actual_digest))) => {
if &cd_digest.data != actual_digest {
problems.push(VerificationProblem {
context: context.clone(),
problem: VerificationProblemType::CodeDigestMismatch(
cd_index,
cd_digest.to_vec(),
actual_digest.clone(),
),
});
}
}
(None, Some((actual_index, actual_digest))) => {
problems.push(VerificationProblem {
context: context.clone(),
problem: VerificationProblemType::CodeDigestMissingEntry(
actual_index,
actual_digest.clone(),
),
});
}
(Some((cd_index, cd_digest)), None) => {
problems.push(VerificationProblem {
context: context.clone(),
problem: VerificationProblemType::CodeDigestExtraEntry(
cd_index,
cd_digest.to_vec(),
),
});
}
}
}
}
Err(e) => {
problems.push(VerificationProblem {
context: context.clone(),
problem: VerificationProblemType::CodeDigestError(e),
});
}
}
// All slots beneath some threshold should have a special hash.
// It isn't clear where this threshold is. But the alternate code directory and
// CMS slots appear to start at 0x1000. We set our limit at 32, which seems
// reasonable considering there are ~10 defined slots starting at value 0.
//
// The code directory doesn't have a digest because one cannot hash self.
for blob in &signature.blobs {
let slot = blob.slot;
if u32::from(slot) < 32
&& !cd.slot_digests().contains_key(&slot)
&& slot != CodeSigningSlot::CodeDirectory
{
problems.push(VerificationProblem {
context: context.clone(),
problem: VerificationProblemType::SlotDigestMissing(slot),
});
}
}
let max_slot = cd
.slot_digests()
.keys()
.map(|slot| u32::from(*slot))
.filter(|slot| *slot < 32)
.max()
.unwrap_or(0);
let null_digest = b"\0".repeat(cd.digest_size as usize);
// Verify the special/slot digests we do have match reality.
for (slot, cd_digest) in cd.slot_digests().iter() {
match signature.find_slot(*slot) {
Some(entry) => match entry.digest_with(cd.digest_type) {
Ok(actual_digest) => {
if actual_digest != cd_digest.to_vec() {
problems.push(VerificationProblem {
context: context.clone(),
problem: VerificationProblemType::SlotDigestMismatch(
*slot,
cd_digest.to_vec(),
actual_digest,
),
});
}
}
Err(e) => {
problems.push(VerificationProblem {
context: context.clone(),
problem: VerificationProblemType::SlotDigestError(e),
});
}
},
None => {
// Some slots have external provided from somewhere that isn't a blob.
if slot.has_external_content() {
// TODO need to validate this external content somewhere.
}
// But slots with a null digest (all 0s) exist as placeholders when there
// is a higher numbered slot present.
else if u32::from(*slot) >= max_slot || cd_digest.to_vec() != null_digest {
problems.push(VerificationProblem {
context: context.clone(),
problem: VerificationProblemType::ExtraSlotDigest(
*slot,
cd_digest.to_vec(),
),
});
}
}
}
}
// TODO verify code_limit[_64] is appropriate.
// TODO verify exec_seg_base is appropriate.
problems
}sourcepub fn is_alternative_code_directory(&self) -> bool
pub fn is_alternative_code_directory(&self) -> bool
Whether this slot is for holding an alternative code directory.
Examples found in repository?
src/embedded_signature_builder.rs (line 187)
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
pub fn add_alternative_code_directory(
&mut self,
cd: CodeDirectoryBlob<'a>,
) -> Result<&CodeDirectoryBlob, AppleCodesignError> {
let mut our_slot = CodeSigningSlot::AlternateCodeDirectory0;
for slot in self.blobs.keys() {
if slot.is_alternative_code_directory() {
our_slot = CodeSigningSlot::from(u32::from(*slot) + 1);
if !our_slot.is_alternative_code_directory() {
return Err(AppleCodesignError::SignatureBuilder(
"no more available alternative code directory slots",
));
}
}
}
self.add_code_directory(our_slot, cd)
}
/// The a CMS signature and register its signature blob.
///
/// `signing_key` and `signing_cert` denote the keypair being used to produce a
/// cryptographic signature.
///
/// `time_stamp_url` is an optional time-stamp protocol server to use to record
/// the signature in.
///
/// `certificates` are extra X.509 certificates to register in the signing chain.
///
/// This method errors if called before a code directory is registered.
pub fn create_cms_signature(
&mut self,
signing_key: &dyn KeyInfoSigner,
signing_cert: &CapturedX509Certificate,
time_stamp_url: Option<&Url>,
certificates: impl Iterator<Item = CapturedX509Certificate>,
) -> Result<(), AppleCodesignError> {
let main_cd = self
.code_directory()
.ok_or(AppleCodesignError::SignatureBuilder(
"cannot create CMS signature unless code directory is present",
))?;
if let Some(cn) = signing_cert.subject_common_name() {
warn!("creating cryptographic signature with certificate {}", cn);
}
let mut cdhashes = vec![];
let mut attributes = vec![];
for (slot, blob) in &self.blobs {
if *slot == CodeSigningSlot::CodeDirectory || slot.is_alternative_code_directory() {
if let BlobData::CodeDirectory(cd) = blob {
// plist digests use the native digest of the code directory but always
// truncated at 20 bytes.
let mut digest = cd.digest_with(cd.digest_type)?;
digest.truncate(20);
cdhashes.push(plist::Value::Data(digest));
// ASN.1 values are a SEQUENCE of (OID, OctetString) with the native
// digest.
let digest = cd.digest_with(cd.digest_type)?;
let alg = DigestAlgorithm::try_from(cd.digest_type)?;
attributes.push(AttributeValue::new(bcder::Captured::from_values(
bcder::Mode::Der,
bcder::encode::sequence((
Oid::from(alg).encode_ref(),
bcder::OctetString::new(digest.into()).encode_ref(),
)),
)));
} else {
return Err(AppleCodesignError::SignatureBuilder(
"unexpected blob type in code directory slot",
));
}
}
}
let mut plist_dict = plist::Dictionary::new();
plist_dict.insert("cdhashes".to_string(), plist::Value::Array(cdhashes));
let mut plist_xml = vec![];
plist::Value::from(plist_dict)
.to_writer_xml(&mut plist_xml)
.map_err(AppleCodesignError::CodeDirectoryPlist)?;
// We also need to include a trailing newline to conform with Apple's XML
// writer.
plist_xml.push(b'\n');
let signer = SignerBuilder::new(signing_key, signing_cert.clone())
.message_id_content(main_cd.to_blob_bytes()?)
.signed_attribute_octet_string(
Oid(Bytes::copy_from_slice(CD_DIGESTS_PLIST_OID.as_ref())),
&plist_xml,
);
let signer = signer.signed_attribute(Oid(CD_DIGESTS_OID.as_ref().into()), attributes);
let signer = if let Some(time_stamp_url) = time_stamp_url {
info!("Using time-stamp server {}", time_stamp_url);
signer.time_stamp_url(time_stamp_url.clone())?
} else {
signer
};
let der = SignedDataBuilder::default()
// The default is `signed-data`. But Apple appears to use the `data` content-type,
// in violation of RFC 5652 Section 5, which says `signed-data` should be
// used when there are signatures.
.content_type(Oid(OID_ID_DATA.as_ref().into()))
.signer(signer)
.certificates(certificates)
.build_der()?;
self.blobs.insert(
CodeSigningSlot::Signature,
BlobData::BlobWrapper(Box::new(BlobWrapperBlob::from_data_owned(der))),
);
self.state = BlobsState::SignatureAdded;
Ok(())
}sourcepub fn is_code_directory_specials_expressible(&self) -> bool
pub fn is_code_directory_specials_expressible(&self) -> bool
Whether this slot’s digest is expressed in code directories list of special slot digests.
Examples found in repository?
src/embedded_signature_builder.rs (line 161)
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
pub fn add_code_directory(
&mut self,
cd_slot: CodeSigningSlot,
mut cd: CodeDirectoryBlob<'a>,
) -> Result<&CodeDirectoryBlob, AppleCodesignError> {
if matches!(self.state, BlobsState::SignatureAdded) {
return Err(AppleCodesignError::SignatureBuilder(
"cannot add code directory after signature data added",
));
}
for (slot, blob) in &self.blobs {
// Not all slots are expressible in the cd specials list!
if !slot.is_code_directory_specials_expressible() {
continue;
}
let digest = blob.digest_with(cd.digest_type)?;
cd.set_slot_digest(*slot, digest)?;
}
self.blobs.insert(cd_slot, cd.into());
self.state = BlobsState::CodeDirectoryAdded;
Ok(self.code_directory().expect("we just inserted this key"))
}More examples
src/code_directory.rs (line 531)
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
fn serialize_payload(&self) -> Result<Vec<u8>, AppleCodesignError> {
let mut cursor = std::io::Cursor::new(Vec::<u8>::new());
// We need to do this in 2 phases because we don't know the length until
// we build up the data structure.
cursor.iowrite_with(self.version, scroll::BE)?;
cursor.iowrite_with(self.flags.bits, scroll::BE)?;
let digest_offset_cursor_position = cursor.position();
cursor.iowrite_with(0u32, scroll::BE)?;
let ident_offset_cursor_position = cursor.position();
cursor.iowrite_with(0u32, scroll::BE)?;
assert_eq!(cursor.position(), 0x10);
// Digest offsets and counts are wonky. The recorded digest offset is the beginning
// of code digests and special digests are in "negative" indices before
// that offset. Digests are also at the index of their CodeSigningSlot constant.
// e.g. Code Directory is the first element in the specials array because
// it is slot 0. This means we need to write out empty digests for missing
// special slots. Our local specials HashMap may not have all entries. So compute
// how many specials there should be and write that here. We'll insert placeholder
// digests later.
let highest_slot = self
.special_digests
.keys()
.map(|slot| u32::from(*slot))
.max()
.unwrap_or(0);
cursor.iowrite_with(highest_slot, scroll::BE)?;
cursor.iowrite_with(self.code_digests.len() as u32, scroll::BE)?;
cursor.iowrite_with(self.code_limit, scroll::BE)?;
cursor.iowrite_with(self.digest_size, scroll::BE)?;
cursor.iowrite_with(u8::from(self.digest_type), scroll::BE)?;
cursor.iowrite_with(self.platform, scroll::BE)?;
cursor.iowrite_with(self.page_size.trailing_zeros() as u8, scroll::BE)?;
assert_eq!(cursor.position(), 0x20);
cursor.iowrite_with(self.spare2, scroll::BE)?;
let mut scatter_offset_cursor_position = None;
let mut team_offset_cursor_position = None;
if self.version >= CodeDirectoryVersion::SupportsScatter as u32 {
scatter_offset_cursor_position = Some(cursor.position());
cursor.iowrite_with(self.scatter_offset.unwrap_or(0), scroll::BE)?;
if self.version >= CodeDirectoryVersion::SupportsTeamId as u32 {
team_offset_cursor_position = Some(cursor.position());
cursor.iowrite_with(0u32, scroll::BE)?;
if self.version >= CodeDirectoryVersion::SupportsCodeLimit64 as u32 {
cursor.iowrite_with(self.spare3.unwrap_or(0), scroll::BE)?;
assert_eq!(cursor.position(), 0x30);
cursor.iowrite_with(self.code_limit_64.unwrap_or(0), scroll::BE)?;
if self.version >= CodeDirectoryVersion::SupportsExecutableSegment as u32 {
cursor.iowrite_with(self.exec_seg_base.unwrap_or(0), scroll::BE)?;
assert_eq!(cursor.position(), 0x40);
cursor.iowrite_with(self.exec_seg_limit.unwrap_or(0), scroll::BE)?;
cursor.iowrite_with(
self.exec_seg_flags
.unwrap_or_else(ExecutableSegmentFlags::empty)
.bits,
scroll::BE,
)?;
if self.version >= CodeDirectoryVersion::SupportsRuntime as u32 {
assert_eq!(cursor.position(), 0x50);
cursor.iowrite_with(self.runtime.unwrap_or(0), scroll::BE)?;
cursor
.iowrite_with(self.pre_encrypt_offset.unwrap_or(0), scroll::BE)?;
if self.version >= CodeDirectoryVersion::SupportsLinkage as u32 {
cursor.iowrite_with(
self.linkage_hash_type.unwrap_or(0),
scroll::BE,
)?;
cursor.iowrite_with(
self.linkage_truncated.unwrap_or(0),
scroll::BE,
)?;
cursor.iowrite_with(self.spare4.unwrap_or(0), scroll::BE)?;
cursor
.iowrite_with(self.linkage_offset.unwrap_or(0), scroll::BE)?;
assert_eq!(cursor.position(), 0x60);
cursor.iowrite_with(self.linkage_size.unwrap_or(0), scroll::BE)?;
}
}
}
}
}
}
// We've written all the struct fields. Now write variable length fields.
let identity_offset = cursor.position();
cursor.write_all(self.ident.as_bytes())?;
cursor.write_all(b"\0")?;
let team_offset = cursor.position();
if team_offset_cursor_position.is_some() {
if let Some(team_name) = &self.team_name {
cursor.write_all(team_name.as_bytes())?;
cursor.write_all(b"\0")?;
}
}
// TODO consider aligning cursor on page boundary here for performance?
// The boundary conditions are a bit wonky here. We want to go from greatest
// to smallest, not writing index 0 because that's the first code digest.
for slot_index in (1..highest_slot + 1).rev() {
let slot = CodeSigningSlot::from(slot_index);
assert!(
slot.is_code_directory_specials_expressible(),
"slot is expressible in code directory special digests"
);
if let Some(digest) = self.special_digests.get(&slot) {
assert_eq!(
digest.data.len(),
self.digest_size as usize,
"special slot digest length matches expected length"
);
cursor.write_all(&digest.data)?;
} else {
cursor.write_all(&b"\0".repeat(self.digest_size as usize))?;
}
}
let code_digests_start_offset = cursor.position();
for digest in &self.code_digests {
cursor.write_all(&digest.data)?;
}
// TODO write out scatter vector.
// Now go back and update the placeholder offsets. We need to add 8 to account
// for the blob header, which isn't present in this buffer.
cursor.set_position(digest_offset_cursor_position);
cursor.iowrite_with(code_digests_start_offset as u32 + 8, scroll::BE)?;
cursor.set_position(ident_offset_cursor_position);
cursor.iowrite_with(identity_offset as u32 + 8, scroll::BE)?;
if scatter_offset_cursor_position.is_some() && self.scatter_offset.is_some() {
return Err(AppleCodesignError::Unimplemented("scatter offset"));
}
if let Some(offset) = team_offset_cursor_position {
if self.team_name.is_some() {
cursor.set_position(offset);
cursor.iowrite_with(team_offset as u32 + 8, scroll::BE)?;
}
}
Ok(cursor.into_inner())
}
}
impl<'a> CodeDirectoryBlob<'a> {
/// Obtain the mapping of slots to digests.
pub fn slot_digests(&self) -> &HashMap<CodeSigningSlot, Digest<'a>> {
&self.special_digests
}
/// Obtain the recorded digest for a given [CodeSigningSlot].
pub fn slot_digest(&self, slot: CodeSigningSlot) -> Option<&Digest<'a>> {
self.special_digests.get(&slot)
}
/// Set the digest for a given slot.
pub fn set_slot_digest(
&mut self,
slot: CodeSigningSlot,
digest: impl Into<Digest<'a>>,
) -> Result<(), AppleCodesignError> {
if !slot.is_code_directory_specials_expressible() {
return Err(AppleCodesignError::LogicError(format!(
"slot {slot:?} cannot have its digest expressed on code directories"
)));
}
let digest = digest.into();
if digest.data.len() != self.digest_size as usize {
return Err(AppleCodesignError::LogicError(format!(
"attempt to assign digest for slot {:?} whose length {} does not match code directory digest length {}",
slot, digest.data.len(), self.digest_size
)));
}
self.special_digests.insert(slot, digest);
Ok(())
}Trait Implementations§
source§impl Clone for CodeSigningSlot
impl Clone for CodeSigningSlot
source§fn clone(&self) -> CodeSigningSlot
fn clone(&self) -> CodeSigningSlot
Returns a copy of the value. Read more
1.0.0 · source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moresource§impl Debug for CodeSigningSlot
impl Debug for CodeSigningSlot
source§impl From<CodeSigningSlot> for u32
impl From<CodeSigningSlot> for u32
source§fn from(v: CodeSigningSlot) -> Self
fn from(v: CodeSigningSlot) -> Self
Converts to this type from the input type.
source§impl From<u32> for CodeSigningSlot
impl From<u32> for CodeSigningSlot
source§impl Hash for CodeSigningSlot
impl Hash for CodeSigningSlot
source§impl Ord for CodeSigningSlot
impl Ord for CodeSigningSlot
source§impl PartialEq<CodeSigningSlot> for CodeSigningSlot
impl PartialEq<CodeSigningSlot> for CodeSigningSlot
source§fn eq(&self, other: &CodeSigningSlot) -> bool
fn eq(&self, other: &CodeSigningSlot) -> bool
This method tests for
self and other values to be equal, and is used
by ==.source§impl PartialOrd<CodeSigningSlot> for CodeSigningSlot
impl PartialOrd<CodeSigningSlot> for CodeSigningSlot
1.0.0 · source§fn le(&self, other: &Rhs) -> bool
fn le(&self, other: &Rhs) -> bool
This method tests less than or equal to (for
self and other) and is used by the <=
operator. Read moreimpl Copy for CodeSigningSlot
impl Eq for CodeSigningSlot
impl StructuralEq for CodeSigningSlot
impl StructuralPartialEq for CodeSigningSlot
Auto Trait Implementations§
impl RefUnwindSafe for CodeSigningSlot
impl Send for CodeSigningSlot
impl Sync for CodeSigningSlot
impl Unpin for CodeSigningSlot
impl UnwindSafe for CodeSigningSlot
Blanket Implementations§
§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
§impl<T> Conv for T
impl<T> Conv for T
source§impl<Q, K> Equivalent<K> for Qwhere
Q: Eq + ?Sized,
K: Borrow<Q> + ?Sized,
impl<Q, K> Equivalent<K> for Qwhere
Q: Eq + ?Sized,
K: Borrow<Q> + ?Sized,
source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
Compare self to
key and return true if they are equal.§impl<T> FmtForward for T
impl<T> FmtForward for T
§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
Causes
self to use its Binary implementation when Debug-formatted.§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
Causes
self to use its Display implementation when
Debug-formatted.§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
Causes
self to use its LowerExp implementation when
Debug-formatted.§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
Causes
self to use its LowerHex implementation when
Debug-formatted.§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
Causes
self to use its Octal implementation when Debug-formatted.§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
Causes
self to use its Pointer implementation when
Debug-formatted.§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
Causes
self to use its UpperExp implementation when
Debug-formatted.§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
Causes
self to use its UpperHex implementation when
Debug-formatted.§fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
Formats each item in a sequence. Read more
source§impl<T> Instrument for T
impl<T> Instrument for T
source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Pipes by value. This is generally the method you want to use. Read more
§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
Borrows
self and passes that borrow into the pipe function. Read more§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
Mutably borrows
self and passes that borrow into the pipe function. Read more§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> Rwhere
Self: Borrow<B>,
B: 'a + ?Sized,
R: 'a,
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> Rwhere
Self: Borrow<B>,
B: 'a + ?Sized,
R: 'a,
§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R
) -> Rwhere
Self: BorrowMut<B>,
B: 'a + ?Sized,
R: 'a,
fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R
) -> Rwhere
Self: BorrowMut<B>,
B: 'a + ?Sized,
R: 'a,
§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> Rwhere
Self: AsRef<U>,
U: 'a + ?Sized,
R: 'a,
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> Rwhere
Self: AsRef<U>,
U: 'a + ?Sized,
R: 'a,
Borrows
self, then passes self.as_ref() into the pipe function.§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> Rwhere
Self: AsMut<U>,
U: 'a + ?Sized,
R: 'a,
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> Rwhere
Self: AsMut<U>,
U: 'a + ?Sized,
R: 'a,
Mutably borrows
self, then passes self.as_mut() into the pipe
function.§impl<T> Pointable for T
impl<T> Pointable for T
§impl<T> Tap for T
impl<T> Tap for T
§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Selfwhere
Self: Borrow<B>,
B: ?Sized,
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Selfwhere
Self: Borrow<B>,
B: ?Sized,
Immutable access to the
Borrow<B> of a value. Read more§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Selfwhere
Self: BorrowMut<B>,
B: ?Sized,
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Selfwhere
Self: BorrowMut<B>,
B: ?Sized,
Mutable access to the
BorrowMut<B> of a value. Read more§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Selfwhere
Self: AsRef<R>,
R: ?Sized,
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Selfwhere
Self: AsRef<R>,
R: ?Sized,
Immutable access to the
AsRef<R> view of a value. Read more§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Selfwhere
Self: AsMut<R>,
R: ?Sized,
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Selfwhere
Self: AsMut<R>,
R: ?Sized,
Mutable access to the
AsMut<R> view of a value. Read more§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Selfwhere
Self: Deref<Target = T>,
T: ?Sized,
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Selfwhere
Self: Deref<Target = T>,
T: ?Sized,
Immutable access to the
Deref::Target of a value. Read more§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Selfwhere
Self: DerefMut<Target = T> + Deref,
T: ?Sized,
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Selfwhere
Self: DerefMut<Target = T> + Deref,
T: ?Sized,
Mutable access to the
Deref::Target of a value. Read more§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
Calls
.tap() only in debug builds, and is erased in release builds.§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
Calls
.tap_mut() only in debug builds, and is erased in release
builds.§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Selfwhere
Self: Borrow<B>,
B: ?Sized,
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Selfwhere
Self: Borrow<B>,
B: ?Sized,
Calls
.tap_borrow() only in debug builds, and is erased in release
builds.§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Selfwhere
Self: BorrowMut<B>,
B: ?Sized,
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Selfwhere
Self: BorrowMut<B>,
B: ?Sized,
Calls
.tap_borrow_mut() only in debug builds, and is erased in release
builds.§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Selfwhere
Self: AsRef<R>,
R: ?Sized,
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Selfwhere
Self: AsRef<R>,
R: ?Sized,
Calls
.tap_ref() only in debug builds, and is erased in release
builds.§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Selfwhere
Self: AsMut<R>,
R: ?Sized,
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Selfwhere
Self: AsMut<R>,
R: ?Sized,
Calls
.tap_ref_mut() only in debug builds, and is erased in release
builds.