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
use std::path::PathBuf;

pub use cargo_metadata;

pub fn locate_bootloader() -> Result<BootloaderInfo, Error> {
    let project_metadata = cargo_metadata::MetadataCommand::new().exec()?;

    let kernel_manifest_path = locate_cargo_manifest::locate_manifest()?;

    let kernel_pkg = project_metadata
        .packages
        .iter()
        .find(|p| p.manifest_path == kernel_manifest_path)
        .ok_or_else(|| Error::KernelPackageNotFound {
            manifest_path: kernel_manifest_path.to_owned(),
        })?;

    let bootloader_pkg = bootloader_package(&project_metadata, kernel_pkg)?;

    Ok(BootloaderInfo {
        package: bootloader_pkg.to_owned(),
        kernel_manifest_path,
    })
}

#[derive(Debug)]
pub struct BootloaderInfo {
    pub package: cargo_metadata::Package,
    pub kernel_manifest_path: PathBuf,
}

/// There is something wrong with the bootloader dependency.
#[derive(Debug, thiserror::Error)]
pub enum Error {
    /// Error occured while running `cargo_metadata`
    #[error("An error occured while running `cargo_metadata`")]
    CargoMetadata {
        #[from]
        metadata_error: cargo_metadata::Error,
    },

    /// Failed to locate cargo manifest
    #[error("Failed to locate the cargo manifest (`Cargo.toml`)")]
    LocateManifest(#[from] locate_cargo_manifest::LocateManifestError),

    /// Bootloader dependency not found
    #[error(
        "Bootloader dependency not found\n\n\
        You need to add a dependency on a crate named `bootloader` in your Cargo.toml."
    )]
    BootloaderNotFound,

    /// Could not find kernel package in cargo metadata
    #[error(
        "Could not find package with manifest path `{manifest_path}` in cargo metadata output"
    )]
    KernelPackageNotFound {
        /// The manifest path of the kernel package
        manifest_path: PathBuf,
    },

    /// Could not find some required information in the `cargo metadata` output
    #[error("Could not find required key `{key}` in cargo metadata output")]
    CargoMetadataIncomplete {
        /// The required key that was not found
        key: String,
    },
}

/// Returns the package metadata for the bootloader crate
fn bootloader_package<'a>(
    project_metadata: &'a cargo_metadata::Metadata,
    kernel_package: &cargo_metadata::Package,
) -> Result<&'a cargo_metadata::Package, Error> {
    let bootloader_name = {
        let mut dependencies = kernel_package.dependencies.iter();
        let bootloader_package = dependencies
            .find(|p| p.rename.as_ref().unwrap_or(&p.name) == "bootloader")
            .ok_or(Error::BootloaderNotFound)?;
        bootloader_package.name.clone()
    };

    project_metadata
        .packages
        .iter()
        .find(|p| p.name == bootloader_name)
        .ok_or(Error::CargoMetadataIncomplete {
            key: format!("packages[name = `{}`", &bootloader_name),
        })
}