We built a Rust web framework around one idea: everything about an endpoint belongs together. Feedback wanted

Actually, for the url parts, what our design is use APP.url("formatted_string") to form an instance of endpoint, that endpoint instance is internally linked to the static app instance named APP, and the attributes are things registered to that url instance. That's the reason I keep that design and I don't know whether this explanation is good or not. What that field actually is, is a field which plug in an instance of URL but not simply the string since we need to know which APP you register that URL (we are planning to support multi-app)

For function/endpoint name, you may use _ at that place if you want to make it anonymous (I keep that field just to make the endpoint and middleware looks identical)

For pub, it is just a reserved field for future integration of internal calls (which is still In planning stage, and that keyword is optional)

So, actually, an minimal endpoint looks like

endpoint!{ 
    APP.url("/simple") 

    _ <HTTP> { 
        akari_render!("index.html") 
    } 
} 

I am thinking if it is a good idea to add "flavour" to syntax and let user to choose whether to use the original, rusty custom macro or even more rusty - direct attribute macro. That will be easy to impl though (because before 0.7 version, which is the actual ancestor of our framework did in proc_macro), its syntax looks line

#[url(APP.reg_from(&[TEST_URL.clone(), LitUrl("get")]), config=[HttpSafety::new().with_allowed_method(HttpMethod::GET)])]  
async fn get_only() -> HttpResponse { 
    text_response("Get only")  
} 

#[url(APP.reg_from(&[TEST_URL.clone(), LitUrl("post")]), config=[HttpSafety::new().with_allowed_methods(vec![HttpMethod::POST])])]  
async fn post_only() -> HttpResponse { 
    text_response("Post only")  
} 

Maybe we can simplify the URL reg into the new way and keep that proc macro (which looks like this?) as one of the option user can choose

#[endpoint(APP.url("/test/reg"), config=[HttpSafety::new().with_allowed_method(HttpMethod::GET)])]  
async fn get_only() -> HttpResponse { 
    text_response("Get only")  
} 

For middleware, we use UpperCamelCase because the struct will be generated when the macro is expanded (so fundamentally, it is not a function). Do you think it will be better if we use small_snake_case? Or actually we just directly let the user choose (since the naming does not matter expansion)

Actually I am planning to add ways for user to customise the syntax, and I am planning to add price macro back lol, I did that in crates.io: Rust Package Registry. Do you think this will be better?

I am thinking how to make a balance between preserving the multi-protocol feature, novel syntax and the user's expectation because I don't want it looks too similar to traditional things lol

Then it should be possible to expand endpoint! macro to use a special match arm app = APP where the default value is APP so no need to write it explicitly. For my use-cases most of the time one main app is good enough, multiple Tokio instances ect. i have used only for toy projects.

Them maybe middleware! macro can be updated with an arm name = some_name_that_can_be_used and use there anonymous functions as well. For me, if the goal is to minimize boilerplate, it seems there is no need for function names if they can not be used anyways.

I would avoid that. I believe that framework designers must make a choice. Some will agree with it and others will not. Mixed "flavour" would confuse users, some would use mixed "flavour" in one project and that would look like a mess. I've experienced that type of problems in other projects.

Comparing ancestors decorator to "new" macro, i think new one looks cleaner. Tracking errors might be harder due to the way macro gets expanded (though i did not look at what magic it does), but for my taste looks cleaner.

I do not like long one-liners written in one line. I still prefer if code keeps in 80-100 chars per line.

The way i would like those decorators to be is:

#[endpoint = "/test/reg"]
#[app = APP]
#[config = ..]
#[middleware = [ .. ] ]
async fn get_only() -> HttpResponse { 
    text_response("Get only")  
} 

As far as i remember, proc_macro gives those as input combined if they are in same group. I wrote the most concise syntax i would prefer, but maybe the way use statement is used to import macros it is not possible to write so short. Maybe it would have to start with #[hotaru]as prefix. But the availability of short syntax would be so much better. I usually do not use different crates with same macro/struct names.

I think user should not be allowed to choose, framework designer must decide on convention used. From user's perspective a macro expands like magic, i think initially user should not be bothered by that, thus if struct is generated under the hood, then as a user, i would like that a framework designer takes the naming convention burden on himself. I have seen some Rust crates that does that and use #![allow(non_camel_case_types)] before each macro-generated-type to silence warnings. This would make macro from users perspective look seamless, but from developers the code one works with is more ugly. Though working with macros is not "non-ugly" to say the least.

As from users perspective i think user should have an experience as close to Rust code as possible. If he defines what is presented as function, then naming should follow Rusts snake_case, if user is presented that he defines struct, then PascalCase.

While you can do things however you like, keep in mind that people do like familiar things. If things are too alien without good reason to be so (very concise, easy to grasp, etc.), nobody wants to learn or even look into them.

I think of the following way, and I wonder whether you think that will be better? I still wondering about the following 4 questions.

#[endpoint(
    url = auto!("/path/<var>"), 
    middleware = [Auth, CSessionRW, .. ], 
    config = [GET, HttpSafety::new()] 
)] 
fn get_only(req: HTTP) -> { 
    akari_render!("temp.html") 
} 

auto!() means register url directly to app named APP. If app named not APP, we just directly use OtherNameAPP.url(...)

Where I am thinking

a. Whether auto!() is a good name? auto!() expands into APP.url(...)

b. Do we explicitly say url = ... or just make the first input as the URL instance?

c. In function do we write in fn xxx (req: HTTP) or fn xxx -> HTTP since HttpResponse does not exist in newer version (it has been abstracted)

We write fn xxx<HTTP> or fn xxx<HTTP>() or fn xxx<HTTP>(req) where default request variable name as req

d. Whether to use separate macros? But it looks like things are in different places so I still may want a better formatted, single macro.

Where middleware, just easy, #[middleware] and allowing to use snake_case

I will keep our DSL for future integration with other languages I believe (such as the Akari project). But by default it will be macro syntax and we encourage to use that in Rust. DSL mode maybe will be just for my use (since I like that syntax a lot lol). A feature (which maybe called trans must be enabled if we use DSL and if that is triggered, only the DSL can be used) then it will be clear

A rule of thumb is, does said "HTTP" go into function or out of it? The former option works when it is input, the latter when it is returned. Neither input nor output but a configuration would fit a generic fn xxx<HTTP>().

HTTP owns both Request and Response struct inside it (so actually it does act as input and output. (This is what the macro actually did)

But putting in <> seems to be a good way and make the new syntax the same as my DSL syntax. I believe I will take that, thank you

This syntax looks cleaner.

Is there some technical challenge that does not allow to avoid use of App.url(..) syntax? Why is it not possible to just have special arm app = APP and if omitted, then APP is used as default?

I would accept both ways as good approaches. URL most probably is mandatory anyways, so it can always be first and mandatory argument, makes syntax shorter. If explicit url = is used, then it makes macro easy to understand, like multiple key = value knobs that set up endpoint and anyone who sees the code for the first time understands right away what it is for.

I'm not so sure if that is a "wise" choice... I mean... what i have seen is that usually a few main/core people are the main drivers for a library/framework code, so IMHO they should somewhat enjoy what they are doing, so that there is a natural motivation to work on it.
If this is the case, then maybe you should take a path where your DSL is the way the framework is used. I would go that path and then either succeed or fail, to me it still would be better than writing something i do not like or believe in.

In the end i would like that you are the one that makes the choice and stands by it. My goal here was just to answer the call, share my thoughts on the subject, what i think is good/bad and the reasoning why i think so. Other than that, take it as some random persons thoughts on the internet.