jonaslu / ain

A HTTP API client for the terminal
MIT License
601 stars 13 forks source link

Interpreting `#`s in the `[Body]` section as comments scrambles the content #25

Closed kimjisena closed 6 months ago

kimjisena commented 8 months ago

In the [Body] section, I'm passing a JSON object with these details:

{
  "email": "user@gmail.com",
  "password": "0x143#",
  "user_type": "tenant"
}

ain interprets # part of the password field as the start of a comment, resulting in this body instead:

{
  "email": "user@gmail.com",
  "password": "0x143
  "user_type": "tenant"
}

This is causing the request to fail because the resulting body is not valid JSON.

jonaslu commented 8 months ago

Hi, thanks for reporting!

There is no current way of escaping # yet. I'll think of what makes more sense - escaping it like \# everywhere or leaving it within quotes.

In the meantime - since this is JSON - you can get around this particular case by using unicode escaping like so:

{
  "email": "user@gmail.com",
  "password": "0x143\u0023",
  "user_type": "tenant"
}
kimjisena commented 8 months ago

Thanks for building ain. Totally love it! I also liked your post on atomic commits.

jonaslu commented 8 months ago

Thanks for those kind words!

jonaslu commented 7 months ago

Hi!

I've thought about this and I'd like to keep things such as quoting and escaping out of the way as much as possible in ain. Quoting is only mandatory in 4 places, whitespace in arguments to executables, backend-options, $EDITOR and template-names when piping. Quoting is needed there, while the rest of the template is plain text.

So for escaping comments, I'm thinking one syntax everywhere including inside quotes is simpler than one syntax outside quotes (the majority of the cases) and yet another syntax inside quotes. More syntaxes to keep in your head. The quotes in your JSON-example are accidental because it's JSON, it's not quotes ain knows or cares about.

So, here's my idea. Things that have meaning to ain are #, ${, $( and [Headings] on a new line. All will be escaped with a backtick (`). The backtick is visually non-intrusive and unlikely to occur in elsewhere in the template. Escaping the backtick itself is only necessary if you want a literal backtick immediately followed by one of the four cases that have meaning to ain.

`# <- Escaped comment. Outputted as #
\`# <- Escaped ` immediately followed by a comment
`${ <- Escaped envvar. Outputted as ${
\`${ <- Escaped ` immediately followed by an actual envvar expression
`${ <- Escaped executable. Outputted as $(
\`${ <- Escaped ` immediately followed by an actual executable expression

And for symmetry (although escaping a section (e g \[Headers]) is already supported and we'll keep that but not mention it):

`[Headers] <- Escaped section if preceded only by whitespace on a line.
\`[Headers] <- Literal ` followed by the literal [Headers] preceded only by whitespace on a line.

The backtick enables escaping only in the cases above, everywhere else it's interpreted verbatim and no escaping of it is needed. E g abc`123 is passed as abc`123 and no \` is needed. Only in the rare cases above.

This way quoting gets out of the way as much as possible. Escaped comments are 97% of what people will use. %3 might need the escaped $( and ${ and I'll bet no-one will need the escaped backtick followed by a literal comment, envvar or executable expression.

Makes sense?

kimjisena commented 7 months ago

Hi @jonaslu!

Thanks for taking your time to think about this. Based on your explanation above, I think that approach would work okay.

jonaslu commented 6 months ago

Done in commit 4917c65