Repository: cargo-generate/cargo-generate
Pull request #666 (2022-06-25 19:48:35)

Return the path of the generated project from the `generate` function
Hello there!

This PR aims to return the path of the generated project from the `generate` function.
Allowing the user of the public API to re-use the path when they can't guess the folder name of the generated project.

The changes there are quite big for this not so big goal, I you have any suggestions to make it clearer.

yozhgoor: > In general; I like it - but **why** do you need the path to be returned? It's not used nor explained anywhere - not even in the tests.

My use case is that I want to create the template in a temporary directory. But I can't guess the generated directory and using `GenerateArgs.destination` directly means that I'm a directory level to far (`base/tmp_dir/template` instead of `base/template`). The idea is to return the path directly from the `generate` so I can re-use it directly so I can add files after creating the template. 
yozhgoor: I reduced to a single test, but I can split them into two different test if you prefer that.
sassman: only a fix for the test is required:

```plain
test library::it_allows_generate_call_with_public_args_and_returns_generated_path ... FAILED
failures:
---- library::it_allows_generate_call_with_public_args_and_returns_generated_path stdout ----
🔧   Basedir: /private/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/.tmpf8jHPV ...
🔧   Generating template ...
🔧   Moving generated files into: `/private/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/.tmpf8jHPV/foobar_project`...
💡   Initializing a fresh Git repository
✨   Done! New project created /private/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/.tmpf8jHPV/foobar_project
thread 'library::it_allows_generate_call_with_public_args_and_returns_generated_path' panicked at 'assertion failed: `(left == right)`
  left: `"/private/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/.tmpf8jHPV/foobar_project"`,
 right: `"/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/.tmpf8jHPV/foobar_project"`', tests/integration/library.rs:49:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```
yozhgoor: > only a fix for the test is required:

I'm on it, not really sure how I can fix this. This is really weird to me since I use the same variable for the two paths.


yozhgoor: I'm not proud of the tests, I need to investigate a bit more.
I'm gonna draft for the moment (need to go to the kid's fancy fair :dancers:).
yozhgoor: > macOS creates several folders of caches and temporary items to speed up the system. Those under your control are located in /Library/Caches and /private/tmp. But system-related cache files are hidden in the /private/var folder on Mac.

Seems linked to the caching system from macOS, even my dirty try who added the `/private` part of the path by myself doesn't work. 

Patch:
diff --git a/src/app_config.rs b/src/app_config.rs
index 94d79e6..232ebcb 100644
--- a/src/app_config.rs
+++ b/src/app_config.rs
@@ -58,6 +58,7 @@ impl TryFrom<&Path> for AppConfig {
     }
 }
 
+/// # Panics
 pub fn app_config_path(path: &Option<PathBuf>) -> Result<PathBuf> {
     path.as_ref()
         .map(|p| p.canonicalize().unwrap())
diff --git a/src/favorites.rs b/src/favorites.rs
index 0b5fdb6..e8c209e 100644
--- a/src/favorites.rs
+++ b/src/favorites.rs
@@ -1,13 +1,15 @@
 //! Module dealing with <favorite> arg passed to cargo-generate
 
 use crate::{
-    app_config::{AppConfig, FavoriteConfig},
+    app_config::{app_config_path, AppConfig, FavoriteConfig},
     emoji, GenerateArgs,
 };
 use anyhow::Result;
 use console::style;
 
-pub fn list_favorites(app_config: &AppConfig, args: &GenerateArgs) -> Result<()> {
+pub fn list_favorites(args: &GenerateArgs) -> Result<()> {
+    let app_config: AppConfig = app_config_path(&args.config)?.as_path().try_into()?;
+
     let data = {
         let mut d = app_config
             .favorites
diff --git a/src/lib.rs b/src/lib.rs
index a21b0d1..b5fbb31 100755
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -41,12 +41,13 @@ mod template_filters;
 mod template_variables;
 mod user_parsed_input;
 
+pub use crate::app_config::{app_config_path, AppConfig};
+pub use crate::favorites::list_favorites;
 pub use args::*;
 
 use anyhow::{anyhow, bail, Context, Result};
 use config::{locate_template_configs, Config, CONFIG_FILE_NAME};
 use console::style;
-use favorites::list_favorites;
 use git::DEFAULT_BRANCH;
 use hooks::{execute_post_hooks, execute_pre_hooks};
 use ignore_me::remove_dir_files;
@@ -68,19 +69,14 @@ use tempfile::TempDir;
 
 use crate::template_variables::load_env_and_args_template_values;
 use crate::{
-    app_config::{app_config_path, AppConfig},
     project_variables::ConversionError,
     template_variables::{CrateType, ProjectName},
 };
 
 /// # Panics
-pub fn generate(mut args: GenerateArgs) -> Result<()> {
+pub fn generate(mut args: GenerateArgs) -> Result<PathBuf> {
     let app_config: AppConfig = app_config_path(&args.config)?.as_path().try_into()?;
 
-    if args.list_favorites {
-        return list_favorites(&app_config, &args);
-    }
-
     if args.ssh_identity.is_none()
         && app_config.defaults.is_some()
         && app_config.defaults.as_ref().unwrap().ssh_identity.is_some()
@@ -157,7 +153,8 @@ pub fn generate(mut args: GenerateArgs) -> Result<()> {
         style("New project created").bold(),
         style(&project_dir.display()).underlined()
     );
-    Ok(())
+
+    Ok(project_dir)
 }
 
 fn prepare_local_template(
diff --git a/src/main.rs b/src/main.rs
index 3f279f4..ed19ef3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,10 +1,15 @@
 use anyhow::Result;
-use cargo_generate::{generate, Cli};
+use cargo_generate::{generate, list_favorites, Cli};
 use clap::Parser;
 
 fn main() -> Result<()> {
     let Cli::Generate(args) = Cli::parse();
-    generate(args)?;
+
+    if args.list_favorites {
+        list_favorites(&args)?;
+    } else {
+        generate(args)?;
+    }
 
     Ok(())
 }


Test patch:
diff --git a/tests/integration/library.rs b/tests/integration/library.rs
index b5f4d51..d63e071 100644
--- a/tests/integration/library.rs
+++ b/tests/integration/library.rs
@@ -2,20 +2,10 @@ use crate::helpers::project_builder::tmp_dir;
 use cargo_generate::{generate, GenerateArgs, TemplatePath, Vcs};
 
 #[test]
-fn it_allows_generate_call_with_public_args() {
-    let template = tmp_dir()
-        .file(
-            "Cargo.toml",
-            r#"[package]
-name = "{{project-name}}"
-description = "A wonderful project"
-version = "0.1.0"
-"#,
-        )
-        .init_git()
-        .build();
+fn it_allows_generate_call_with_public_args_and_returns_the_generated_path() {
+    let template = tmp_dir().init_default_template().init_git().build();
 
-    let dir = tmp_dir().build();
+    let dir = tmp_dir().build().root.into_path();
 
     let args_exposed: GenerateArgs = GenerateArgs {
         template_path: TemplatePath {
@@ -39,15 +29,19 @@ version = "0.1.0"
         ssh_identity: None,
         define: vec![],
         init: false,
-        destination: None,
+        destination: Some(dir.clone()),
         force_git_init: false,
         allow_commands: false,
     };
-    // need to cd to the dir as we aren't running in the cargo shell.
-    assert!(std::env::set_current_dir(&dir.root).is_ok());
-    assert!(generate(args_exposed).is_ok());
 
-    assert!(dir
-        .read("foobar_project/Cargo.toml")
-        .contains("foobar_project"));
+    assert_eq!(
+        generate(args_exposed).expect("cannot generate project"),
+        dir.join("foobar_project")
+    );
+
+    assert!(
+        std::fs::read_to_string(&dir.join("foobar_project").join("Cargo.toml"))
+            .expect("cannot read file")
+            .contains("foobar_project")
+    );
 }