Repository: conan-io/conan
Pull request: Add format json option to conan cache path
Changelog: Feature: Add `--format=json` option to `conan cache path`.
Docs: Omit

Added `--format=json` option to the `conan cache path` command that returns a json as: 
`{"cache_path": "C:\\Users\\user\\.conan2\\p\\zlib2cad50ee1214e\\e"}`

Closes: https://github.com/conan-io/conan/issues/15599

Problem statement:
[feature] add `--format=json` to conan cache path
### What is your suggestion?

I would like for conan to be able to give me a machine readable file, rather than having to pipe the output of the `conan cache path` command.

There's nothing wrong with piping, but this makes life slightly easier on a CI system that needs to support both bash syntax and bat/cmd syntax on Windows. 

### Have you read the CONTRIBUTING guide?

- [ ] I've read the CONTRIBUTING guide

Hints from issue discussion:
We probably want this extended to `conan config home` if we end up likeing this idea
These 2 commands were intended to get the "text" output also valid for automation. So when a command returns a single string (path) as a result, the text in ``stdout`` will be equivalent to a potential ``json`` output with 
```
{"path": "/result/path"}
```
Though maybe the line ending in the terminal is disturbing the "text" flow? In that case, sure, I don't oppose to have a json formatter for these commands, good to me

Commit message: add cache path format json option

Patch to review:
diff --git a/conan/cli/commands/cache.py b/conan/cli/commands/cache.py
index e52456f3c3b..466297eab9d 100644
--- a/conan/cli/commands/cache.py
+++ b/conan/cli/commands/cache.py
@@ -1,3 +1,5 @@
+import json
+
 from conan.api.conan_api import ConanAPI
 from conan.api.model import ListPattern, MultiPackagesList
 from conan.api.output import cli_out_write, ConanOutput
@@ -9,6 +11,10 @@
 from conans.model.recipe_ref import RecipeReference
 
 
+def json_export(data):
+    cli_out_write(json.dumps({"cache_path": data}))
+
+
 @conan_command(group="Consumer")
 def cache(conan_api: ConanAPI, parser, *args):
     """
@@ -17,7 +23,7 @@ def cache(conan_api: ConanAPI, parser, *args):
     pass
 
 
-@conan_subcommand(formatters={"text": cli_out_write})
+@conan_subcommand(formatters={"text": cli_out_write, "json": json_export})
 def cache_path(conan_api: ConanAPI, parser, subparser, *args):
     """
     Show the path to the Conan cache for a given reference.
diff --git a/conans/test/integration/command_v2/test_cache_path.py b/conans/test/integration/command_v2/test_cache_path.py
index 326852237b1..111f496691b 100644
--- a/conans/test/integration/command_v2/test_cache_path.py
+++ b/conans/test/integration/command_v2/test_cache_path.py
@@ -1,3 +1,6 @@
+import json
+import os
+
 import pytest
 
 from conans.test.assets.genconanfile import GenConanfile
@@ -111,3 +114,14 @@ def test_cache_path_does_not_exist_folder():
     client.run(f"install --requires mypkg/0.1")
     client.run(f"cache path {pref} --folder build", assert_error=True)
     assert f"ERROR: 'build' folder does not exist for the reference {pref}" in client.out
+
+def test_cache_path_output_json():
+    client = TestClient()
+    conanfile = GenConanfile("mypkg", "0.1")
+    client.save({"conanfile.py": conanfile})
+    client.run("export .")
+    layout = client.exported_layout()
+    client.run("cache path mypkg/0.1 --format=json", redirect_stdout="out.json")
+    output = json.loads(client.load("out.json"))
+    cache_path = os.path.join(layout.base_folder, "e")
+    assert output == {"cache_path": f"{cache_path}"}


Code review comments:
diff_hunk: @@ -111,3 +114,14 @@ def test_cache_path_does_not_exist_folder():
     client.run(f"install --requires mypkg/0.1")
     client.run(f"cache path {pref} --folder build", assert_error=True)
     assert f"ERROR: 'build' folder does not exist for the reference {pref}" in client.out
+
+def test_cache_path_output_json():
+    client = TestClient()
+    conanfile = GenConanfile("mypkg", "0.1")
+    client.save({"conanfile.py": conanfile})
+    client.run("export .")
+    layout = client.exported_layout()
+    client.run("cache path mypkg/0.1 --format=json", redirect_stdout="out.json")
+    output = json.loads(client.load("out.json"))
+    cache_path = os.path.join(layout.base_folder, "e")
+    assert output == {"cache_path": f"{cache_path}"}
path: conans/test/integration/command_v2/test_cache_path.py
text: @user1:
```suggestion
    assert output == {"cache_path": os.path.join(layout.base_folder, "e")}
```

diff_hunk: @@ -111,3 +114,14 @@ def test_cache_path_does_not_exist_folder():
     client.run(f"install --requires mypkg/0.1")
     client.run(f"cache path {pref} --folder build", assert_error=True)
     assert f"ERROR: 'build' folder does not exist for the reference {pref}" in client.out
+
+def test_cache_path_output_json():
+    client = TestClient()
+    conanfile = GenConanfile("mypkg", "0.1")
+    client.save({"conanfile.py": conanfile})
+    client.run("export .")
+    layout = client.exported_layout()
+    client.run("cache path mypkg/0.1 --format=json", redirect_stdout="out.json")
+    output = json.loads(client.load("out.json"))
path: conans/test/integration/command_v2/test_cache_path.py
text: @user1:
```suggestion
    client.run("cache path mypkg/0.1 --format=json")
    output = json.loads(client.stdout)
```