diff --git a/crates/tuono/Cargo.toml b/crates/tuono/Cargo.toml index a3daaff2..487348dd 100644 --- a/crates/tuono/Cargo.toml +++ b/crates/tuono/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tuono" version = "0.19.0" -edition = "2021" +edition = "2024" authors = ["V. Ageno "] description = "Superfast React fullstack framework" homepage = "https://tuono.dev" diff --git a/crates/tuono/src/app.rs b/crates/tuono/src/app.rs index 81dda92a..efbdc1e4 100644 --- a/crates/tuono/src/app.rs +++ b/crates/tuono/src/app.rs @@ -1,14 +1,14 @@ use crate::mode::Mode; use crate::route::Route; -use glob::glob; use glob::GlobError; +use glob::glob; use http::Method; use std::collections::hash_set::HashSet; -use std::collections::{hash_map::Entry, HashMap}; +use std::collections::{HashMap, hash_map::Entry}; use std::fs::File; use std::io; -use std::io::prelude::*; use std::io::BufReader; +use std::io::prelude::*; use std::path::Path; use std::path::PathBuf; use std::process::Child; @@ -153,9 +153,9 @@ impl App { if let Err(_e) = rust_listener { eprintln!("Error: Failed to bind to port {}", config.server.port); eprintln!( - "Please ensure that port {} is not already in use by another process or application.", - config.server.port - ); + "Please ensure that port {} is not already in use by another process or application.", + config.server.port + ); std::process::exit(1); } @@ -167,9 +167,9 @@ impl App { if let Err(_e) = vite_listener { eprintln!("Error: Failed to bind to port {}", vite_port); eprintln!( - "Please ensure that port {} is not already in use by another process or application.", - vite_port - ); + "Please ensure that port {} is not already in use by another process or application.", + vite_port + ); std::process::exit(1); } } @@ -219,7 +219,9 @@ impl App { Ok(config) => self.config = Some(config), Err(error) => { match error.kind() { - io::ErrorKind::NotFound => eprintln!("Failed to read config. Please run `npm install` to generate automatically."), + io::ErrorKind::NotFound => eprintln!( + "Failed to read config. Please run `npm install` to generate automatically." + ), _ => { error!("Failed to read config with the following error:"); error!("{}", error); @@ -453,19 +455,21 @@ mod tests { .into_iter() .for_each(|(path, expected_has_server_handler)| { if expected_has_server_handler { - assert!(app - .route_map - .get(path) - .expect("Failed to get route path") - .axum_info - .is_some()) + assert!( + app.route_map + .get(path) + .expect("Failed to get route path") + .axum_info + .is_some() + ) } else { - assert!(app - .route_map - .get(path) - .expect("Failed to get route path") - .axum_info - .is_none()) + assert!( + app.route_map + .get(path) + .expect("Failed to get route path") + .axum_info + .is_none() + ) } }) } diff --git a/crates/tuono/src/cli.rs b/crates/tuono/src/cli.rs index eabe3d3a..f0e66f81 100644 --- a/crates/tuono/src/cli.rs +++ b/crates/tuono/src/cli.rs @@ -1,5 +1,5 @@ use clap::{Parser, Subcommand}; -use tracing::{span, Level}; +use tracing::{Level, span}; use crate::commands::{build, dev, new}; use crate::mode::Mode; diff --git a/crates/tuono/src/commands/build.rs b/crates/tuono/src/commands/build.rs index 234911b0..35d529b0 100644 --- a/crates/tuono/src/commands/build.rs +++ b/crates/tuono/src/commands/build.rs @@ -1,4 +1,4 @@ -use fs_extra::dir::{copy, CopyOptions}; +use fs_extra::dir::{CopyOptions, copy}; use spinners::{Spinner, Spinners}; use std::path::PathBuf; use std::thread::sleep; diff --git a/crates/tuono/src/commands/dev.rs b/crates/tuono/src/commands/dev.rs index 504307b3..37f8c0f1 100644 --- a/crates/tuono/src/commands/dev.rs +++ b/crates/tuono/src/commands/dev.rs @@ -6,7 +6,7 @@ use watchexec_supervisor::command::{Command, Program}; use miette::{IntoDiagnostic, Result}; use watchexec::Watchexec; use watchexec_signals::Signal; -use watchexec_supervisor::job::{start_job, Job}; +use watchexec_supervisor::job::{Job, start_job}; use crate::source_builder::SourceBuilder; use console::Term; diff --git a/crates/tuono/src/commands/new.rs b/crates/tuono/src/commands/new.rs index 0e4b30de..22b3ce9f 100644 --- a/crates/tuono/src/commands/new.rs +++ b/crates/tuono/src/commands/new.rs @@ -3,7 +3,7 @@ use reqwest::blocking; use reqwest::blocking::Client; use serde::Deserialize; use std::env; -use std::fs::{self, create_dir, File, OpenOptions}; +use std::fs::{self, File, OpenOptions, create_dir}; use std::io::{self, prelude::*}; use std::path::{Path, PathBuf}; @@ -100,14 +100,18 @@ pub fn create_new_project( if new_project_files.is_empty() { eprintln!("Error: Template '{template}' not found"); - println!("Hint: you can view the available templates at https://github.com/tuono-labs/tuono/tree/main/examples"); + println!( + "Hint: you can view the available templates at https://github.com/tuono-labs/tuono/tree/main/examples" + ); std::process::exit(1); } if folder != "." { if Path::new(&folder).exists() { eprintln!("Error: Directory '{folder}' already exists"); - println!("Hint: you can scaffold a tuono project within an existing folder with 'cd {folder} && tuono new .'"); + println!( + "Hint: you can scaffold a tuono project within an existing folder with 'cd {folder} && tuono new .'" + ); std::process::exit(1); } create_dir(&folder).unwrap(); diff --git a/crates/tuono/src/route.rs b/crates/tuono/src/route.rs index 6733c3f9..de0f5206 100644 --- a/crates/tuono/src/route.rs +++ b/crates/tuono/src/route.rs @@ -1,8 +1,8 @@ use fs_extra::dir::create_all; use http::Method; use regex::Regex; -use reqwest::blocking::Client; use reqwest::Url; +use reqwest::blocking::Client; use std::fs::File; use std::io; use std::path::PathBuf; @@ -180,7 +180,7 @@ impl Route { return Err(format!( "Failed to get the parent directory {:?}", file_path - )) + )); } }; @@ -216,7 +216,7 @@ impl Route { return Err(format!( "Failed to get the parent directory {:?}", data_file_path - )) + )); } }; @@ -255,7 +255,7 @@ impl Route { return Err(format!( "Failed to create the JSON file: {:?}", data_file_path - )) + )); } }; diff --git a/crates/tuono/tests/cli_build.rs b/crates/tuono/tests/cli_build.rs index b7cdd42a..bd076f5b 100644 --- a/crates/tuono/tests/cli_build.rs +++ b/crates/tuono/tests/cli_build.rs @@ -100,8 +100,11 @@ fn it_successfully_create_multiple_api_for_the_same_file() { assert!(temp_main_rs_content.contains( r#".route("/api/health_check", post(api_health_check::post_tuono_internal_api))"# )); - assert!(temp_main_rs_content - .contains(r#".route("/api/health_check", get(api_health_check::get_tuono_internal_api))"#)); + assert!( + temp_main_rs_content.contains( + r#".route("/api/health_check", get(api_health_check::get_tuono_internal_api))"# + ) + ); } #[test] diff --git a/crates/tuono/tests/utils/temp_tuono_project.rs b/crates/tuono/tests/utils/temp_tuono_project.rs index 3adba32f..d5195f35 100644 --- a/crates/tuono/tests/utils/temp_tuono_project.rs +++ b/crates/tuono/tests/utils/temp_tuono_project.rs @@ -3,7 +3,7 @@ use std::env; use std::fs::File; use std::io::Write; use std::path::{Path, PathBuf}; -use tempfile::{tempdir, TempDir}; +use tempfile::{TempDir, tempdir}; #[derive(Debug)] pub struct TempTuonoProject { diff --git a/crates/tuono_internal/Cargo.toml b/crates/tuono_internal/Cargo.toml index 0696a552..9a3475c8 100644 --- a/crates/tuono_internal/Cargo.toml +++ b/crates/tuono_internal/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tuono_internal" version = "0.19.0" -edition = "2021" +edition = "2024" authors = ["V. Ageno "] description = "Superfast React fullstack framework" homepage = "https://tuono.dev" diff --git a/crates/tuono_internal/tests/utils.rs b/crates/tuono_internal/tests/utils.rs index 3a537f93..351a6416 100644 --- a/crates/tuono_internal/tests/utils.rs +++ b/crates/tuono_internal/tests/utils.rs @@ -3,7 +3,7 @@ use std::env; use std::fs::File; use std::io::Write; use std::path::PathBuf; -use tempfile::{tempdir, TempDir}; +use tempfile::{TempDir, tempdir}; #[derive(Debug)] pub struct TempTuonoProject { diff --git a/crates/tuono_lib/Cargo.toml b/crates/tuono_lib/Cargo.toml index e1909b09..76abcf8f 100644 --- a/crates/tuono_lib/Cargo.toml +++ b/crates/tuono_lib/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tuono_lib" version = "0.19.0" -edition = "2021" +edition = "2024" authors = ["V. Ageno "] description = "Superfast React fullstack framework" homepage = "https://tuono.dev" diff --git a/crates/tuono_lib/src/catch_all.rs b/crates/tuono_lib/src/catch_all.rs index 39b8a1c9..7fdc147e 100644 --- a/crates/tuono_lib/src/catch_all.rs +++ b/crates/tuono_lib/src/catch_all.rs @@ -1,4 +1,4 @@ -use crate::{ssr::Js, Payload}; +use crate::{Payload, ssr::Js}; use axum::extract::{Path, Request}; use axum::response::Html; use std::collections::HashMap; diff --git a/crates/tuono_lib/src/env.rs b/crates/tuono_lib/src/env.rs index f25e6df3..c59320fb 100644 --- a/crates/tuono_lib/src/env.rs +++ b/crates/tuono_lib/src/env.rs @@ -3,7 +3,12 @@ use std::collections::HashSet; use std::env; use std::fs; -pub fn load_env_vars(mode: Mode) { +/// Read the env variables from the .env files +/// and set them in the OS env +/// +/// This function is unsafe because it modifies the OS env variables (which needs +/// to be done in a single-threaded context). +pub unsafe fn load_env_vars(mode: Mode) { let mut env_files = vec![String::from(".env"), String::from(".env.local")]; let mode_name = match mode { @@ -32,7 +37,9 @@ pub fn load_env_vars(mode: Mode) { continue; // Skip if key exists in system env } - env::set_var(key, value); + unsafe { + env::set_var(key, value); + } } } } @@ -63,7 +70,9 @@ mod tests { fn add_system_var(&mut self, k: &str, v: &str) { self.vars.insert(k.to_string(), v.to_string()); - env::set_var(k, v); + unsafe { + env::set_var(k, v); + } } pub fn setup_env_file(&mut self, file_name: &str, contents: &str) { @@ -86,7 +95,9 @@ mod tests { let _ = fs::remove_file(file.as_str()); } for key in self.vars.keys() { - env::remove_var(key); + unsafe { + env::remove_var(key); + } } } } @@ -99,7 +110,9 @@ mod tests { mock_env.add_system_var("TEST_KEY", "system_value"); mock_env.setup_env_file(".env", "TEST_KEY=file_value"); - load_env_vars(Mode::Dev); + unsafe { + load_env_vars(Mode::Dev); + } mock_env.capture_keys(&["TEST_KEY"]); @@ -114,7 +127,9 @@ mod tests { mock_env.setup_env_file(".env", "TEST_KEY=base_value"); mock_env.setup_env_file(".env.development", "TEST_KEY=development_value"); - load_env_vars(Mode::Dev); + unsafe { + load_env_vars(Mode::Dev); + } mock_env.capture_keys(&["TEST_KEY"]); @@ -129,7 +144,9 @@ mod tests { mock_env.setup_env_file(".env", "TEST_KEY=base_value"); mock_env.setup_env_file(".env.production", "TEST_KEY=production_value"); - load_env_vars(Mode::Prod); + unsafe { + load_env_vars(Mode::Prod); + } mock_env.capture_keys(&["TEST_KEY"]); @@ -144,7 +161,9 @@ mod tests { mock_env.setup_env_file(".env", "TEST_KEY=base_value"); mock_env.setup_env_file(".env.local", "TEST_KEY=local_value"); - load_env_vars(Mode::Dev); + unsafe { + load_env_vars(Mode::Dev); + } mock_env.capture_keys(&["TEST_KEY"]); @@ -160,7 +179,9 @@ mod tests { mock_env.setup_env_file(".env.development", "TEST_KEY=development_value"); mock_env.setup_env_file(".env.development.local", "TEST_KEY=local_dev_value"); - load_env_vars(Mode::Dev); + unsafe { + load_env_vars(Mode::Dev); + } mock_env.capture_keys(&["TEST_KEY"]); @@ -176,7 +197,9 @@ mod tests { mock_env.setup_env_file(".env.production", "TEST_KEY=production_value"); mock_env.setup_env_file(".env.production.local", "TEST_KEY=local_prod_value"); - load_env_vars(Mode::Prod); + unsafe { + load_env_vars(Mode::Prod); + } mock_env.capture_keys(&["TEST_KEY"]); @@ -191,7 +214,9 @@ mod tests { mock_env.setup_env_file(".env.development", "TEST_KEY=development_value"); mock_env.setup_env_file(".env.production", "TEST_KEY=production_value"); - load_env_vars(Mode::Prod); + unsafe { + load_env_vars(Mode::Prod); + } mock_env.capture_keys(&["TEST_KEY"]); @@ -205,7 +230,9 @@ mod tests { mock_env.setup_env_file(".env", ""); - load_env_vars(Mode::Dev); + unsafe { + load_env_vars(Mode::Dev); + } assert!(env::var("NON_EXISTENT_KEY").is_err()); } @@ -216,8 +243,9 @@ mod tests { let mut mock_env = MockEnv::new(); mock_env.setup_env_file(".env", "INVALID_LINE\nMISSING_EQUALS_SIGN"); - - load_env_vars(Mode::Dev); + unsafe { + load_env_vars(Mode::Dev); + } mock_env.capture_keys(&["INVALID_LINE", "MISSING_EQUALS_SIGN"]); @@ -232,7 +260,9 @@ mod tests { mock_env.setup_env_file(".env", r#"TEST_KEY="quoted_value""#); - load_env_vars(Mode::Dev); + unsafe { + load_env_vars(Mode::Dev); + } mock_env.capture_keys(&["TEST_KEY"]); @@ -243,8 +273,9 @@ mod tests { #[serial] fn test_non_existent_env_file() { let mut mock_env = MockEnv::new(); - - load_env_vars(Mode::Dev); + unsafe { + load_env_vars(Mode::Dev); + } mock_env.capture_keys(&["NON_EXISTENT_KEY"]); @@ -258,7 +289,9 @@ mod tests { mock_env.setup_env_file(".env", "KEY1=value1\nKEY2=value2"); - load_env_vars(Mode::Dev); + unsafe { + load_env_vars(Mode::Dev); + } mock_env.capture_keys(&["KEY1", "KEY2"]); diff --git a/crates/tuono_lib/src/lib.rs b/crates/tuono_lib/src/lib.rs index f6087496..30083a10 100644 --- a/crates/tuono_lib/src/lib.rs +++ b/crates/tuono_lib/src/lib.rs @@ -21,7 +21,7 @@ pub use mode::Mode; pub use payload::Payload; pub use request::Request; pub use response::{Props, Response}; -pub use server::{tuono_internal_init_v8_platform, Server}; +pub use server::{Server, tuono_internal_init_v8_platform}; pub use tuono_lib_macros::{api, handler}; // Re-exports diff --git a/crates/tuono_lib/src/payload.rs b/crates/tuono_lib/src/payload.rs index b8cfaf39..977d669a 100644 --- a/crates/tuono_lib/src/payload.rs +++ b/crates/tuono_lib/src/payload.rs @@ -1,6 +1,6 @@ use crate::config::GLOBAL_CONFIG; use crate::manifest::MANIFEST; -use crate::mode::{Mode, GLOBAL_MODE}; +use crate::mode::{GLOBAL_MODE, Mode}; use erased_serde::Serialize; use regex::Regex; use serde::Serialize as SerdeSerialize; diff --git a/crates/tuono_lib/src/response.rs b/crates/tuono_lib/src/response.rs index adfbbad2..21a931f4 100644 --- a/crates/tuono_lib/src/response.rs +++ b/crates/tuono_lib/src/response.rs @@ -1,8 +1,8 @@ use crate::Request; -use crate::{ssr::Js, Payload}; +use crate::{Payload, ssr::Js}; +use axum::Json; use axum::http::{HeaderMap, StatusCode}; use axum::response::{Html, IntoResponse, Redirect}; -use axum::Json; use axum_extra::extract::cookie::{Cookie, CookieJar}; use erased_serde::Serialize; @@ -82,7 +82,7 @@ impl Props { } impl Response { - pub fn render_to_string(&self, req: Request) -> impl IntoResponse { + pub fn render_to_string(&self, req: Request) -> impl IntoResponse + use<> { match self { Self::Props(Props { data, @@ -106,7 +106,7 @@ impl Response { } } - pub fn json(&self) -> impl IntoResponse { + pub fn json(&self) -> impl IntoResponse + use<> { match self { Self::Props(Props { data, diff --git a/crates/tuono_lib/src/server.rs b/crates/tuono_lib/src/server.rs index 0a13074a..89c7809d 100644 --- a/crates/tuono_lib/src/server.rs +++ b/crates/tuono_lib/src/server.rs @@ -1,7 +1,7 @@ use crate::config::GLOBAL_CONFIG; use crate::manifest::load_manifest; -use crate::mode::{Mode, GLOBAL_MODE}; -use axum::routing::{get, Router}; +use crate::mode::{GLOBAL_MODE, Mode}; +use axum::routing::{Router, get}; use colored::Colorize; use ssr_rs::Ssr; use tower_http::services::ServeDir; @@ -63,7 +63,13 @@ impl Server { let server_address = format!("{}:{}", config.server.host, config.server.port); - load_env_vars(mode); + unsafe { + // This function is unsafe because it modifies the OS env variables + // which is not thread-safe. + // However, we are using it in a controlled environment which hasn't + // spawned any threads yet. + load_env_vars(mode); + } Server { router, diff --git a/crates/tuono_lib/src/services/logger.rs b/crates/tuono_lib/src/services/logger.rs index 77555f0d..24091978 100644 --- a/crates/tuono_lib/src/services/logger.rs +++ b/crates/tuono_lib/src/services/logger.rs @@ -1,5 +1,5 @@ use colored::Colorize; -use http::{method::Method, Request, Response}; +use http::{Request, Response, method::Method}; use pin_project::pin_project; use std::fmt::Debug; use std::future::Future; diff --git a/crates/tuono_lib/src/ssr.rs b/crates/tuono_lib/src/ssr.rs index ebc96ee8..4ffd027a 100644 --- a/crates/tuono_lib/src/ssr.rs +++ b/crates/tuono_lib/src/ssr.rs @@ -1,4 +1,4 @@ -use crate::mode::{Mode, GLOBAL_MODE}; +use crate::mode::{GLOBAL_MODE, Mode}; use ssr_rs::{Ssr, SsrError}; use std::cell::RefCell; use std::fs::read_to_string; diff --git a/crates/tuono_lib/src/vite_websocket_proxy.rs b/crates/tuono_lib/src/vite_websocket_proxy.rs index 863ffce6..5c2a0eee 100644 --- a/crates/tuono_lib/src/vite_websocket_proxy.rs +++ b/crates/tuono_lib/src/vite_websocket_proxy.rs @@ -4,8 +4,8 @@ use axum::response::IntoResponse; use futures_util::{SinkExt, StreamExt}; use tokio_tungstenite::connect_async; use tokio_tungstenite::tungstenite::{Error, Message}; -use tungstenite::client::IntoClientRequest; use tungstenite::ClientRequestBuilder; +use tungstenite::client::IntoClientRequest; const VITE_WS_PROTOCOL: &str = "vite-hmr"; diff --git a/crates/tuono_lib/tests/server_test.rs b/crates/tuono_lib/tests/server_test.rs index ce0558f2..d38ba757 100644 --- a/crates/tuono_lib/tests/server_test.rs +++ b/crates/tuono_lib/tests/server_test.rs @@ -71,11 +71,13 @@ async fn index_html_route() { // TODO: This should return a 404 status code assert!(response.status().is_success()); - assert!(response - .text() - .await - .unwrap() - .starts_with("")); + assert!( + response + .text() + .await + .unwrap() + .starts_with("") + ); } #[tokio::test] diff --git a/crates/tuono_lib/tests/utils/health_check.rs b/crates/tuono_lib/tests/utils/health_check.rs index fb01e194..257e246c 100644 --- a/crates/tuono_lib/tests/utils/health_check.rs +++ b/crates/tuono_lib/tests/utils/health_check.rs @@ -1,5 +1,5 @@ -use tuono_lib::axum::http::StatusCode; use tuono_lib::Request; +use tuono_lib::axum::http::StatusCode; #[tuono_lib::api(GET)] async fn health_check(_req: Request) -> StatusCode { diff --git a/crates/tuono_lib/tests/utils/mock_server.rs b/crates/tuono_lib/tests/utils/mock_server.rs index 93d8d8e7..37981d10 100644 --- a/crates/tuono_lib/tests/utils/mock_server.rs +++ b/crates/tuono_lib/tests/utils/mock_server.rs @@ -3,9 +3,9 @@ use std::fs::File; use std::io::Write; use std::path::PathBuf; use std::{env, fs}; -use tempfile::{tempdir, TempDir}; +use tempfile::{TempDir, tempdir}; use tuono_lib::axum::routing::get; -use tuono_lib::{axum::Router, tuono_internal_init_v8_platform, Mode, Server}; +use tuono_lib::{Mode, Server, axum::Router, tuono_internal_init_v8_platform}; use crate::utils::catch_all::get_tuono_internal_api as catch_all; use crate::utils::dynamic_parameter::get_tuono_internal_api as dynamic_parameter; diff --git a/crates/tuono_lib_macros/Cargo.toml b/crates/tuono_lib_macros/Cargo.toml index bdf2dd61..a1f19b5d 100644 --- a/crates/tuono_lib_macros/Cargo.toml +++ b/crates/tuono_lib_macros/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tuono_lib_macros" version = "0.19.0" -edition = "2021" +edition = "2024" description = "Superfast React fullstack framework" homepage = "https://tuono.dev" keywords = [ "react", "typescript", "fullstack", "web", "ssr"] diff --git a/crates/tuono_lib_macros/src/api.rs b/crates/tuono_lib_macros/src/api.rs index b2acb63a..988ce3b7 100644 --- a/crates/tuono_lib_macros/src/api.rs +++ b/crates/tuono_lib_macros/src/api.rs @@ -6,7 +6,7 @@ use proc_macro::{Span, TokenStream}; use quote::quote; use syn::punctuated::Punctuated; use syn::token::Comma; -use syn::{parse_macro_input, FnArg, Ident, ItemFn, Pat}; +use syn::{FnArg, Ident, ItemFn, Pat, parse_macro_input}; pub fn api_core(attrs: TokenStream, item: TokenStream) -> TokenStream { let item = parse_macro_input!(item as ItemFn); diff --git a/crates/tuono_lib_macros/src/handler.rs b/crates/tuono_lib_macros/src/handler.rs index 5db5afb3..b481b100 100644 --- a/crates/tuono_lib_macros/src/handler.rs +++ b/crates/tuono_lib_macros/src/handler.rs @@ -7,7 +7,7 @@ use proc_macro::TokenStream; use quote::quote; use syn::punctuated::Punctuated; use syn::token::Comma; -use syn::{parse_macro_input, FnArg, ItemFn, Pat}; +use syn::{FnArg, ItemFn, Pat, parse_macro_input}; pub fn handler_core(_args: TokenStream, item: TokenStream) -> TokenStream { let item = parse_macro_input!(item as ItemFn); diff --git a/crates/tuono_lib_macros/src/utils.rs b/crates/tuono_lib_macros/src/utils.rs index 8511513a..55510046 100644 --- a/crates/tuono_lib_macros/src/utils.rs +++ b/crates/tuono_lib_macros/src/utils.rs @@ -1,7 +1,7 @@ use quote::quote; use syn::punctuated::Punctuated; use syn::token::Comma; -use syn::{parse2, parse_quote, FnArg, Pat, Stmt}; +use syn::{FnArg, Pat, Stmt, parse_quote, parse2}; pub fn create_struct_fn_arg() -> FnArg { parse2(quote! {