TekWizely / bash-tpl

A smart, lightweight shell script templating engine, written in Bash
MIT License
69 stars 4 forks source link

Feature request: pass format to printf #21

Open nkh opened 1 year ago

nkh commented 1 year ago

Hi, nice work

Would it be possible to pass a format string to printf? Here's an example without

.DELIMS stmt-block="<% %>" tag="{{ }}"
<%
ENV_VARIABLE="$(printf "%17s" "$ENV_VARIABLE")"
name1="$(printf "%-13s" "$1")"
name2="$(printf "%-13s" "$2")"
%>

+---------------+---------+-------------------+
|               |         |                   |
+---------------+---------+-------------------+
| {{ $name1  }} | ....... | {{$ENV_VARIABLE}} |
| {{ $name2  }} | ....... | {{$ENV_VARIABLE}} |
+---------------+---------+-------------------+

and with,

.DELIMS tag="{{ }}"

+------------------+---------+-------------------+
|                  |         |                   |
+------------------+---------+-------------------+
| {{%-13s $1    }} | ....... | {{%17s $ENV_VARIABLE}} |
| {{%-13s $2    }} | ....... | {{%17s $ENV_VARIABLE}} |
+---------------+---------+-------------------+
TekWizely commented 1 year ago

Greetings!

This is really great idea, and I wish I had thought of it earlier.

A challenge is determining a parsable pattern that works for all current tag types:

Of course we'll also want the delimiter(s) to be configurable.

I tend to want to put the formatting at the end of the tag vs the beginning to keep the focus on the data, but having it at the beginning will be easier to parse.

When using the default delimiters <% %>, I think we need to wrap the formatting in something like |%xx| so that the % doesn't conflict, especially if the default statement delimiter % is configured.

Something like:

I do see value in a more verbose version, so maybe when the tag and statement delimiters are configured to be something other than <% %> and % we could support something like:

Essentially I see the default delimiters as |% and | and being able to configure them as % and (space) as long as they don't cause conflicts.

NOTE: I'm considering the % to be a configurable delimiter here so you could even remove it, ie:

I'm interested in your thoughts on these

Thanks for taking the time to create the discussion and for your interest in my project!

-TW

nkh commented 1 year ago

hi, what about

{{ '%s %03d' $text $figure }}

that can be passed as is to printf, no need for a new format if an old one works and in this case it fits right in.

it would also make the template below possible

<%
color=green
%>

{{"$color %s" $some_variable}}

And maybe even

{{"%$format" $variable}}
nkh commented 1 year ago

after testing a bit more I found out that the code below would work

printf -- '-%.0s' {1..13}

which means accepting

{{  -- '-%.0s' {1..13} }}
+------------------+---------+-------------------+
|                  |         |                   |
+------------------+---------+-------------------+
| {{%-13s $1    }} | ....... | {{%17s $ENV_VARIABLE}} |
| {{%-13s $2    }} | ....... | {{%17s $ENV_VARIABLE}} |
+---------------+---------+-------------------+

# ugly but parameterized
+-{{  -- '-%.0s' {1..13} }}-+---------+-{{  -- '-%.0s' {1..17} }}-+
| {{  -- '-%.0s' {1..13} }}  |           | {{  -- '-%.0s' {1..17} }} |
+-{{  -- '-%.0s' {1..13} }}-+---------+-{{  -- '-%.0s' {1..17} }}-+
| {{ '%-13s' $1            }} | ....... | {{ '%17s' $ENV_VARIABLE}} |
| {{ '%-13s' $1            }} | ....... | {{ '%17s' $ENV_VARIABLE}} |
+-{{  -- '-%.0s' {1..13} }}-+---------+-{{  -- '-%.0s' {1..17} }}-+

# and with some  pre-computation
{{ $HEADER }}
| {{ '%-13s' $1 }} | ....... | {{ '%17s' $ENV_VARIABLE}} |
| {{ '%-13s' $1 }} | ....... | {{ '%17s' $ENV_VARIABLE}} |
{{ $FOOTER}}

# and with column formats in variables

{{ $HEADER }}
| $cf1 $1 }} | ....... | {{ $cf2 $ENV_VARIABLE}} |
| $cf1 $1 }} | ....... | {{ $cf2 $ENV_VARIABLE}} |
{{ $FOOTER}}
nkh commented 3 weeks ago

@TekWizely Is the commit from last week plus the latest commit (I see no change to the docs) supposed to implement this and how much of it?

TekWizely commented 3 weeks ago

@TekWizely Is the commit from last week plus the latest commit (I see no change to the docs) supposed to implement this and how much of it?

Hi @nkh : The linked MR has a full new section added to the README : Formatting Text Tags

Essentially you can add one format specifier per tag.

Give it a read.

With the commits I just added, I think the MR is ready to merge, but I wouldn't mind if you had a few minute to take the branch for a test run?

nkh commented 3 weeks ago

@TekWizely I did but it didn't seem to match what I wrote above, a few examples in the docs would be great (maybe I was sow that day). I'll be happy to run a test.

TekWizely commented 3 weeks ago

The Formatting Text Tags section is pretty clear I think, but here's a (slightly modified) example using your initial post above:

test.tpl

.DELIMS tag="{{ }}"

+-------------------+---------+----------------------------+
|                   |         |                            |
+-------------------+---------+----------------------------+
| '{{|%-15s| $1 }}' | ....... | '{{|%24s| $ENV_VARIABLE}}' |
| '{{|%-15s| $2 }}' | ....... | '{{|%24s| $ENV_VARIABLE}}' |
+-------------------+---------+----------------------------+

usage

ENV_VARIABLE="Hello There" source <( ./bash-tpl test.tpl ) TekWizely NKH

output

+-------------------+---------+----------------------------+
|                   |         |                            |
+-------------------+---------+----------------------------+
| 'TekWizely      ' | ....... | '             Hello There' |
| 'NKH            ' | ....... | '             Hello There' |
+-------------------+---------+----------------------------+
nkh commented 3 weeks ago

@TekWizely I'll play a bit with it but I already found something that I think should be fixed, the '<' character can't be in the format, printf complains about it, it's probably a quoting problem. same with '\'

{{|<%-10s> -<-%5s>-| $2  abc 1 1  }}
nkh commented 3 weeks ago

@TekWizely

{{|-%-10s- -%5s-| $1 $2 }}

gives

1 2        --     -

which is not what printf gives, due to you merging the arguments

nkh commented 3 weeks ago

There can be no space between the tag and the printf format, which is fine, but means that only one format is possible, but is not clear in the documentation.

{{|%-10s| $1 }}
{{ |%10s| $1 }}
nkh commented 3 weeks ago

Another idea, here because I didn't want to open a ticket for something just about to be merged.

if it was possible to either parameterize the format or have a tag which expects a parameter for the format.

<%
f1=17
f2='-%17s'
%>

+-------------------+-------------------+
| {{|%-17s|}} | {{|%-17s|}} |
| {{|%-17s| $1 }} | {{|%-17s|$2}} |
| {{|%-${f1}s| $1 }} | {{|$f2|$2}} |
| {{/f1/ $1 }} | {{/$f2/ $2}} |
TekWizely commented 3 weeks ago

Hi,

{{|-%-10s- -%5s-| $1 $2 }}

The template engine is not designed to support multiple distinct values in a single tag, instead you should use two tags:

{{|-%-10s-| $1 }}{{|-%5s-| $2 }}

{{|<%-10s> -<-%5s>-| $2 abc 1 1 }}

Are < and > valid printf format specifier characters? Can you link to man page that describes them?

Looks like you're trying to wrap the printed values in arbitrary characters (<,>)? Similar to the ' I used in my example above, but I put them outside of the tags.

Non-formatting characters aren't supposed to be in the format specifier ...

Tags are meant to make it easier to put dynamic information in a text line alongside non-dynamic information, ie:

I have some static text with {{|-15s| $dynamic }} information

Bash-tpl compiles that line above into a single printf statement:

printf "%b%-15s%b\n" 'I have some static text with ' "$dynamic" ' information'

The idea of trying trying to re-use formats (i.e 2 formats to format 4 entries) in a single tag could never really work here.

At some point you're better off just coding your printf statement directly:

% printf "<%-10s> -<-%5s>-\n" "$2" abc 1 1

[edit] added content

nkh commented 2 weeks ago

Hi,

the fact that two values in a single tag isn't supported with format should be documented since the code below works

{{ $1 $2}}

this works

printf '<%s>' something

Given that the '|' needs to be just after the tag, if I'm not wrong no space is accepted, then it looks weird to have what needs to be printed outside the format, as I wrote in my first entry, anything that can be given to printf should be passed as is. If there's a good reason not too then it should be documented; changing what can and cannot be passed to printf is counter intuitive.

I'll disagree with the idea of re-using format doesn't work, there's no support for it which is completely different, and using the code below would just make me use code everywhere and not use any templating at all; that a workaround.


% printf "<%-10s> -<-%5s>-\n" "$2" abc 1 1
``

if {{|format| xxx}} works, there is little reason that {{/predefined_format_in_a_variable/ xxx}}  wouldn't.  Think of the use case where the format is pre-computed, ie the width of a column in a table of ten rows, replacing a "good looking" table template with 10 code lines is not an attractive solution.
TekWizely commented 2 weeks ago

the code below works

{{ $1 $2 }}

It does, but its treated as a single output:

printf "%s\n" "$1 $2"

Where it seems you're hoping for something like:

printf "%s%s\n" "$1" "$2"

Its not possible for to safely parse a single tag into multiple entries.

The intention of the | | is not to provide "anything that can be given to printf", its meant to supply a formatting specifier for the result of the tag's dynamic expression.

Supporting arbitrary text in the formatting specifier is not a direct goal of the feature.

However ...

if {{|format| xxx}} works, there is little reason that {{/predefined_format_in_a_variable/ xxx}} wouldn't. Think of the use case where the format is pre-computed, ie the width of a column in a table of ten rows ...

I agree that you should be able to use a variable in the format specifier, and you can:

template

% fmt='-10s'
% var='Hello'
"{{|%$fmt| $var}}"

compile

fmt='-10s'
var='Hello'
printf "%b%$fmt%b\n" '"' "$var" '"'

output

$ source <( ./bash-tpl test.tpl )

"Hello     "

But it really isn't as intuitive as it should be ... I see now that making the % optional and manually adding it into the output limits this use case ...

The code really should support:

% fmt='%-10s'
% var='Hello'
"{{|$fmt| $var}}"

And in order to support that, I really just need to pass the value between the | | as-is to printf

I'll disagree with the idea of re-using format doesn't work

You're trying to use 2 format specifiers to format 4 entries ... That idea doesn't work in any printf statement that contains other entries (i.e any template line that is more than just a single tag)

And for that line-is-a-single-tag use case, the a hard-coded printf is no more difficult to type.

Given that the '|' needs to be just after the tag, if I'm not wrong no space is accepted, then it looks weird to have what needs to be printed outside the format

I really don't know what you mean here - The '|' needs to be adjacent to the '{{' mostly so the regex can know you are intentionally adding a format specifier in the tag and not just using the '|' character for something else that you want to go into the output.

You seem to want {{|"%-13s"| $var}} instead of "{{|%-13s| $var}}" but at some point its just abusing the format specifier ... why not put the whole line in there:

{{|This is a line of text that contains "%s"| $var}}

vs

This is a line of text that contains "{{|%s| $var}}"

Such support is not a direct goal of this feature, but with the change's I'm going to make to ensure that |$fmt| works as expected, it will be possible.

It may be a few days before I get the next round of changes up ...

nkh commented 2 weeks ago

The important thing is that things are documented, and I think we have a lot of examples above, so there's no surprising behavior.

TekWizely commented 2 weeks ago

@nkh, I just got my changes up - These will likely be the last changes before I merge the MR sometime tomorrow. If you have time to give it a try, that would be great. Either way thanks for participating in my project and I hope you find this new feature helpful.