diff --git a/Cargo.lock b/Cargo.lock index 4ef0923..32a2c67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -223,18 +223,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - -[[package]] -name = "aho-corasick" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -397,9 +388,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c" dependencies = [ "memchr", "serde", @@ -428,9 +419,9 @@ dependencies = [ [[package]] name = "cargo_toml" -version = "0.15.3" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "599aa35200ffff8f04c1925aa1acc92fa2e08874379ef42e210a80e527e60838" +checksum = "4d1ece59890e746567b467253aea0adbe8a21784d0b025d8a306f66c391c2957" dependencies = [ "serde", "toml", @@ -581,6 +572,39 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -778,15 +802,15 @@ checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" [[package]] name = "globset" -version = "0.4.10" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" dependencies = [ - "aho-corasick 0.7.20", + "aho-corasick", "bstr", - "fnv", "log", - "regex", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] @@ -916,17 +940,16 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" +checksum = "747ad1b4ae841a78e8aba0d63adbfbeaea26b517b63705d47856b73015d27060" dependencies = [ + "crossbeam-deque", "globset", - "lazy_static", "log", "memchr", - "regex", + "regex-automata 0.4.3", "same-file", - "thread_local", "walkdir", "winapi-util", ] @@ -1067,6 +1090,7 @@ dependencies = [ "convert_case 0.6.0", "env_logger", "http", + "ignore", "include_dir", "indexmap 2.0.0", "indoc", @@ -1207,15 +1231,24 @@ dependencies = [ [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] [[package]] name = "mime" @@ -1552,10 +1585,10 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89089e897c013b3deb627116ae56a6955a72b8bed395c9526af31c9fe528b484" dependencies = [ - "aho-corasick 1.0.2", + "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.3.0", + "regex-syntax 0.7.3", ] [[package]] @@ -1564,9 +1597,20 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa250384981ea14565685dea16a9ccc4d1c541a13f82b9c168572264d1df8c56" dependencies = [ - "aho-corasick 1.0.2", + "aho-corasick", + "memchr", + "regex-syntax 0.7.3", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.2", ] [[package]] @@ -1575,6 +1619,12 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846" +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1677,9 +1727,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" dependencies = [ "serde", ] @@ -1987,9 +2037,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" dependencies = [ "serde", "serde_spanned", @@ -1999,18 +2049,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.19.12" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" dependencies = [ "indexmap 2.0.0", "serde", @@ -2229,9 +2279,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", @@ -2405,9 +2455,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.7" +version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" +checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" dependencies = [ "memchr", ] diff --git a/core/src/fs.rs b/core/src/fs.rs index 21b7088..c3550d6 100644 --- a/core/src/fs.rs +++ b/core/src/fs.rs @@ -65,7 +65,7 @@ fn copy_files_recursive( } /// Copy static files to the destination path. -pub fn copy_files(dest_path: &Path, project_template: &str, ignore: &[&str]) -> anyhow::Result<()> { +pub fn copy_builtin_files(dest_path: &Path, project_template: &str, ignore: &[&str]) -> anyhow::Result<()> { copy_files_recursive( dest_path, TEMPLATE_DIR.get_dir(project_template).unwrap(), diff --git a/core/src/template.rs b/core/src/template.rs index a96f734..494d8b0 100644 --- a/core/src/template.rs +++ b/core/src/template.rs @@ -7,7 +7,7 @@ use crate::{OutputOptions, write_file}; pub static TEMPLATE_DIR: include_dir::Dir<'_> = include_dir::include_dir!("$CARGO_MANIFEST_DIR/template"); -pub fn copy_templates( +pub fn copy_builtin_templates( opts: &OutputOptions, tera: &tera::Tera, context: &Context, diff --git a/core/template/rust/Cargo.toml.j2 b/core/template/rust/Cargo.toml.j2 index 587a7f6..1fe4f4d 100644 --- a/core/template/rust/Cargo.toml.j2 +++ b/core/template/rust/Cargo.toml.j2 @@ -15,7 +15,7 @@ repository = "https://github.com/{{ github_repo }}" doctest = false [dependencies] -httpclient = "0.18.0" +httpclient = "0.19.0" serde = { version = "1.0.137", features = ["derive"] } serde_json = "1.0.81" futures = "0.3.25" diff --git a/libninja/Cargo.toml b/libninja/Cargo.toml index 5e3becc..517653b 100644 --- a/libninja/Cargo.toml +++ b/libninja/Cargo.toml @@ -30,8 +30,8 @@ tera = "1.19.0" include_dir = "0.7.3" regex = "1.9.0" indoc = "2.0.2" -cargo_toml = "0.15.3" -toml = "0.7.6" +cargo_toml = "0.17.1" +toml = "0.8.8" topo_sort = "0.4.0" url = "2.4.0" http = "0.2.9" @@ -45,6 +45,7 @@ ln-core = { path = "../core" } libninja_mir = { path = "../mir" } libninja_hir = { path = "../hir" } libninja_commercial = { path = "../commercial" , optional = true } +ignore = "0.4.21" [dev-dependencies] env_logger = "0.10.0" diff --git a/libninja/src/rust.rs b/libninja/src/rust.rs index de03c47..0341520 100644 --- a/libninja/src/rust.rs +++ b/libninja/src/rust.rs @@ -2,7 +2,7 @@ use std::hash::Hash; use std::path::Path; use std::thread::current; -use anyhow::Result; +use anyhow::{anyhow, Result}; use convert_case::{Case, Casing}; use indoc::formatdoc; use openapiv3::OpenAPI; @@ -12,7 +12,7 @@ use quote::quote; use codegen::ToRustIdent; use codegen::ToRustType; use format::format_code; -use ln_core::{copy_files, copy_templates, create_context, get_template_file, prepare_templates}; +use ln_core::{copy_builtin_files, copy_builtin_templates, create_context, get_template_file, prepare_templates}; use ::mir::{Visibility, Import, File}; use ln_core::fs; use hir::{HirSpec, IntegerSerialization, DateSerialization}; @@ -91,6 +91,24 @@ pub fn calculate_extras(spec: &HirSpec) -> Extras { } +pub fn copy_from_target_templates(opts: &OutputOptions) -> Result<()> { + let template_path = opts.dest_path.join("template"); + for path in ignore::Walk::new(&template_path) { + let path: ignore::DirEntry = path?; + let rel_path = path.path().strip_prefix(&template_path)?; + if rel_path.to_str().unwrap() == "src/lib.rs" { + continue; + } + if path.file_type().expect(&format!("Failed to read file: {}", path.path().display())).is_file() { + let dest = opts.dest_path.join(rel_path); + fs::create_dir_all(dest.parent().unwrap())?; + //copy the file + std::fs::copy(&path.path(), &dest)?; + } + } + Ok(()) +} + pub fn generate_rust_library(spec: OpenAPI, opts: OutputOptions) -> Result<()> { let config = &opts.library_options.config; let src_path = opts.dest_path.join("src"); @@ -123,8 +141,9 @@ pub fn generate_rust_library(spec: OpenAPI, opts: OutputOptions) -> Result<()> { context.insert("client_docs_url", &format!("https://docs.rs/{}", opts.library_options.package_name)); - copy_files(&opts.dest_path, &opts.library_options.language.to_string(), &["src"])?; - copy_templates(&opts, &tera, &context)?; + copy_builtin_files(&opts.dest_path, &opts.library_options.language.to_string(), &["src"])?; + copy_builtin_templates(&opts, &tera, &context)?; + copy_from_target_templates(&opts)?; bump_version_and_update_deps(&extras, &opts)?; diff --git a/libninja/src/rust/client.rs b/libninja/src/rust/client.rs index dc52f54..adb1165 100644 --- a/libninja/src/rust/client.rs +++ b/libninja/src/rust/client.rs @@ -129,10 +129,12 @@ pub fn build_api_client_method(operation: &Operation) -> TokenStream { let name = &operation.name.to_rust_ident(); quote! { #doc - pub fn #name(&self, #(#fn_args),*) -> request::#request_struct { - request::#request_struct { - http_client: &self, - #(#struct_field_values,)* + pub fn #name(&self, #(#fn_args),*) -> FluentRequest<'_, request::#request_struct> { + FluentRequest { + client: self, + params: request::#request_struct { + #(#struct_field_values,)* + } } } } diff --git a/libninja/src/rust/codegen.rs b/libninja/src/rust/codegen.rs index 166c4e6..e9e97d7 100644 --- a/libninja/src/rust/codegen.rs +++ b/libninja/src/rust/codegen.rs @@ -351,7 +351,7 @@ pub fn to_rust_example_value(ty: &Ty, name: &str, spec: &HirSpec, use_ref_value: Ty::Any => quote!(serde_json::json!({})), Ty::Date { .. } => quote!(chrono::Utc::now().date_naive()), Ty::DateTime { .. } => quote!(chrono::Utc::now()), - Ty::Currency { .. } => quote!(rust_decimal::dec!(100.01)) + Ty::Currency { .. } => quote!(rust_decimal_macros::dec!(100.01)) }; Ok(s) } diff --git a/libninja/src/rust/request.rs b/libninja/src/rust/request.rs index 750cf0f..4471824 100644 --- a/libninja/src/rust/request.rs +++ b/libninja/src/rust/request.rs @@ -1,3 +1,4 @@ +use std::sync::OnceLock; use std::default::Default; use convert_case::{Case, Casing}; @@ -77,6 +78,7 @@ pub fn assign_inputs_to_request(inputs: &[Parameter]) -> TokenStream { } } +/// This is complicated because we need to interpolate any param values. pub fn build_url(operation: &Operation) -> TokenStream { let inputs = operation .parameters @@ -89,12 +91,15 @@ pub fn build_url(operation: &Operation) -> TokenStream { #path } } else { - let re = regex::Regex::new("\\{([_\\w]+)\\}").unwrap(); + static FIX_PLACEHOLDERS: OnceLock = OnceLock::new(); + let fix = FIX_PLACEHOLDERS.get_or_init(|| + regex::Regex::new("\\{([_\\w]+)\\}").unwrap() + ); let inputs = inputs.into_iter().map(|input| { let name = input.name.to_rust_ident(); - quote! { #name = self.#name } + quote! { #name = self.params.#name } }); - let path = re + let path = fix .replace_all(&operation.path, |cap: &Captures| { format!("{{{}}}", cap.get(1).unwrap().as_str().to_case(Case::Snake)) }) @@ -105,50 +110,50 @@ pub fn build_url(operation: &Operation) -> TokenStream { } } -pub fn authorize_request(spec: &HirSpec) -> TokenStream { - if spec_defines_auth(spec) { - quote! { - r = self.http_client.authenticate(r); - } - } else { - quote! {} - } -} +// pub fn authorize_request(spec: &HirSpec) -> TokenStream { +// if spec_defines_auth(spec) { +// quote! { +// r = self.http_client.authenticate(r); +// } +// } else { +// quote! {} +// } +// } -pub fn build_send_function(operation: &Operation, spec: &HirSpec) -> Function { - let assign_inputs = assign_inputs_to_request(&operation.parameters); - let auth = authorize_request(spec); - let response = operation.ret.to_rust_type(); - let method = syn::Ident::new(&operation.method, proc_macro2::Span::call_site()); - let url = build_url(operation); - - let ret = if matches!(operation.ret , Ty::Unit) { - quote!(Ok(())) - } else { - quote!(res - .json() - .await - .map_err(|e| anyhow::anyhow!("{:?}", e)) - ) - }; - Function { - name: "send".into(), - ret: quote! { - ::httpclient::InMemoryResult<#response> - }, - body: quote! { - let mut r = self.http_client.client.#method(#url); - #assign_inputs - #auth - let res = r - .await?; - res.json().map_err(Into::into) - }, - async_: true, - public: true, - ..Function::default() - } -} +// pub fn build_send_function(operation: &Operation, spec: &HirSpec) -> Function { +// let assign_inputs = assign_inputs_to_request(&operation.parameters); +// let auth = authorize_request(spec); +// let response = operation.ret.to_rust_type(); +// let method = syn::Ident::new(&operation.method, proc_macro2::Span::call_site()); +// let url = build_url(operation); +// +// let ret = if matches!(operation.ret , Ty::Unit) { +// quote!(Ok(())) +// } else { +// quote!(res +// .json() +// .await +// .map_err(|e| anyhow::anyhow!("{:?}", e)) +// ) +// }; +// Function { +// name: "send".into(), +// ret: quote! { +// ::httpclient::InMemoryResult<#response> +// }, +// body: quote! { +// let mut r = self.http_client.client.#method(#url); +// #assign_inputs +// #auth +// let res = r +// .await?; +// res.json().map_err(Into::into) +// }, +// async_: true, +// public: true, +// ..Function::default() +// } +// } pub fn build_struct_fields( inputs: &[Parameter],