| use std::hash::{Hash, Hasher}; |
| use std::path::{Path, PathBuf}; |
| use std::{fmt, io}; |
| |
| use rustc_error_messages::into_diag_arg_using_display; |
| use rustc_fs_util::try_canonicalize; |
| use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; |
| |
| /// Either a target tuple string or a path to a JSON file. |
| #[derive(Clone, Debug)] |
| pub enum TargetTuple { |
| TargetTuple(String), |
| TargetJson { |
| /// Warning: This field may only be used by rustdoc. Using it anywhere else will lead to |
| /// inconsistencies as it is discarded during serialization. |
| path_for_rustdoc: PathBuf, |
| tuple: String, |
| contents: String, |
| }, |
| } |
| |
| // Use a manual implementation to ignore the path field |
| impl PartialEq for TargetTuple { |
| fn eq(&self, other: &Self) -> bool { |
| match (self, other) { |
| (Self::TargetTuple(l0), Self::TargetTuple(r0)) => l0 == r0, |
| ( |
| Self::TargetJson { path_for_rustdoc: _, tuple: l_tuple, contents: l_contents }, |
| Self::TargetJson { path_for_rustdoc: _, tuple: r_tuple, contents: r_contents }, |
| ) => l_tuple == r_tuple && l_contents == r_contents, |
| _ => false, |
| } |
| } |
| } |
| |
| // Use a manual implementation to ignore the path field |
| impl Hash for TargetTuple { |
| fn hash<H: Hasher>(&self, state: &mut H) -> () { |
| match self { |
| TargetTuple::TargetTuple(tuple) => { |
| 0u8.hash(state); |
| tuple.hash(state) |
| } |
| TargetTuple::TargetJson { path_for_rustdoc: _, tuple, contents } => { |
| 1u8.hash(state); |
| tuple.hash(state); |
| contents.hash(state) |
| } |
| } |
| } |
| } |
| |
| // Use a manual implementation to prevent encoding the target json file path in the crate metadata |
| impl<S: Encoder> Encodable<S> for TargetTuple { |
| fn encode(&self, s: &mut S) { |
| match self { |
| TargetTuple::TargetTuple(tuple) => { |
| s.emit_u8(0); |
| s.emit_str(tuple); |
| } |
| TargetTuple::TargetJson { path_for_rustdoc: _, tuple, contents } => { |
| s.emit_u8(1); |
| s.emit_str(tuple); |
| s.emit_str(contents); |
| } |
| } |
| } |
| } |
| |
| impl<D: Decoder> Decodable<D> for TargetTuple { |
| fn decode(d: &mut D) -> Self { |
| match d.read_u8() { |
| 0 => TargetTuple::TargetTuple(d.read_str().to_owned()), |
| 1 => TargetTuple::TargetJson { |
| path_for_rustdoc: PathBuf::new(), |
| tuple: d.read_str().to_owned(), |
| contents: d.read_str().to_owned(), |
| }, |
| _ => { |
| panic!("invalid enum variant tag while decoding `TargetTuple`, expected 0..2"); |
| } |
| } |
| } |
| } |
| |
| impl TargetTuple { |
| /// Creates a target tuple from the passed target tuple string. |
| pub fn from_tuple(tuple: &str) -> Self { |
| TargetTuple::TargetTuple(tuple.into()) |
| } |
| |
| /// Creates a target tuple from the passed target path. |
| pub fn from_path(path: &Path) -> Result<Self, io::Error> { |
| let canonicalized_path = try_canonicalize(path)?; |
| let contents = std::fs::read_to_string(&canonicalized_path).map_err(|err| { |
| io::Error::new( |
| io::ErrorKind::InvalidInput, |
| format!("target path {canonicalized_path:?} is not a valid file: {err}"), |
| ) |
| })?; |
| let tuple = canonicalized_path |
| .file_stem() |
| .expect("target path must not be empty") |
| .to_str() |
| .expect("target path must be valid unicode") |
| .to_owned(); |
| Ok(TargetTuple::TargetJson { path_for_rustdoc: canonicalized_path, tuple, contents }) |
| } |
| |
| /// Returns a string tuple for this target. |
| /// |
| /// If this target is a path, the file name (without extension) is returned. |
| pub fn tuple(&self) -> &str { |
| match *self { |
| TargetTuple::TargetTuple(ref tuple) | TargetTuple::TargetJson { ref tuple, .. } => { |
| tuple |
| } |
| } |
| } |
| |
| /// Returns an extended string tuple for this target. |
| /// |
| /// If this target is a path, a hash of the path is appended to the tuple returned |
| /// by `tuple()`. |
| pub fn debug_tuple(&self) -> String { |
| use std::hash::DefaultHasher; |
| |
| match self { |
| TargetTuple::TargetTuple(tuple) => tuple.to_owned(), |
| TargetTuple::TargetJson { path_for_rustdoc: _, tuple, contents: content } => { |
| let mut hasher = DefaultHasher::new(); |
| content.hash(&mut hasher); |
| let hash = hasher.finish(); |
| format!("{tuple}-{hash}") |
| } |
| } |
| } |
| } |
| |
| impl fmt::Display for TargetTuple { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!(f, "{}", self.debug_tuple()) |
| } |
| } |
| |
| into_diag_arg_using_display!(&TargetTuple); |