fn target_path()

in tough/src/editor/signed.rs [640:731]


    fn target_path(
        &self,
        input: &Path,
        outdir: &Path,
        target_filename: Option<&TargetName>,
    ) -> Result<TargetPath> {
        let outdir =
            std::fs::canonicalize(outdir).context(error::AbsolutePathSnafu { path: outdir })?;

        // If the caller requested a specific target filename, use that, otherwise use the filename
        // component of the input path.
        let target_name = if let Some(target_filename) = target_filename {
            Cow::Borrowed(target_filename)
        } else {
            Cow::Owned(TargetName::new(
                input
                    .file_name()
                    .context(error::NoFileNameSnafu { path: input })?
                    .to_str()
                    .context(error::PathUtf8Snafu { path: input })?,
            )?)
        };

        // create a Target object using the input path.
        let target_from_path =
            Target::from_path(input).context(error::TargetFromPathSnafu { path: input })?;

        // Use the file name to see if a target exists in the repo
        // with that name. If so...
        let repo_targets = &self.targets();
        let repo_target = repo_targets
            .get(&target_name)
            .context(error::PathIsNotTargetSnafu { path: input })?;
        // compare the hashes of the target from the repo and the target we just created.  They
        // should match, or we alert the caller; if target replacement is intended, it should
        // happen earlier, in RepositoryEditor.
        ensure!(
            target_from_path.hashes.sha256 == repo_target.hashes.sha256,
            error::HashMismatchSnafu {
                context: "target",
                calculated: hex::encode(target_from_path.hashes.sha256),
                expected: hex::encode(&repo_target.hashes.sha256),
            }
        );

        let dest = if self.consistent_snapshot() {
            outdir.join(format!(
                "{}.{}",
                hex::encode(&target_from_path.hashes.sha256),
                target_name.resolved()
            ))
        } else {
            outdir.join(target_name.resolved())
        };

        // Return the target path, using the `TargetPath` enum that represents the type of file
        // that already exists at that path (if any)
        if !dest.exists() {
            return Ok(TargetPath::New { path: dest });
        }

        // If we're using consistent snapshots, filenames include the checksum, so we know they're
        // unique; if we're not, then there could be a target from another repo with the same name
        // but different checksum.  We can't assume such conflicts are OK, so we fail.
        if !self.consistent_snapshot() {
            // Use DigestAdapter to get a streaming checksum of the file without needing to hold
            // its contents.
            let f = fs::File::open(&dest).context(error::FileOpenSnafu { path: &dest })?;
            let mut reader = DigestAdapter::sha256(
                Box::new(f),
                &repo_target.hashes.sha256,
                Url::from_file_path(&dest)
                    .ok() // dump unhelpful `()` error
                    .context(error::FileUrlSnafu { path: &dest })?,
            );
            let mut dev_null = std::io::sink();
            // The act of reading with the DigestAdapter verifies the checksum, assuming the read
            // succeeds.
            std::io::copy(&mut reader, &mut dev_null)
                .context(error::FileReadSnafu { path: &dest })?;
        }

        let metadata =
            fs::symlink_metadata(&dest).context(error::FileMetadataSnafu { path: &dest })?;
        if metadata.file_type().is_file() {
            Ok(TargetPath::File { path: dest })
        } else if metadata.file_type().is_symlink() {
            Ok(TargetPath::Symlink { path: dest })
        } else {
            error::InvalidFileTypeSnafu { path: dest }.fail()
        }
    }