Repository: mitsuhiko/minijinja
Pull request #633 (2024-11-04 19:33:13)

Enable all features by default in minijinja-cli
The doc says that all features are enabled by default, but it's not true: by default (and in the release binaries too) ini format support and shell completion aren't enabled + ini format is actually partially broken even when enabled (can't be specified in the command line).

This PR fixes the issue.

KonishchevDmitry: @mitsuhiko, I've actually faced with yet another problem with ini format. I have a `*.conf` file which I use in both shell scripts and for template generation. And in this file all variables are uppercase (as it often used in `*.env` files). But `minijinja-cli` loads them in lowercase, which was unexpected for me.

Is it intentional? Personally, I would prefer that my variables are passed as-is to the templates. It can be fixed by using `configparser::ini::Ini::new_cs()` instead of `configparser::ini::Ini::new()`. I haven't done this in this PR, but can add the change if you don't mind. At least if would be consistent with `--env` option, which passes environment variables as-is.
mitsuhiko: Yes, this should be fixed. Definitely unexpected and unintended. Could you also add a test for that?

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index af0dbe0..6b76f31 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,10 @@ All notable changes to MiniJinja are documented here.
   rendering.  #627
 - Syntax errors caused by the lexer now include the correct position of
   the error.  #630
+- `minijinja-cli` now has all features enabled by default as documented
+  (that means also shell completion and ini).  #633
+- `minijinja-cli` now does not convert INI files to lowercase anymore.  This was
+  an unintended behavior.  #633
 
 ## 2.4.0
 
diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md
index 50dffde..5086176 100644
--- a/COMPATIBILITY.md
+++ b/COMPATIBILITY.md
@@ -156,4 +156,12 @@ are available in MiniJinja or behave the same.
 ## Filters
 
 MiniJinja supports many common Jinja2 filters but leaves out some.  For instance
-some string formatting filters like `|xmlattr` or `|urlize` are missing.
+some string formatting filters like `|xmlattr` or `|urlize` are missing.  Additionally
+some filters do not support all the same arguments or only support some arguments
+as positional ones.
+
+It's a soft goal to increase the number of filters that are supported and to
+match the behavior of Jinja2 as close as possible but there are some situations
+where it might be acceptable to deviate.  For instance there are some filters
+which in Jinja2 support an `attribute` argument.  Most of those filters currently
+do not support that argument in MiniJinja.
diff --git a/minijinja-cli/Cargo.toml b/minijinja-cli/Cargo.toml
index a7d82c1..2879694 100644
--- a/minijinja-cli/Cargo.toml
+++ b/minijinja-cli/Cargo.toml
@@ -12,7 +12,7 @@ readme = "README.md"
 rust-version = "1.65"
 
 [features]
-default = ["toml", "yaml", "querystring", "cbor", "datetime", "json5", "repl", "unicode", "contrib", "preserve_order"]
+default = ["toml", "yaml", "querystring", "cbor", "datetime", "json5", "repl", "completions", "unicode", "ini", "contrib", "preserve_order"]
 yaml = ["serde_yml"]
 querystring = ["serde_qs"]
 cbor = ["ciborium"]
diff --git a/minijinja-cli/README.md b/minijinja-cli/README.md
index 6cea69a..9aead4c 100644
--- a/minijinja-cli/README.md
+++ b/minijinja-cli/README.md
@@ -85,7 +85,7 @@ The following formats are supported:
 - `toml` (`*.toml`): TOML
 - `cbor` (`*.cbor`): CBOR
 - `querystring` (`*.qs`): URL encoded query strings
-- `ini` (`*.ini`, `*.config`, `*.properties`): text only INI files
+- `ini` (`*.ini`, `*.conf`, `*.config`, `*.properties`): text only INI files
 
 For most formats there is a pretty straightforward mapping into the template
 context.  The only exception to this is currently INI files where sections are
@@ -93,7 +93,7 @@ effectively mandatory.  If keys are placed in the unnamed section, the second
 is renamed to `default`.  You can use `--select` to make a section be implied:
 
 ```
-minijinja-cli template.j2 input.ini --section default
+minijinja-cli template.j2 input.ini --select default
 ```
 
 Note that not all formats support all input types.  For instance querystring
diff --git a/minijinja-cli/src/cli.rs b/minijinja-cli/src/cli.rs
index c24e74f..847e79e 100644
--- a/minijinja-cli/src/cli.rs
+++ b/minijinja-cli/src/cli.rs
@@ -112,7 +112,7 @@ fn load_data(
         #[cfg(feature = "ini")]
         "ini" => {
             let contents = String::from_utf8(contents).context("invalid utf-8")?;
-            let mut config = configparser::ini::Ini::new();
+            let mut config = configparser::ini::Ini::new_cs();
             config
                 .read(contents)
                 .map_err(|msg| anyhow::anyhow!("could not load ini: {}", msg))?;
diff --git a/minijinja-cli/src/command.rs b/minijinja-cli/src/command.rs
index 1a9c6f0..bee8e32 100644
--- a/minijinja-cli/src/command.rs
+++ b/minijinja-cli/src/command.rs
@@ -15,7 +15,11 @@ pub static SUPPORTED_FORMATS: &[(&str, &str, &[&str])] = &[
     #[cfg(feature = "cbor")]
     ("cbor", "CBOR", &["cbor"]),
     #[cfg(feature = "ini")]
-    ("ini", "INI / Config", &["ini", "config", "properties"]),
+    (
+        "ini",
+        "INI / Config",
+        &["ini", "conf", "config", "properties"],
+    ),
     #[cfg(not(feature = "json5"))]
     ("json", "JSON", &["json"]),
     #[cfg(feature = "json5")]
@@ -79,15 +83,17 @@ pub(super) fn make_command() -> Command {
                     [env var: MINIJINJA_FORMAT]"))
                 .value_parser([
                     "auto",
+                    #[cfg(feature = "cbor")]
+                    "cbor",
+                    #[cfg(feature = "ini")]
+                    "ini",
                     "json",
                     #[cfg(feature = "querystring")]
                     "querystring",
-                    #[cfg(feature = "yaml")]
-                    "yaml",
                     #[cfg(feature = "toml")]
                     "toml",
-                    #[cfg(feature = "cbor")]
-                    "cbor",
+                    #[cfg(feature = "yaml")]
+                    "yaml",
                 ]),
             arg!(-a --autoescape <MODE> "Reconfigures autoescape behavior")
                 .long_help("\


Test patch:
diff --git a/minijinja-cli/tests/snapshots/test_basic__long_help.snap b/minijinja-cli/tests/snapshots/test_basic__long_help.snap
index d7de019..4f6bbca 100644
--- a/minijinja-cli/tests/snapshots/test_basic__long_help.snap
+++ b/minijinja-cli/tests/snapshots/test_basic__long_help.snap
@@ -44,6 +44,9 @@ Arguments:
           defaults to '-' which means the template is loaded from stdin.  When the format is set to
           'auto' which is the default, the extension of the filename is used to detect the format.
           
+          This argument can be set to an empty string when --template is provided to allow a data
+          file to be supplied.
+          
           [default: -]
 
   [DATA_FILE]
@@ -70,7 +73,7 @@ Options:
           
           - auto
           - cbor (CBOR): *.cbor
-          - ini (INI / Config): *.ini, *.config, *.properties
+          - ini (INI / Config): *.ini, *.conf, *.config, *.properties
           - json (JSON / JSON5): *.json, *.json5
           - querystring (Query String / Form Encoded): *.qs
           - toml (TOML): *.toml
@@ -84,7 +87,7 @@ Options:
           
           [env var: MINIJINJA_FORMAT]
           
-          [possible values: auto, json, querystring, yaml, toml, cbor]
+          [possible values: auto, cbor, ini, json, querystring, toml, yaml]
 
   -D, --define <EXPR>
           This defines an input variable for the template.  This is used in addition to the input
diff --git a/minijinja-cli/tests/snapshots/test_basic__short_help.snap b/minijinja-cli/tests/snapshots/test_basic__short_help.snap
index ebf08ae..62f42df 100644
--- a/minijinja-cli/tests/snapshots/test_basic__short_help.snap
+++ b/minijinja-cli/tests/snapshots/test_basic__short_help.snap
@@ -20,8 +20,8 @@ Arguments:
 
 Options:
       --config-file <PATH>          Alternative path to the config file.
-  -f, --format <FORMAT>             The format of the input data [possible values: auto, json,
-                                    querystring, yaml, toml, cbor]
+  -f, --format <FORMAT>             The format of the input data [possible values: auto, cbor, ini,
+                                    json, querystring, toml, yaml]
   -D, --define <EXPR>               Defines an input variable (key=value / key:=json_value)
   -t, --template <TEMPLATE_STRING>  Render a string template
   -o, --output <FILENAME>           Path to the output file [default: -]
diff --git a/minijinja-cli/tests/test_basic.rs b/minijinja-cli/tests/test_basic.rs
index 48c9764..0befa9e 100644
--- a/minijinja-cli/tests/test_basic.rs
+++ b/minijinja-cli/tests/test_basic.rs
@@ -35,6 +35,7 @@ macro_rules! bind_common_filters {
             r"\(in .*minijinja-testfile--.*?:(\d+)\)",
             "(in [TEMPLATE]:$1)",
         );
+        settings.add_filter(r"\bminijinja-cli\.exe\b", "minijinja-cli");
         let _guard = settings.bind_to_scope();
     };
 }
@@ -212,6 +213,43 @@ fn test_toml() {
     "###);
 }
 
+#[test]
+#[cfg(feature = "ini")]
+fn test_ini_casing() {
+    let input = file_with_contents_and_ext("[section]\nFOO = bar", ".ini");
+    let tmpl = file_with_contents(r#"Hello {{ section.FOO }}!"#);
+
+    assert_cmd_snapshot!(
+        cli()
+            .arg(tmpl.path())
+            .arg(input.path()),
+        @r###"
+    success: true
+    exit_code: 0
+    ----- stdout -----
+    Hello bar!
+
+    ----- stderr -----
+    "###);
+
+    let mut tmpl = NamedTempFile::new().unwrap();
+    tmpl.write_all(br#"Hello {{ FOO }}!"#).unwrap();
+
+    assert_cmd_snapshot!(
+        cli()
+            .arg("--select=section")
+            .arg(tmpl.path())
+            .arg(input.path()),
+        @r###"
+    success: true
+    exit_code: 0
+    ----- stdout -----
+    Hello bar!
+
+    ----- stderr -----
+    "###);
+}
+
 #[test]
 #[cfg(feature = "querystring")]
 fn test_querystring() {
@@ -808,6 +846,7 @@ fn test_load_config() {
     feature = "yaml",
 ))]
 fn test_help() {
+    bind_common_filters!();
     assert_cmd_snapshot!("short_help", cli().arg("--help"));
     assert_cmd_snapshot!("long_help", cli().arg("--long-help"));
 }