Repository: rinja-rs/askama (language: rust)

Issue:
0.12.1 only allow lower-case variable name in template
I don't know if I asked a stupid question, but I spend one day to find it ! my rust version is 1.74.0, I run it on my win11 and WSL ubuntu 22.04, see the same error below
```rust
use askama::Template;

#[derive(Template)] 
#[template(path = "hello.html")]
struct HelloTemplate<'a> {
    nAme: &'a str, 
}
fn main() {
    let hello = HelloTemplate { nAme: "world" }; 
    println!("{}", hello.render().unwrap());
}
```
```html
Hello, {{ nAme }}!
```
```toml
[package]
name = "hello"
version = "0.1.0"
edition = "2021"

[dependencies]
askama="0.12.1"
```
[xxx.DESKTOP-ABCDE12] ⮞ cargo run -p hello
   Compiling hello v0.1.0 (C:\xx\xx\xx\hello)
error[E0425]: cannot find value `nAme` in this scope
 --> hello\src\main.rs:3:10
  |
3 | #[derive(Template)]
  |          ^^^^^^^^
  |
  = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)
help: you might have meant to use the available field
  |
3 | #[derive(self.Template)]
  |          +++++

For more information about this error, try `rustc --explain E0425`.
error: could not compile `hello` (bin "hello") due to previous error



Discussion / hints:
I'm new to axum, I have learn the docs https://djc.github.io/askama/askama.html, but it seems not have the request of lower case
I suggest you get the macro expansion via Rust Analyzer or via the [debugging](https://djc.github.io/askama/debugging.html) features. I guess the code generator applies some heuristics that take variable name casing into account in a way that might be surprising.
I just find that, the var name with upper case => Path(["nAme"]), the var name with lower case => Var("na")) Var("me")), but I 
 don't know it is the purpose of Askama or not ? I don't find the code which product this difference  either.

```html
<h1>Hello, {{ na }} {{ nAme }} {{ me }} !</h1>
```
```rust
use askama::Template;

#[derive(Template)]
#[template(path = "hello.html", print = "all")]
struct HelloTemplate<'a> {
    na: &'a str,
    nAme: &'a str,
    me: &'a str,
}

fn main() { println!("hello world"); }
```
```shell
[aaaa.DESKTOP-ABCDE] ⮞ cargo run -p hello
   Compiling hello v0.1.0 (C:\aaa\aaa\aaa\hello)
[Lit(Lit { lws: "", val: "<h1>Hello,", rws: " " }), Expr(Ws(None, None), Var("na")), Lit(Lit { lws: " ", val: "", rws: "" }), Expr(Ws(None, None), Path(["nAme"])), Lit(Lit { lws: " ", val: "", rws: "" }), Expr(Ws(None, None), Var("me")), Lit(Lit { lws: " ", val: "!</h1>", rws: "" })]
......
error[E0425]: cannot find value `nAme` in this scope
 --> hello\src\main.rs:3:10
  |
3 | #[derive(Template)]
  |          ^^^^^^^^
  |
  = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)
help: you might have meant to use the available field
  |
3 | #[derive(self.Template)]
  |          +++++

For more information about this error, try `rustc --explain E0425`.
error: could not compile `hello` (bin "hello") due to previous error
```
I ran in to the same issue. For some reason if you have uppercase, the code it generates fails. See this example

```
            expr0 = &::askama::MarkupDisplay::new_unsafe(&(self.title), ::askama::Html),
            expr1 = &::askama::MarkupDisplay::new_unsafe(&(xxxYYY), ::askama::Html),
```

with uppercase, it doesnt add self. in front of the variable name. I couldn't not find any mention in the documentation for this. It's kind of an annoying limitation if intended.

if I make YYY to lower case, it generates the following

```
            expr0 = &::askama::MarkupDisplay::new_unsafe(&(self.title), ::askama::Html),
            expr1 = &::askama::MarkupDisplay::new_unsafe(&(self.xxxyyy), ::askama::Html),
```

You can get around it by just doing this in the html template

`<h1>{{ self.xxxYYY }}</h1>`

any reason for this behavior?


Fix patch:
diff --git a/askama_parser/src/lib.rs b/askama_parser/src/lib.rs
index e6f133c37..1cb0e870f 100644
--- a/askama_parser/src/lib.rs
+++ b/askama_parser/src/lib.rs
@@ -349,9 +349,9 @@ fn path_or_identifier(i: &str) -> ParseResult<'_, PathOrIdentifier<'_>> {
     let rest = rest.as_deref().unwrap_or_default();
 
     // The returned identifier can be assumed to be path if:
-    // - Contains both a lowercase and uppercase character, i.e. a type name like `None`
-    // - Doesn't contain any lowercase characters, i.e. it's a constant
-    // In short, if it contains any uppercase characters it's a path.
+    // - it is an absolute path (starts with `::`), or
+    // - it has multiple components (at least one `::`), or
+    // - the first letter is uppercase
     match (root, start, rest) {
         (Some(_), start, tail) => {
             let mut path = Vec::with_capacity(2 + tail.len());
@@ -360,7 +360,7 @@ fn path_or_identifier(i: &str) -> ParseResult<'_, PathOrIdentifier<'_>> {
             path.extend(rest);
             Ok((i, PathOrIdentifier::Path(path)))
         }
-        (None, name, []) if !name.contains(char::is_uppercase) => {
+        (None, name, []) if name.chars().next().map_or(true, |c| c.is_lowercase()) => {
             Ok((i, PathOrIdentifier::Identifier(name)))
         }
         (None, start, tail) => {


Test patch:
diff --git a/testing/tests/simple.rs b/testing/tests/simple.rs
index f0f0a26bf..ba806e222 100644
--- a/testing/tests/simple.rs
+++ b/testing/tests/simple.rs
@@ -484,3 +484,23 @@ fn test_num_literals() {
         "[90, -90, 90, 2, 56, 240, 10.5, 10.5, 100000000000, 105000000000]",
     );
 }
+
+#[allow(non_snake_case)]
+#[derive(askama::Template)]
+#[template(source = "{{ xY }}", ext = "txt")]
+struct MixedCase {
+    xY: &'static str,
+}
+
+/// Test that we can use mixed case in variable names
+///
+/// We use some heuristics to distinguish paths (`std::str::String`) from
+/// variable names (`foo`). Previously, this test would fail because any
+/// name containing uppercase characters would be considered a path.
+///
+/// https://github.com/djc/askama/issues/924
+#[test]
+fn test_mixed_case() {
+    let template = MixedCase { xY: "foo" };
+    assert_eq!(template.render().unwrap(), "foo");
+}