--- /dev/null
+From 3fa3ac75fb82180cc7f32aa8d69b18536143fddf Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Fabian=20Gr=C3=BCnbichler?= <f.gruenbichler@proxmox.com>
+Date: Fri, 10 Jan 2020 14:41:28 +0100
+Subject: [PATCH 1/3] implement local crate support
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
+---
+ debcargo.toml.example | 4 +++
+ src/bin/debcargo.rs | 7 +++-
+ src/config.rs | 8 +++++
+ src/crates.rs | 84 ++++++++++++++++++++++++++++++++++++++++++-
+ src/debian/mod.rs | 53 +++++++++++++++------------
+ 5 files changed, 131 insertions(+), 25 deletions(-)
+
+Index: debcargo/debcargo.toml.example
+===================================================================
+--- debcargo.orig/debcargo.toml.example
++++ debcargo/debcargo.toml.example
+@@ -35,6 +35,10 @@
+ #
+ #overlay = "."
+
++# Local directory where crate can be found, instead of downloading from crates.io. Resolved relative to the directory that contains this config file.
++#
++#crate_src_path = "../.."
++
+ # Paths from the crate tarball, to exclude from the orig tarball.
+ # See https://docs.rs/glob/latest/glob/struct.Pattern.html for syntax
+ #excludes = ["libgit2/**"]
+Index: debcargo/src/bin/debcargo.rs
+===================================================================
+--- debcargo.orig/src/bin/debcargo.rs
++++ debcargo/src/bin/debcargo.rs
+@@ -57,7 +57,12 @@ fn do_package(matches: &ArgMatches) -> R
+ let overlay_write_back = !matches.is_present("no-overlay-write-back");
+ let copyright_guess_harder = matches.is_present("copyright-guess-harder");
+
+- let mut crate_info = CrateInfo::new(crate_name, version)?;
++ let crate_path = config.crate_src_path(config_path);
++
++ let mut crate_info = match crate_path {
++ Some(p) => CrateInfo::new_with_local_crate(crate_name, version, &p)?,
++ None => CrateInfo::new(crate_name, version)?,
++ };
+ let pkgbase = BaseInfo::new(
+ crate_name,
+ &crate_info,
+Index: debcargo/src/config.rs
+===================================================================
+--- debcargo.orig/src/config.rs
++++ debcargo/src/config.rs
+@@ -19,6 +19,7 @@ pub struct Config {
+ pub excludes: Option<Vec<String>>,
+ pub whitelist: Option<Vec<String>>,
+ pub allow_prerelease_deps: bool,
++ pub crate_src_path: Option<PathBuf>,
+ pub summary: String,
+ pub description: String,
+ pub maintainer: Option<String>,
+@@ -62,6 +63,7 @@ impl Default for Config {
+ excludes: None,
+ whitelist: None,
+ allow_prerelease_deps: false,
++ crate_src_path: None,
+ summary: "".to_string(),
+ description: "".to_string(),
+ maintainer: None,
+@@ -85,6 +87,12 @@ impl Config {
+ .as_ref()
+ .map(|p| config_path.unwrap().parent().unwrap().join(p))
+ }
++
++ pub fn crate_src_path(&self, config_path: Option<&Path>) -> Option<PathBuf> {
++ self.crate_src_path
++ .as_ref()
++ .map(|p| config_path.unwrap().parent().unwrap().join(p))
++ }
+
+ pub fn is_source_present(&self) -> bool {
+ self.source.is_some()
+Index: debcargo/src/crates.rs
+===================================================================
+--- debcargo.orig/src/crates.rs
++++ debcargo/src/crates.rs
+@@ -5,8 +5,11 @@ use cargo::{
+ core::InternedString,
+ core::{
+ Dependency, EitherManifest, FeatureValue, Manifest, Package, PackageId, Registry, Source,
+- SourceId, Summary, Target, TargetKind,
++ SourceId, Summary, Target, TargetKind, Workspace,
+ },
++ core::source::MaybePackage,
++ ops,
++ ops::PackageOpts,
+ sources::registry::RegistrySource,
+ util::{toml::read_manifest, FileLock},
+ Config,
+@@ -77,6 +80,85 @@ impl CrateInfo {
+ CrateInfo::new_with_update(crate_name, version, true)
+ }
+
++ pub fn new_with_local_crate(crate_name: &str, version: Option<&str>, crate_path: &Path) -> Result<CrateInfo> {
++ let config = Config::default()?;
++ let crate_path = crate_path.canonicalize()?;
++ let source_id = SourceId::for_path(&crate_path)?;
++
++
++ let (package, crate_file) = {
++ let yanked_whitelist = HashSet::new();
++
++ let mut source = source_id.load(&config, &yanked_whitelist)?;
++ source.update()?;
++
++ let package_id = match version {
++ Some(version) => PackageId::new(crate_name, version, source_id)?,
++ None => {
++ let dep = Dependency::parse_no_deprecated(crate_name, None, source_id)?;
++ let mut package_id: Option<PackageId> = None;
++ source.query(&dep, &mut |p| package_id = Some(p.package_id()))?;
++ package_id.unwrap()
++ },
++ };
++
++ let maybe_package = source.download(package_id)?;
++ let package = match maybe_package {
++ MaybePackage::Ready(p) => Ok(p),
++ _ => Err(format_err!("Failed to 'download' local crate {} from {}",
++ crate_name,
++ crate_path.display()
++ )),
++ }?;
++
++ let crate_file = {
++ let workspace = Workspace::ephemeral(
++ package.clone(),
++ &config,
++ None,
++ true)?;
++
++ let opts = PackageOpts {
++ config: &config,
++ verify: false,
++ list: false,
++ check_metadata: true,
++ allow_dirty: true,
++ all_features: true,
++ no_default_features: false,
++ jobs: None,
++ target: None,
++ features: Vec::new(),
++ };
++
++ // as of cargo 0.41 this returns a FileLock with a temp path, instead of the one
++ // it got renamed to
++ if ops::package(&workspace, &opts)?.is_none() {
++ return Err(format_err!("Failed to assemble crate file for local crate {} at {}\n",
++ crate_name,
++ crate_path.display()
++ ));
++ }
++ let filename = format!("{}-{}.crate", crate_name, package_id.version().to_string());
++ workspace.target_dir().join("package").open_rw(&filename, &config, "crate file")?
++ };
++
++ (package.clone(), crate_file)
++ };
++
++ let manifest = package.manifest().clone();
++
++ Ok(CrateInfo {
++ package,
++ manifest,
++ crate_file,
++ config,
++ source_id,
++ excludes: vec![],
++ includes: vec![],
++ })
++ }
++
+ pub fn new_with_update(
+ crate_name: &str,
+ version: Option<&str>,
+Index: debcargo/src/debian/mod.rs
+===================================================================
+--- debcargo.orig/src/debian/mod.rs
++++ debcargo/src/debian/mod.rs
+@@ -346,27 +346,29 @@ pub fn prepare_debian_folder(
+
+ // debian/watch
+ let mut watch = file("watch")?;
+- let uscan_version_pattern = pkgbase
+- .uscan_version_pattern
+- .as_ref()
+- .map_or_else(|| "@ANY_VERSION@".to_string(), |ref s| s.to_string());
+- let uscan_lines = &[
+- "version=4".into(),
+- format!(
+- r"opts=filenamemangle=s/.*\/(.*)\/download/{name}-$1\.tar\.gz/g,\",
+- name = upstream_name
+- ),
+- r"uversionmangle=s/(\d)[_\.\-\+]?((RC|rc|pre|dev|beta|alpha)\d*)$/$1~$2/ \".into(),
+- format!(
+- "https://qa.debian.org/cgi-bin/fakeupstream.cgi?upstream=crates.io/{name} \
+- .*/crates/{name}/{version_pattern}/download",
+- name = upstream_name,
+- version_pattern = uscan_version_pattern
+- ),
+- ];
+- for line in uscan_lines {
+- writeln!(watch, "{}", line)?;
+- }
++ match config.crate_src_path(config_path) {
++ Some(_) => write!(watch, "FIXME add uscan directive for local crate")?,
++ None => {
++ let uscan_version_pattern = pkgbase
++ .uscan_version_pattern
++ .as_ref()
++ .map_or_else(|| "@ANY_VERSION@".to_string(), |ref s| s.to_string());
++ write!(watch, "version=4\n")?;
++ write!(
++ watch,
++ r"opts=filenamemangle=s/.*\/(.*)\/download/{name}-$1\.tar\.gz/g,\\n",
++ name = upstream_name
++ )?;
++ write!(watch, r"uversionmangle=s/(\d)[_\.\-\+]?((RC|rc|pre|dev|beta|alpha)\d*)$/$1~$2/ \\n")?;
++ write!(
++ watch,
++ "https://qa.debian.org/cgi-bin/fakeupstream.cgi?upstream=crates.io/{name} \
++ .*/crates/{name}/{version_pattern}/download\n",
++ name = upstream_name,
++ version_pattern = uscan_version_pattern
++ )?;
++ },
++ };
+
+ // debian/source/format
+ fs::create_dir_all(tempdir.path().join("source"))?;
+@@ -622,14 +624,19 @@ pub fn prepare_debian_folder(
+ // debian/changelog
+ if !changelog_ready {
+ let author = control::get_deb_author()?;
++ let crate_src = match config.crate_src_path(config_path) {
++ Some(_) => "local source",
++ None => "crates.io",
++ };
+ let autogenerated_item = format!(
+- " * Package {} {} from crates.io using debcargo {}",
++ " * Package {} {} from {} using debcargo {}",
+ &crate_name,
+ &crate_version,
++ &crate_src,
+ pkgbase.debcargo_version()
+ );
+ let autogenerated_re =
+- Regex::new(r"^ \* Package (.*) (.*) from crates.io using debcargo (.*)$").unwrap();
++ Regex::new(&format!(r"^ \* Package (.*) (.*) from {} using debcargo (.*)$", &crate_src)).unwrap();
+
+ // Special-case d/changelog:
+ let (mut changelog, changelog_data) = changelog_or_new(tempdir.path())?;