summaryrefslogtreecommitdiffstats
path: root/src/bindgen/cargo
diff options
context:
space:
mode:
Diffstat (limited to 'src/bindgen/cargo')
-rw-r--r--src/bindgen/cargo/cargo.rs252
-rw-r--r--src/bindgen/cargo/cargo_expand.rs145
-rw-r--r--src/bindgen/cargo/cargo_lock.rs51
-rw-r--r--src/bindgen/cargo/cargo_metadata.rs253
-rw-r--r--src/bindgen/cargo/cargo_toml.rs67
-rw-r--r--src/bindgen/cargo/mod.rs12
6 files changed, 780 insertions, 0 deletions
diff --git a/src/bindgen/cargo/cargo.rs b/src/bindgen/cargo/cargo.rs
new file mode 100644
index 0000000..69cf938
--- /dev/null
+++ b/src/bindgen/cargo/cargo.rs
@@ -0,0 +1,252 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::path::{Path, PathBuf};
+
+use crate::bindgen::cargo::cargo_expand;
+use crate::bindgen::cargo::cargo_lock::{self, Lock};
+pub(crate) use crate::bindgen::cargo::cargo_metadata::PackageRef;
+use crate::bindgen::cargo::cargo_metadata::{self, Metadata};
+use crate::bindgen::cargo::cargo_toml;
+use crate::bindgen::config::Profile;
+use crate::bindgen::error::Error;
+use crate::bindgen::ir::Cfg;
+
+/// Parse a dependency string used in Cargo.lock
+fn parse_dep_string(dep_string: &str) -> (&str, Option<&str>) {
+ let split: Vec<&str> = dep_string.split_whitespace().collect();
+
+ (split[0], split.get(1).cloned())
+}
+
+/// A collection of metadata for a library from cargo.
+#[derive(Clone, Debug)]
+pub(crate) struct Cargo {
+ manifest_path: PathBuf,
+ binding_crate_name: String,
+ lock: Option<Lock>,
+ metadata: Metadata,
+ clean: bool,
+}
+
+impl Cargo {
+ /// Gather metadata from cargo for a specific library and binding crate
+ /// name. If dependency finding isn't needed then Cargo.lock files don't
+ /// need to be parsed.
+ pub(crate) fn load(
+ crate_dir: &Path,
+ lock_file: Option<&str>,
+ binding_crate_name: Option<&str>,
+ use_cargo_lock: bool,
+ clean: bool,
+ only_target_dependencies: bool,
+ existing_metadata_file: Option<&Path>,
+ ) -> Result<Cargo, Error> {
+ let toml_path = crate_dir.join("Cargo.toml");
+ let metadata =
+ cargo_metadata::metadata(&toml_path, existing_metadata_file, only_target_dependencies)
+ .map_err(|x| Error::CargoMetadata(toml_path.to_str().unwrap().to_owned(), x))?;
+ let lock_path = lock_file
+ .map(PathBuf::from)
+ .unwrap_or_else(|| Path::new(&metadata.workspace_root).join("Cargo.lock"));
+
+ let lock = if use_cargo_lock {
+ match cargo_lock::lock(&lock_path) {
+ Ok(lock) => Some(lock),
+ Err(x) => {
+ warn!("Couldn't load lock file {:?}: {:?}", lock_path, x);
+ None
+ }
+ }
+ } else {
+ None
+ };
+
+ // Use the specified binding crate name or infer it from the manifest
+ let binding_crate_name = match binding_crate_name {
+ Some(s) => s.to_owned(),
+ None => {
+ let manifest = cargo_toml::manifest(&toml_path)
+ .map_err(|x| Error::CargoToml(toml_path.to_str().unwrap().to_owned(), x))?;
+ manifest.package.name
+ }
+ };
+
+ Ok(Cargo {
+ manifest_path: toml_path,
+ binding_crate_name,
+ lock,
+ metadata,
+ clean,
+ })
+ }
+
+ pub(crate) fn binding_crate_name(&self) -> &str {
+ &self.binding_crate_name
+ }
+
+ pub(crate) fn binding_crate_ref(&self) -> PackageRef {
+ match self.find_pkg_ref(&self.binding_crate_name) {
+ Some(pkg_ref) => pkg_ref,
+ None => panic!(
+ "Unable to find {} for {:?}",
+ self.binding_crate_name, self.manifest_path
+ ),
+ }
+ }
+
+ pub(crate) fn dependencies(&self, package: &PackageRef) -> Vec<(PackageRef, Option<Cfg>)> {
+ let lock = match self.lock {
+ Some(ref lock) => lock,
+ None => return vec![],
+ };
+
+ let mut dependencies = None;
+
+ // Find the dependencies listing in the lockfile
+ if let Some(ref root) = lock.root {
+ // If the version is not on the lockfile then it shouldn't be
+ // ambiguous.
+ if root.name == package.name
+ && package
+ .version
+ .as_ref()
+ .map_or(true, |v| *v == root.version)
+ {
+ dependencies = root.dependencies.as_ref();
+ }
+ }
+ if dependencies.is_none() {
+ if let Some(ref lock_packages) = lock.package {
+ for lock_package in lock_packages {
+ if lock_package.name == package.name
+ && package
+ .version
+ .as_ref()
+ .map_or(true, |v| *v == lock_package.version)
+ {
+ dependencies = lock_package.dependencies.as_ref();
+ break;
+ }
+ }
+ }
+ }
+ if dependencies.is_none() {
+ return vec![];
+ }
+
+ dependencies
+ .unwrap()
+ .iter()
+ .map(|dep| {
+ let (dep_name, dep_version) = parse_dep_string(dep);
+
+ // If a version was not specified find the only package with the name of the dependency
+ let dep_version = dep_version.or_else(|| {
+ let mut versions = self.metadata.packages.iter().filter_map(|package| {
+ if package.name_and_version.name != dep_name {
+ return None;
+ }
+ package.name_and_version.version.as_deref()
+ });
+
+ // If the iterator contains more items, meaning multiple versions of the same
+ // package are present, warn! amd abort.
+ let version = versions.next();
+ if versions.next().is_none() {
+ version
+ } else {
+ warn!("when looking for a version for package {}, multiple versions where found", dep_name);
+ None
+ }
+ });
+
+ // Try to find the cfgs in the Cargo.toml
+ let cfg = self
+ .metadata
+ .packages
+ .get(package)
+ .and_then(|meta_package| meta_package.dependencies.get(dep_name))
+ .and_then(Cfg::load_metadata);
+
+ let package_ref = PackageRef {
+ name: dep_name.to_owned(),
+ version: dep_version.map(|v| v.to_owned()),
+ };
+
+ (package_ref, cfg)
+ })
+ .collect()
+ }
+
+ /// Finds the package reference in `cargo metadata` that has `package_name`
+ /// ignoring the version.
+ fn find_pkg_ref(&self, package_name: &str) -> Option<PackageRef> {
+ for package in &self.metadata.packages {
+ if package.name_and_version.name == package_name {
+ return Some(package.name_and_version.clone());
+ }
+ }
+ None
+ }
+
+ /// Finds the directory for a specified package reference.
+ #[allow(unused)]
+ pub(crate) fn find_crate_dir(&self, package: &PackageRef) -> Option<PathBuf> {
+ self.metadata
+ .packages
+ .get(package)
+ .and_then(|meta_package| {
+ Path::new(&meta_package.manifest_path)
+ .parent()
+ .map(|x| x.to_owned())
+ })
+ }
+
+ /// Finds `src/lib.rs` for a specified package reference.
+ pub(crate) fn find_crate_src(&self, package: &PackageRef) -> Option<PathBuf> {
+ let kind_lib = String::from("lib");
+ let kind_staticlib = String::from("staticlib");
+ let kind_rlib = String::from("rlib");
+ let kind_cdylib = String::from("cdylib");
+ let kind_dylib = String::from("dylib");
+
+ self.metadata
+ .packages
+ .get(package)
+ .and_then(|meta_package| {
+ for target in &meta_package.targets {
+ if target.kind.contains(&kind_lib)
+ || target.kind.contains(&kind_staticlib)
+ || target.kind.contains(&kind_rlib)
+ || target.kind.contains(&kind_cdylib)
+ || target.kind.contains(&kind_dylib)
+ {
+ return Some(PathBuf::from(&target.src_path));
+ }
+ }
+ None
+ })
+ }
+
+ pub(crate) fn expand_crate(
+ &self,
+ package: &PackageRef,
+ expand_all_features: bool,
+ expand_default_features: bool,
+ expand_features: &Option<Vec<String>>,
+ profile: Profile,
+ ) -> Result<String, cargo_expand::Error> {
+ cargo_expand::expand(
+ &self.manifest_path,
+ &package.name,
+ package.version.as_deref(),
+ self.clean,
+ expand_all_features,
+ expand_default_features,
+ expand_features,
+ profile,
+ )
+ }
+}
diff --git a/src/bindgen/cargo/cargo_expand.rs b/src/bindgen/cargo/cargo_expand.rs
new file mode 100644
index 0000000..565a0d1
--- /dev/null
+++ b/src/bindgen/cargo/cargo_expand.rs
@@ -0,0 +1,145 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use crate::bindgen::config::Profile;
+use std::env;
+use std::error;
+use std::fmt;
+use std::io;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+use std::str::{from_utf8, Utf8Error};
+
+extern crate tempfile;
+use self::tempfile::Builder;
+
+#[derive(Debug)]
+/// Possible errors that can occur during `rustc -Zunpretty=expanded`.
+pub enum Error {
+ /// Error during creation of temporary directory
+ Io(io::Error),
+ /// Output of `cargo metadata` was not valid utf8
+ Utf8(Utf8Error),
+ /// Error during execution of `cargo rustc -Zunpretty=expanded`
+ Compile(String),
+}
+
+impl From<io::Error> for Error {
+ fn from(err: io::Error) -> Self {
+ Error::Io(err)
+ }
+}
+impl From<Utf8Error> for Error {
+ fn from(err: Utf8Error) -> Self {
+ Error::Utf8(err)
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Error::Io(ref err) => err.fmt(f),
+ Error::Utf8(ref err) => err.fmt(f),
+ Error::Compile(ref err) => write!(f, "{}", err),
+ }
+ }
+}
+
+impl error::Error for Error {
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ match self {
+ Error::Io(ref err) => Some(err),
+ Error::Utf8(ref err) => Some(err),
+ Error::Compile(..) => None,
+ }
+ }
+}
+
+/// Use rustc to expand and pretty print the crate into a single file,
+/// removing any macros in the process.
+#[allow(clippy::too_many_arguments)]
+pub fn expand(
+ manifest_path: &Path,
+ crate_name: &str,
+ version: Option<&str>,
+ use_tempdir: bool,
+ expand_all_features: bool,
+ expand_default_features: bool,
+ expand_features: &Option<Vec<String>>,
+ profile: Profile,
+) -> Result<String, Error> {
+ let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo"));
+ let mut cmd = Command::new(cargo);
+
+ let mut _temp_dir = None; // drop guard
+ if use_tempdir {
+ _temp_dir = Some(Builder::new().prefix("cbindgen-expand").tempdir()?);
+ cmd.env("CARGO_TARGET_DIR", _temp_dir.unwrap().path());
+ } else if let Ok(ref path) = env::var("CARGO_EXPAND_TARGET_DIR") {
+ cmd.env("CARGO_TARGET_DIR", path);
+ } else if let Ok(ref path) = env::var("OUT_DIR") {
+ // When cbindgen was started programatically from a build.rs file, Cargo is running and
+ // locking the default target directory. In this case we need to use another directory,
+ // else we would end up in a deadlock. If Cargo is running `OUT_DIR` will be set, so we
+ // can use a directory relative to that.
+ cmd.env("CARGO_TARGET_DIR", PathBuf::from(path).join("expanded"));
+ }
+
+ // Set this variable so that we don't call it recursively if we expand a crate that is using
+ // cbindgen
+ cmd.env("_CBINDGEN_IS_RUNNING", "1");
+
+ cmd.arg("rustc");
+ cmd.arg("--lib");
+ // When build with the release profile we can't choose the `check` profile.
+ if profile != Profile::Release {
+ cmd.arg("--profile=check");
+ }
+ cmd.arg("--manifest-path");
+ cmd.arg(manifest_path);
+ if let Some(features) = expand_features {
+ cmd.arg("--features");
+ let mut features_str = String::new();
+ for (index, feature) in features.iter().enumerate() {
+ if index != 0 {
+ features_str.push(' ');
+ }
+ features_str.push_str(feature);
+ }
+ cmd.arg(features_str);
+ }
+ if expand_all_features {
+ cmd.arg("--all-features");
+ }
+ if !expand_default_features {
+ cmd.arg("--no-default-features");
+ }
+ match profile {
+ Profile::Debug => {}
+ Profile::Release => {
+ cmd.arg("--release");
+ }
+ }
+ cmd.arg("-p");
+ let mut package = crate_name.to_owned();
+ if let Some(version) = version {
+ package.push(':');
+ package.push_str(version);
+ }
+ cmd.arg(&package);
+ cmd.arg("--verbose");
+ cmd.arg("--");
+ cmd.arg("-Zunpretty=expanded");
+ info!("Command: {:?}", cmd);
+ let output = cmd.output()?;
+
+ let src = from_utf8(&output.stdout)?.to_owned();
+ let error = from_utf8(&output.stderr)?.to_owned();
+
+ if src.is_empty() {
+ Err(Error::Compile(error))
+ } else {
+ Ok(src)
+ }
+}
diff --git a/src/bindgen/cargo/cargo_lock.rs b/src/bindgen/cargo/cargo_lock.rs
new file mode 100644
index 0000000..9302082
--- /dev/null
+++ b/src/bindgen/cargo/cargo_lock.rs
@@ -0,0 +1,51 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::fs::File;
+use std::io;
+use std::io::Read;
+use std::path::Path;
+
+#[derive(Debug)]
+/// Possible errors that can occur during Cargo.toml parsing.
+pub enum Error {
+ /// Error during reading of Cargo.toml
+ Io(io::Error),
+ /// Deserialization error
+ Toml(toml::de::Error),
+}
+
+impl From<io::Error> for Error {
+ fn from(err: io::Error) -> Self {
+ Error::Io(err)
+ }
+}
+impl From<toml::de::Error> for Error {
+ fn from(err: toml::de::Error) -> Self {
+ Error::Toml(err)
+ }
+}
+
+#[derive(Clone, Deserialize, Debug)]
+pub struct Lock {
+ pub root: Option<Package>,
+ pub package: Option<Vec<Package>>,
+}
+
+#[derive(Clone, Deserialize, Debug)]
+pub struct Package {
+ pub name: String,
+ pub version: String,
+ /// A list of dependencies formatted like "NAME VERSION-OPT REGISTRY-OPT"
+ pub dependencies: Option<Vec<String>>,
+}
+
+/// Parse the Cargo.toml for a given path
+pub fn lock(manifest_path: &Path) -> Result<Lock, Error> {
+ let mut s = String::new();
+ let mut f = File::open(manifest_path)?;
+ f.read_to_string(&mut s)?;
+
+ toml::from_str::<Lock>(&s).map_err(|x| x.into())
+}
diff --git a/src/bindgen/cargo/cargo_metadata.rs b/src/bindgen/cargo/cargo_metadata.rs
new file mode 100644
index 0000000..01ab80f
--- /dev/null
+++ b/src/bindgen/cargo/cargo_metadata.rs
@@ -0,0 +1,253 @@
+#![deny(missing_docs)]
+#![allow(dead_code)]
+//! Structured access to the output of `cargo metadata`
+//! Usually used from within a `cargo-*` executable
+
+// Forked from `https://github.com/oli-obk/cargo_metadata`
+// Modifications:
+// 1. Remove `resolve` from Metadata because it was causing parse failures
+// 2. Fix the `manifest-path` argument
+// 3. Add `--all-features` argument
+// 4. Remove the `--no-deps` argument
+
+use std::borrow::{Borrow, Cow};
+use std::collections::{HashMap, HashSet};
+use std::env;
+use std::error;
+use std::fmt;
+use std::hash::{Hash, Hasher};
+use std::io;
+use std::path::Path;
+use std::process::{Command, Output};
+use std::str::Utf8Error;
+
+#[derive(Clone, Deserialize, Debug)]
+/// Starting point for metadata returned by `cargo metadata`
+pub struct Metadata {
+ /// A list of all crates referenced by this crate (and the crate itself)
+ pub packages: HashSet<Package>,
+ version: usize,
+ /// path to the workspace containing the `Cargo.lock`
+ pub workspace_root: String,
+}
+
+/// A reference to a package including it's name and the specific version.
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
+pub struct PackageRef {
+ pub name: String,
+ pub version: Option<String>,
+}
+
+#[derive(Clone, Deserialize, Debug)]
+/// A crate
+pub struct Package {
+ #[serde(flatten)]
+ pub name_and_version: PackageRef,
+ id: String,
+ source: Option<String>,
+ /// List of dependencies of this particular package
+ pub dependencies: HashSet<Dependency>,
+ /// Targets provided by the crate (lib, bin, example, test, ...)
+ pub targets: Vec<Target>,
+ features: HashMap<String, Vec<String>>,
+ /// path containing the `Cargo.toml`
+ pub manifest_path: String,
+}
+
+#[derive(Clone, Deserialize, Debug)]
+/// A dependency of the main crate
+pub struct Dependency {
+ /// Name as given in the `Cargo.toml`
+ pub name: String,
+ source: Option<String>,
+ /// Whether this is required or optional
+ pub req: String,
+ kind: Option<String>,
+ optional: bool,
+ uses_default_features: bool,
+ features: Vec<String>,
+ pub target: Option<String>,
+}
+
+#[derive(Clone, Deserialize, Debug)]
+/// A single target (lib, bin, example, ...) provided by a crate
+pub struct Target {
+ /// Name as given in the `Cargo.toml` or generated from the file name
+ pub name: String,
+ /// Kind of target ("bin", "example", "test", "bench", "lib")
+ pub kind: Vec<String>,
+ /// Almost the same as `kind`, except when an example is a library instad of an executable.
+ /// In that case `crate_types` contains things like `rlib` and `dylib` while `kind` is `example`
+ #[serde(default)]
+ pub crate_types: Vec<String>,
+ /// Path to the main source file of the target
+ pub src_path: String,
+}
+
+#[derive(Debug)]
+/// Possible errors that can occur during metadata parsing.
+pub enum Error {
+ /// Error during execution of `cargo metadata`
+ Io(io::Error),
+ /// Metadata extraction failure
+ Metadata(Output),
+ /// Output of `cargo metadata` was not valid utf8
+ Utf8(Utf8Error),
+ /// Deserialization error (structure of json did not match expected structure)
+ Json(serde_json::Error),
+}
+
+impl From<io::Error> for Error {
+ fn from(err: io::Error) -> Self {
+ Error::Io(err)
+ }
+}
+impl From<Utf8Error> for Error {
+ fn from(err: Utf8Error) -> Self {
+ Error::Utf8(err)
+ }
+}
+impl From<serde_json::Error> for Error {
+ fn from(err: serde_json::Error) -> Self {
+ Error::Json(err)
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Error::Io(ref err) => err.fmt(f),
+ Error::Metadata(_) => write!(f, "Metadata error"),
+ Error::Utf8(ref err) => err.fmt(f),
+ Error::Json(ref err) => err.fmt(f),
+ }
+ }
+}
+
+impl error::Error for Error {
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ match self {
+ Error::Io(ref err) => Some(err),
+ Error::Metadata(_) => None,
+ Error::Utf8(ref err) => Some(err),
+ Error::Json(ref err) => Some(err),
+ }
+ }
+}
+
+// Implementations that let us lookup Packages and Dependencies by name (string)
+
+impl Borrow<PackageRef> for Package {
+ fn borrow(&self) -> &PackageRef {
+ &self.name_and_version
+ }
+}
+
+impl Hash for Package {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.name_and_version.hash(state);
+ }
+}
+
+impl PartialEq for Package {
+ fn eq(&self, other: &Self) -> bool {
+ self.name_and_version == other.name_and_version
+ }
+}
+
+impl Eq for Package {}
+
+impl Borrow<str> for Dependency {
+ fn borrow(&self) -> &str {
+ &self.name
+ }
+}
+
+impl Hash for Dependency {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.name.hash(state);
+ }
+}
+
+impl PartialEq for Dependency {
+ fn eq(&self, other: &Self) -> bool {
+ self.name == other.name
+ }
+}
+
+impl Eq for Dependency {}
+
+fn discover_target(manifest_path: &Path) -> Option<String> {
+ if let Ok(target) = std::env::var("TARGET") {
+ return Some(target);
+ }
+
+ // We must be running as a standalone script, not under cargo.
+ // Let's use the host platform instead.
+ // We figure out the host platform through rustc and use that.
+ // We unfortunatelly cannot go through cargo, since cargo rustc _also_ builds.
+ // If `rustc` fails to run, we just fall back to not passing --filter-platforms.
+ //
+ // NOTE: We set the current directory in case of rustup shenanigans.
+ let rustc = env::var("RUSTC").unwrap_or_else(|_| String::from("rustc"));
+ debug!("Discovering host platform by {:?}", rustc);
+
+ let rustc_output = Command::new(rustc)
+ .current_dir(manifest_path.parent().unwrap())
+ .arg("-vV")
+ .output();
+ let rustc_output = match rustc_output {
+ Ok(ref out) => String::from_utf8_lossy(&out.stdout),
+ Err(..) => return None,
+ };
+
+ let field = "host: ";
+ rustc_output
+ .lines()
+ .find_map(|l| l.strip_prefix(field).map(|stripped| stripped.to_string()))
+}
+
+/// The main entry point to obtaining metadata
+pub fn metadata(
+ manifest_path: &Path,
+ existing_metadata_file: Option<&Path>,
+ only_target: bool,
+) -> Result<Metadata, Error> {
+ let output;
+ let metadata = match existing_metadata_file {
+ Some(path) => Cow::Owned(std::fs::read_to_string(path)?),
+ None => {
+ let target = if only_target {
+ let target = discover_target(manifest_path);
+ if target.is_none() {
+ warn!(
+ "Failed to discover host platform for cargo metadata; \
+ will fetch dependencies for all platforms."
+ );
+ }
+ target
+ } else {
+ None
+ };
+
+ let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo"));
+ let mut cmd = Command::new(cargo);
+ cmd.arg("metadata");
+ cmd.arg("--all-features");
+ cmd.arg("--format-version").arg("1");
+ if let Some(target) = target {
+ cmd.arg("--filter-platform").arg(target);
+ }
+ cmd.arg("--manifest-path");
+ cmd.arg(manifest_path);
+ output = cmd.output()?;
+ if !output.status.success() {
+ return Err(Error::Metadata(output));
+ }
+ Cow::Borrowed(std::str::from_utf8(&output.stdout)?)
+ }
+ };
+
+ let meta: Metadata = serde_json::from_str(&metadata)?;
+ Ok(meta)
+}
diff --git a/src/bindgen/cargo/cargo_toml.rs b/src/bindgen/cargo/cargo_toml.rs
new file mode 100644
index 0000000..998176e
--- /dev/null
+++ b/src/bindgen/cargo/cargo_toml.rs
@@ -0,0 +1,67 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::error;
+use std::fmt;
+use std::fs::File;
+use std::io;
+use std::io::Read;
+use std::path::Path;
+
+#[derive(Debug)]
+/// Possible errors that can occur during Cargo.toml parsing.
+pub enum Error {
+ /// Error during reading of Cargo.toml
+ Io(io::Error),
+ /// Deserialization error
+ Toml(toml::de::Error),
+}
+
+impl From<io::Error> for Error {
+ fn from(err: io::Error) -> Self {
+ Error::Io(err)
+ }
+}
+impl From<toml::de::Error> for Error {
+ fn from(err: toml::de::Error) -> Self {
+ Error::Toml(err)
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Error::Io(ref err) => err.fmt(f),
+ Error::Toml(ref err) => err.fmt(f),
+ }
+ }
+}
+
+impl error::Error for Error {
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ match self {
+ Error::Io(ref err) => Some(err),
+ Error::Toml(ref err) => Some(err),
+ }
+ }
+}
+
+#[derive(Clone, Deserialize, Debug)]
+pub struct Manifest {
+ pub package: Package,
+}
+
+#[derive(Clone, Deserialize, Debug)]
+pub struct Package {
+ pub name: String,
+}
+
+/// Parse the Cargo.toml for a given path
+pub fn manifest(manifest_path: &Path) -> Result<Manifest, Error> {
+ let mut s = String::new();
+ let mut f = File::open(manifest_path)?;
+ f.read_to_string(&mut s)?;
+
+ toml::from_str::<Manifest>(&s).map_err(|x| x.into())
+}
diff --git a/src/bindgen/cargo/mod.rs b/src/bindgen/cargo/mod.rs
new file mode 100644
index 0000000..19fef54
--- /dev/null
+++ b/src/bindgen/cargo/mod.rs
@@ -0,0 +1,12 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#[allow(clippy::module_inception)]
+mod cargo;
+pub(crate) mod cargo_expand;
+pub(crate) mod cargo_lock;
+pub(crate) mod cargo_metadata;
+pub(crate) mod cargo_toml;
+
+pub(crate) use self::cargo::*;