diff --git a/advanced/samples/python/wit/workflow_service/_system_nexus_interceptor.py b/advanced/samples/python/wit/workflow_service/_system_nexus_interceptor.py new file mode 100644 index 00000000..f3e8f84d --- /dev/null +++ b/advanced/samples/python/wit/workflow_service/_system_nexus_interceptor.py @@ -0,0 +1,97 @@ +# Generated by nexgen v0.2.2. DO NOT EDIT! + +from __future__ import annotations + + +import abc +import typing + +from temporalio.nexus.system import TEMPORAL_SYSTEM_ENDPOINT + +from . import models + +if typing.TYPE_CHECKING: + import temporalio.workflow + from temporalio.worker._interceptor import StartNexusOperationInput + + +__all__ = [ + "_start_system_nexus_operation", + "_SystemNexusWorkflowOutboundInterceptorBase", + "_SystemNexusWorkflowOutboundInterceptorTerminal", +] + + +_InputT = typing.TypeVar("_InputT") +_OutputT = typing.TypeVar("_OutputT") + + +async def _start_system_nexus_operation( + interceptor: _SystemNexusWorkflowOutboundInterceptorBase, + input: StartNexusOperationInput[_InputT, _OutputT], +) -> temporalio.workflow.NexusOperationHandle[_OutputT]: + if ( + input.service == "temporal.api.workflowservice.v1.WorkflowService" + and input.operation_name == "SignalWithStartWorkflowExecution" + ): + typed_input = typing.cast( + "StartNexusOperationInput[models.SignalWithStartWorkflowRequest, models.SignalWithStartWorkflowResponse]", + input, + ) + # The dispatch check above establishes that this operation's response type is _OutputT. + return typing.cast( + "temporalio.workflow.NexusOperationHandle[_OutputT]", + await interceptor.start_signal_with_start_workflow(typed_input.input), + ) + raise ValueError( + f"unsupported System Nexus operation: {input.service}/{input.operation_name}" + ) + + +class _SystemNexusWorkflowOutboundInterceptorBase(abc.ABC): + @abc.abstractmethod + def _next_system_nexus_interceptor( + self, + ) -> _SystemNexusWorkflowOutboundInterceptorBase: ... + + async def start_signal_with_start_workflow( + self, request: models.SignalWithStartWorkflowRequest + ) -> temporalio.workflow.NexusOperationHandle[ + models.SignalWithStartWorkflowResponse + ]: + """Intercept the SignalWithStartWorkflow operation.""" + return await self._next_system_nexus_interceptor().start_signal_with_start_workflow( + request + ) + + +class _SystemNexusWorkflowOutboundInterceptorTerminal(abc.ABC): + @abc.abstractmethod + async def _outbound_start_nexus_operation( + self, + input: StartNexusOperationInput[_InputT, _OutputT], + ) -> temporalio.workflow.NexusOperationHandle[_OutputT]: ... + + async def start_signal_with_start_workflow( + self, request: models.SignalWithStartWorkflowRequest + ) -> temporalio.workflow.NexusOperationHandle[ + models.SignalWithStartWorkflowResponse + ]: + from temporalio.worker._interceptor import StartNexusOperationInput + from temporalio.workflow import NexusOperationCancellationType + + return await self._outbound_start_nexus_operation( + StartNexusOperationInput( + endpoint=TEMPORAL_SYSTEM_ENDPOINT, + service="temporal.api.workflowservice.v1.WorkflowService", + operation="SignalWithStartWorkflowExecution", + input=request, + output_type=models.SignalWithStartWorkflowResponse, + schedule_to_close_timeout=None, + schedule_to_start_timeout=None, + start_to_close_timeout=None, + cancellation_type=NexusOperationCancellationType.WAIT_COMPLETED, + headers=None, + summary=None, + ) + ) diff --git a/src/generator/dotnet.rs b/src/generator/dotnet.rs index 2ee8e7c1..8685d50d 100644 --- a/src/generator/dotnet.rs +++ b/src/generator/dotnet.rs @@ -2257,22 +2257,21 @@ impl ExternalModelBackend for DotNetExternalModels { pub(crate) fn generate( tree: &crate::spec::ApiSpecTree, support: &crate::SupportFiles, - mode: GenerationMode, ) -> Result { match &tree.root { ApiSpecNode::Leaf(leaf) => { let support_fragments = support_fragments_for_plan(&leaf.spec, support); - generate_leaf(&leaf.spec, &support_fragments, mode) + generate_leaf(&leaf.spec, &support_fragments) } - ApiSpecNode::Branch(branch) => generate_tree(branch, support, mode), + ApiSpecNode::Branch(branch) => generate_tree(branch, support), } } fn generate_leaf( api_plan: &PlannedSpec, support_fragments: &[SupportFragmentSpec], - mode: GenerationMode, ) -> Result { + let mode = crate::nexgen_config::current().mode; let support_namespace = dotnet_support_namespace(support_fragments)?; let generator = ApiPlanner::new(api_plan, support_namespace.as_deref())?; validate_dotnet_support_references( @@ -2322,12 +2321,11 @@ fn generate_leaf( fn generate_tree( branch: &ApiSpecBranch, support: &crate::SupportFiles, - mode: GenerationMode, ) -> Result { let mut files = BTreeMap::new(); let mut warnings = Vec::new(); for node in branch.children.values() { - generate_tree_node(node, support, mode, &mut files, &mut warnings)?; + generate_tree_node(node, support, &mut files, &mut warnings)?; } Ok(GeneratedFiles { layout: crate::generator::GeneratedOutputLayout::Directory, @@ -2339,14 +2337,13 @@ fn generate_tree( fn generate_tree_node( node: &ApiSpecNode, support: &crate::SupportFiles, - mode: GenerationMode, files: &mut BTreeMap, warnings: &mut Vec, ) -> Result<()> { match node { ApiSpecNode::Leaf(leaf) => { let support_fragments = support_fragments_for_plan(&leaf.spec, support); - let generated = generate_leaf(&leaf.spec, &support_fragments, mode)?; + let generated = generate_leaf(&leaf.spec, &support_fragments)?; warnings.extend(generated.warnings); let prefix = leaf.module_path.to_path_buf(); for (path, contents) in generated.files { @@ -2356,7 +2353,7 @@ fn generate_tree_node( } ApiSpecNode::Branch(branch) => { for node in branch.children.values() { - generate_tree_node(node, support, mode, files, warnings)?; + generate_tree_node(node, support, files, warnings)?; } Ok(()) } diff --git a/src/generator/go.rs b/src/generator/go.rs index 0f39bc57..54dce334 100644 --- a/src/generator/go.rs +++ b/src/generator/go.rs @@ -625,9 +625,8 @@ pub(crate) fn generate( api_plan: &PlannedSpec, support_fragments: &[SupportFragmentSpec], options: &GoOptions, - mode: GenerationMode, ) -> Result { - generate_in_tree(api_plan, support_fragments, options, mode, &[]) + generate_in_tree(api_plan, support_fragments, options, &[]) } /// Generates one input file, given every JSON model the whole generate closure @@ -643,10 +642,9 @@ fn generate_in_tree( api_plan: &PlannedSpec, support_fragments: &[SupportFragmentSpec], options: &GoOptions, - mode: GenerationMode, tree_models: &[PlannedJsonType], ) -> Result { - ApiPlanner::new(api_plan, options, mode, tree_models)?.generate(support_fragments) + ApiPlanner::new(api_plan, options, tree_models)?.generate(support_fragments) } /// Every JSON model declared anywhere in the generate closure. @@ -662,11 +660,10 @@ pub(crate) fn generate_tree( tree: &ApiSpecTree, support: &SupportFiles, options: &GoOptions, - mode: GenerationMode, ) -> Result { match &tree.root { - ApiSpecNode::Leaf(leaf) => generate_single_leaf(leaf, support, options, mode), - ApiSpecNode::Branch(branch) => generate_branch_tree(branch, support, options, mode), + ApiSpecNode::Leaf(leaf) => generate_single_leaf(leaf, support, options), + ApiSpecNode::Branch(branch) => generate_branch_tree(branch, support, options), } } @@ -678,9 +675,8 @@ fn generate_single_leaf( leaf: &ApiSpecLeaf, support: &SupportFiles, options: &GoOptions, - mode: GenerationMode, ) -> Result { - let mut generated = generate(&leaf.spec, &support.fragments, options, mode)?; + let mut generated = generate(&leaf.spec, &support.fragments, options)?; if go_tree_has_json_models(&[leaf]) { let package_name = GoPackageContext::new(&leaf.spec, options)?.package_name; insert_generated_file( @@ -696,7 +692,6 @@ fn generate_branch_tree( branch: &ApiSpecBranch, support: &SupportFiles, options: &GoOptions, - mode: GenerationMode, ) -> Result { let mut leaves = Vec::new(); collect_leaf_specs(branch, &mut leaves); @@ -729,7 +724,7 @@ fn generate_branch_tree( // references to sibling files are unqualified within the one package. let mut leaf_spec = leaf.spec.clone(); leaf_spec.module_path = root.clone(); - let generated = generate_in_tree(&leaf_spec, &[], options, mode, &tree_models)?; + let generated = generate_in_tree(&leaf_spec, &[], options, &tree_models)?; warnings.extend(generated.warnings); // `generate` names the single JSON member file `.go`; re-key it @@ -885,9 +880,11 @@ fn plan_uses_json_models(api_plan: &PlannedSpec) -> bool { } impl GoExternalModels { - fn new(api_plan: &PlannedSpec, package: GoPackageContext, mode: GenerationMode) -> Self { + fn new(api_plan: &PlannedSpec, package: GoPackageContext) -> Self { if plan_uses_json_models(api_plan) { - Self::Json(json::ModelBackend::new(mode == GenerationMode::NativeApi)) + Self::Json(json::ModelBackend::new( + crate::nexgen_config::current().mode == GenerationMode::NativeApi, + )) } else { Self::Proto(proto::ModelBackend::new(package)) } @@ -1006,7 +1003,6 @@ impl GoExternalModels { struct ApiPlanner<'a> { api_plan: &'a PlannedSpec, - mode: GenerationMode, package: GoPackageContext, imports: BTreeSet, external_models: GoExternalModels, @@ -1020,7 +1016,6 @@ impl<'a> ApiPlanner<'a> { fn new( api_plan: &'a PlannedSpec, options: &GoOptions, - mode: GenerationMode, tree_models: &[PlannedJsonType], ) -> Result { let package = GoPackageContext::new(api_plan, options)?; @@ -1034,13 +1029,12 @@ impl<'a> ApiPlanner<'a> { collect_imports_from_plan(api_plan, &mut imports); imports.retain(|import_path| !package.is_self_import(import_path)); - let mut external_models = GoExternalModels::new(api_plan, package.clone(), mode); + let mut external_models = GoExternalModels::new(api_plan, package.clone()); external_models.adopt_tree_models(tree_models); external_models.prepare(api_plan)?; Ok(Self { api_plan, - mode, package, imports, external_models, @@ -1156,7 +1150,7 @@ impl<'a> ApiPlanner<'a> { self.imports .insert("github.com/nexus-rpc/sdk-go/nexus".to_string()); } - if self.mode == GenerationMode::NativeApi + if crate::nexgen_config::current().mode == GenerationMode::NativeApi && self.external_models.renders_operation_references() && !self.api_plan.services.is_empty() && !self.package.is_self_import("go.temporal.io/sdk/workflow") diff --git a/src/generator/java.rs b/src/generator/java.rs index df624fd9..c0f08dbf 100644 --- a/src/generator/java.rs +++ b/src/generator/java.rs @@ -6,7 +6,7 @@ use heck::ToLowerCamelCase; use crate::error::{Error, Result}; use crate::generator::json_schema::java as java_json; use crate::generator::json_schema::java::JavaContext; -use crate::generator::{GeneratedFiles, GeneratedOutputLayout, GenerationMode}; +use crate::generator::{GeneratedFiles, GeneratedOutputLayout}; use crate::planning::{PlannedFamily, PlannedJsonType}; use crate::spec::{ApiSpecLeaf, ApiSpecNode}; use crate::spec::{ExternalTypeSpec, ModulePath, ServiceSpec, TypeSpec}; @@ -17,7 +17,6 @@ const JAVA_FORMAT_LINE_LENGTH: usize = 88; pub(crate) fn generate( tree: &crate::spec::ApiSpecTree, _support: &crate::SupportFiles, - _mode: GenerationMode, base_package: Option<&str>, ) -> Result { let base_package = base_package.unwrap_or(DEFAULT_PACKAGE); diff --git a/src/generator/mod.rs b/src/generator/mod.rs index 8753ecd7..70f96dfb 100644 --- a/src/generator/mod.rs +++ b/src/generator/mod.rs @@ -133,24 +133,27 @@ pub(crate) fn generate_files_for_tree_with_mode_and_options( mode: GenerationMode, options: GenerateFilesOptions, ) -> Result { - crate::compile_tree_to_files(language, tree, descriptors, support, mode, options) + let config = crate::nexgen_config::NexgenConfig { + mode, + ..crate::nexgen_config::current() + }; + let _scope = crate::nexgen_config::scope(config); + crate::compile_tree_to_files(language, tree, descriptors, support, options) } pub(crate) fn generate_files_from_planned_tree( language: Language, tree: &ApiSpecTree, support: &SupportFiles, - mode: GenerationMode, options: GenerateFilesOptions, ) -> Result { + let mode = crate::nexgen_config::current().mode; let mut generated = match language { - Language::Dotnet => dotnet::generate(tree, support, mode), - Language::Go => generate_go_tree(tree, support, mode, options), - Language::Java => java::generate(tree, support, mode, options.java_package_root.as_deref()), - Language::Python => python::generate(tree, support, mode), - Language::TypeScript => { - typescript::generate(tree, support, mode, options.ts_date_time_types) - } + Language::Dotnet => dotnet::generate(tree, support), + Language::Go => generate_go_tree(tree, support, options), + Language::Java => java::generate(tree, support, options.java_package_root.as_deref()), + Language::Python => python::generate(tree, support), + Language::TypeScript => typescript::generate(tree, support, options.ts_date_time_types), language => Err(Error::UnsupportedLanguage { language }), }?; generated.warnings = if mode == GenerationMode::NativeApi { @@ -164,7 +167,6 @@ pub(crate) fn generate_files_from_planned_tree( fn generate_go_tree( tree: &ApiSpecTree, support: &SupportFiles, - mode: GenerationMode, options: GenerateFilesOptions, ) -> Result { go::generate_tree( @@ -174,7 +176,6 @@ fn generate_go_tree( output_dir_name: options.go_output_dir_name, ..go::GoOptions::default() }, - mode, ) } diff --git a/src/generator/python.rs b/src/generator/python.rs index 72924793..357b333a 100644 --- a/src/generator/python.rs +++ b/src/generator/python.rs @@ -45,21 +45,19 @@ struct PythonGenerationResult { pub(crate) fn generate( tree: &crate::spec::ApiSpecTree, support: &crate::SupportFiles, - mode: GenerationMode, ) -> Result { match &tree.root { ApiSpecNode::Leaf(leaf) => { let support_fragments = support_fragments_for_plan(&leaf.spec, support); - generate_leaf(&leaf.spec, &support_fragments, mode) + generate_leaf(&leaf.spec, &support_fragments) } - ApiSpecNode::Branch(branch) => generate_tree(branch, support, mode), + ApiSpecNode::Branch(branch) => generate_tree(branch, support), } } fn generate_leaf( api_plan: &PlannedSpec, support_fragments: &[SupportFragmentSpec], - mode: GenerationMode, ) -> Result { reject_support_namespaces(Language::Python, support_fragments)?; let inline_model_rebuilds = api_plan @@ -68,24 +66,22 @@ fn generate_leaf( .values() .all(BTreeSet::is_empty); let generated = - ApiPlanner::new(api_plan, inline_model_rebuilds, None)?.build(support_fragments, mode)?; + ApiPlanner::new(api_plan, inline_model_rebuilds, None)?.build(support_fragments)?; Ok(generated.generated_files) } fn generate_leaf_with_model_hoists( api_plan: &PlannedSpec, support_fragments: &[SupportFragmentSpec], - mode: GenerationMode, model_hoists: &PythonModelHoists, ) -> Result { reject_support_namespaces(Language::Python, support_fragments)?; - ApiPlanner::new(api_plan, true, Some(model_hoists))?.build(support_fragments, mode) + ApiPlanner::new(api_plan, true, Some(model_hoists))?.build(support_fragments) } fn generate_tree( branch: &ApiSpecBranch, support: &crate::SupportFiles, - mode: GenerationMode, ) -> Result { let model_hoists = tree_model_hoists(branch)?; let mut files = BTreeMap::new(); @@ -100,7 +96,6 @@ fn generate_tree( let exported_names = generate_tree_node( node, support, - mode, &model_hoists, &mut files, &mut warnings, @@ -125,7 +120,6 @@ fn generate_tree( fn generate_tree_node( node: &ApiSpecNode, support: &crate::SupportFiles, - mode: GenerationMode, model_hoists: &PythonModelHoists, files: &mut BTreeMap, warnings: &mut Vec, @@ -134,12 +128,8 @@ fn generate_tree_node( match node { ApiSpecNode::Leaf(leaf) => { let support_fragments = support_fragments_for_plan(&leaf.spec, support); - let generated = generate_leaf_with_model_hoists( - &leaf.spec, - &support_fragments, - mode, - model_hoists, - )?; + let generated = + generate_leaf_with_model_hoists(&leaf.spec, &support_fragments, model_hoists)?; extend_root_package_imports(root_package_imports, generated.root_package_imports); warnings.extend(generated.generated_files.warnings); let prefix = leaf.module_path.to_path_buf(); @@ -154,7 +144,6 @@ fn generate_tree_node( let exported_names = generate_tree_node( node, support, - mode, model_hoists, files, warnings, @@ -509,7 +498,6 @@ impl<'a> ApiPlanner<'a> { fn build( mut self, support_fragments: &[SupportFragmentSpec], - mode: GenerationMode, ) -> Result { let api_plan = self.api_plan; let services = api_plan @@ -565,7 +553,7 @@ impl<'a> ApiPlanner<'a> { validate_python_generated_names(self.api_plan, &model_fragments.generated_names)?; let (generated_files, exported_names) = - self.render_package(&model_fragments, &services, support_fragments, mode)?; + self.render_package(&model_fragments, &services, support_fragments)?; Ok(PythonGenerationResult { generated_files, root_package_imports: model_fragments.root_package_imports, @@ -587,8 +575,8 @@ impl<'a> ApiPlanner<'a> { model_fragments: &RenderedModelFragments, services: &[RenderedService<'_>], support_fragments: &[SupportFragmentSpec], - mode: GenerationMode, ) -> Result<(GeneratedFiles, BTreeSet)> { + let mode = crate::nexgen_config::current().mode; let mut files = BTreeMap::new(); render_support_package(&mut files, support_fragments)?; for (path, contents) in self.external_models.render_support_files()? { @@ -682,7 +670,6 @@ impl<'a> ApiPlanner<'a> { &empty_root_package_imports }; let exported_names = package_export_names( - mode, services, if mode == GenerationMode::NativeApi { &package_model_names @@ -733,6 +720,13 @@ impl<'a> ApiPlanner<'a> { render_operations_package_init(), )?; } + if crate::nexgen_config::current().system_nexus && mode == GenerationMode::NativeApi { + insert_generated_file( + &mut files, + "_system_nexus_interceptor.py", + render_system_nexus_interceptor(services), + )?; + } for service in services { for resource in &service.resources { @@ -3327,14 +3321,13 @@ fn render_definitions_only_package_init( } fn package_export_names( - mode: GenerationMode, services: &[RenderedService<'_>], model_names: &[String], root_package_imports: &RootPackageImports, ) -> BTreeSet { let mut names = root_package_export_names(root_package_imports); names.extend(model_names.iter().cloned()); - match mode { + match crate::nexgen_config::current().mode { GenerationMode::DefinitionsOnly => { names.extend(services.iter().map(|service| service.name.to_string())); } @@ -4630,6 +4623,97 @@ fn render_operations_package_init() -> String { output } +/// Renders the mixins that turn System Nexus operations into operation-specific +/// workflow-outbound interception points. +fn render_system_nexus_interceptor(services: &[RenderedService<'_>]) -> String { + let operations = services + .iter() + .flat_map(|service| { + service + .operations + .iter() + .map(move |operation| (service, operation)) + }) + .collect::>(); + let mut output = String::new(); + render_generated_file_header(&mut output); + output.push_str("\n\nimport abc\nimport typing\n\nfrom temporalio.nexus.system import TEMPORAL_SYSTEM_ENDPOINT\n\nfrom . import models\n\nif typing.TYPE_CHECKING:\n import temporalio.workflow\n from temporalio.worker._interceptor import StartNexusOperationInput\n\n\n__all__ = [\n \"_start_system_nexus_operation\",\n \"_SystemNexusWorkflowOutboundInterceptorBase\",\n \"_SystemNexusWorkflowOutboundInterceptorTerminal\",\n]\n\n\n_InputT = typing.TypeVar(\"_InputT\")\n_OutputT = typing.TypeVar(\"_OutputT\")\n\n\n"); + output.push_str( + "async def _start_system_nexus_operation(\n interceptor: _SystemNexusWorkflowOutboundInterceptorBase,\n input: StartNexusOperationInput[_InputT, _OutputT],\n) -> temporalio.workflow.NexusOperationHandle[_OutputT]:\n", + ); + for (service, operation) in &operations { + let output_type = system_nexus_type_expr(&operation.output_type_expr); + output.push_str(" if input.service == "); + output.push_str(&python_string_literal(service.wire_name)); + output.push_str(" and input.operation_name == "); + output.push_str(&python_string_literal(operation.wire_name)); + output.push_str( + ":\n typed_input = typing.cast(\n \"StartNexusOperationInput[", + ); + output.push_str(operation_input_type_ref(operation)); + output.push_str(", "); + output.push_str(&output_type); + output.push_str("]\",\n input,\n )\n # The dispatch check above establishes that this operation's response type is _OutputT.\n return typing.cast(\n \"temporalio.workflow.NexusOperationHandle[_OutputT]\",\n await interceptor.start_"); + output.push_str(&operation.attr_name); + output.push_str("(typed_input.input),\n )\n"); + } + output.push_str(" raise ValueError(f\"unsupported System Nexus operation: {input.service}/{input.operation_name}\")\n"); + + output.push_str("\n\nclass _SystemNexusWorkflowOutboundInterceptorBase(abc.ABC):\n"); + output.push_str(" @abc.abstractmethod\n def _next_system_nexus_interceptor(\n self,\n ) -> _SystemNexusWorkflowOutboundInterceptorBase:\n ...\n"); + for (_service, operation) in &operations { + let output_type = system_nexus_type_expr(&operation.output_type_expr); + output.push_str("\n async def start_"); + output.push_str(&operation.attr_name); + output.push_str("(\n self, request: "); + output.push_str(operation_input_type_ref(operation)); + output.push_str("\n ) -> temporalio.workflow.NexusOperationHandle["); + output.push_str(&output_type); + output.push_str("]:\n"); + output.push_str(" \"\"\"Intercept the "); + output.push_str(operation.name); + output.push_str(" operation.\"\"\"\n"); + output.push_str(" return await self._next_system_nexus_interceptor().start_"); + output.push_str(&operation.attr_name); + output.push_str("(request)\n"); + } + + output.push_str("\n\nclass _SystemNexusWorkflowOutboundInterceptorTerminal(abc.ABC):\n"); + output.push_str(" @abc.abstractmethod\n async def _outbound_start_nexus_operation(\n self,\n input: StartNexusOperationInput[_InputT, _OutputT],\n ) -> temporalio.workflow.NexusOperationHandle[_OutputT]:\n ...\n"); + for (service, operation) in operations { + let output_type = system_nexus_type_expr(&operation.output_type_expr); + output.push_str("\n async def start_"); + output.push_str(&operation.attr_name); + output.push_str("(\n self, request: "); + output.push_str(operation_input_type_ref(operation)); + output.push_str("\n ) -> temporalio.workflow.NexusOperationHandle["); + output.push_str(&output_type); + output.push_str("]:\n"); + output.push_str( + " from temporalio.worker._interceptor import StartNexusOperationInput\n", + ); + output + .push_str(" from temporalio.workflow import NexusOperationCancellationType\n\n"); + output.push_str(" return await self._outbound_start_nexus_operation(\n"); + output.push_str(" StartNexusOperationInput(\n endpoint=TEMPORAL_SYSTEM_ENDPOINT,\n service="); + output.push_str(&python_string_literal(service.wire_name)); + output.push_str(",\n operation="); + output.push_str(&python_string_literal(operation.wire_name)); + output.push_str(",\n input=request,\n output_type="); + output.push_str(&output_type); + output.push_str(",\n schedule_to_close_timeout=None,\n schedule_to_start_timeout=None,\n start_to_close_timeout=None,\n cancellation_type=NexusOperationCancellationType.WAIT_COMPLETED,\n headers=None,\n summary=None,\n )\n )\n"); + } + output +} + +fn system_nexus_type_expr(type_expr: &str) -> String { + if type_expr == "None" || type_expr.contains('.') || type_expr.contains('[') { + type_expr.to_string() + } else { + format!("models.{type_expr}") + } +} + fn function_type_parameters( functions: &[RenderedFunctionField], output_type_parameters: &BTreeSet, @@ -7678,6 +7762,7 @@ mod tests { generate_files_for_tree_with_mode_and_options, generate_source, }; use crate::language::Language; + use crate::nexgen_config::{NexgenConfig, current, scope}; use crate::spec::ApiSpecTree; use crate::spec::{LanguageImportSpec, LanguageImportStyle}; @@ -7915,6 +8000,10 @@ class Example(enum.Enum): let descriptors = DescriptorIndex::load(&root.join("advanced/samples/descriptors/temporal_api.bin")) .unwrap(); + let _scope = scope(NexgenConfig { + system_nexus: true, + ..current() + }); let generated = generate_files_for_tree_with_mode_and_options( Language::Python, ApiSpecTree::single(spec.clone()), diff --git a/src/generator/typescript.rs b/src/generator/typescript.rs index b8072def..6a869193 100644 --- a/src/generator/typescript.rs +++ b/src/generator/typescript.rs @@ -86,9 +86,9 @@ pub(crate) fn render_tree_support_files( fn generate_tree( branch: &ApiSpecBranch, support: &crate::SupportFiles, - mode: GenerationMode, ts_date_time_types: TsDateTimeTypes, ) -> Result { + let mode = crate::nexgen_config::current().mode; let mut files = BTreeMap::new(); let mut warnings = Vec::new(); let tree_support_files = render_tree_support_files(branch); @@ -97,14 +97,7 @@ fn generate_tree( insert_branch_index_file(&mut files, branch, has_json_runtime_module)?; insert_files(&mut files, tree_support_files)?; for node in branch.children.values() { - generate_tree_node( - node, - support, - mode, - ts_date_time_types, - &mut files, - &mut warnings, - )?; + generate_tree_node(node, support, ts_date_time_types, &mut files, &mut warnings)?; } Ok(GeneratedFiles { layout: crate::generator::GeneratedOutputLayout::Directory, @@ -116,7 +109,6 @@ fn generate_tree( fn generate_tree_node( node: &ApiSpecNode, support: &crate::SupportFiles, - mode: GenerationMode, ts_date_time_types: TsDateTimeTypes, files: &mut BTreeMap, warnings: &mut Vec, @@ -124,8 +116,7 @@ fn generate_tree_node( match node { ApiSpecNode::Leaf(leaf) => { let support_fragments = support_fragments_for_plan(&leaf.spec, support); - let generated = - generate_leaf(&leaf.spec, &support_fragments, mode, ts_date_time_types)?; + let generated = generate_leaf(&leaf.spec, &support_fragments, ts_date_time_types)?; warnings.extend(generated.warnings); let prefix = leaf.module_path.to_path_buf(); for (path, mut contents) in generated.files { @@ -141,7 +132,7 @@ fn generate_tree_node( ApiSpecNode::Branch(branch) => { insert_branch_index_file(files, branch, false)?; for node in branch.children.values() { - generate_tree_node(node, support, mode, ts_date_time_types, files, warnings)?; + generate_tree_node(node, support, ts_date_time_types, files, warnings)?; } Ok(()) } @@ -1889,7 +1880,6 @@ impl PlannedOperationExt for PlannedOperation { pub(crate) fn generate( tree: &crate::spec::ApiSpecTree, support: &crate::SupportFiles, - mode: GenerationMode, ts_date_time_types: TsDateTimeTypes, ) -> Result { // A `$ref` resolves against the whole input closure ([[ref]] @@ -1900,9 +1890,9 @@ pub(crate) fn generate( match &tree.root { ApiSpecNode::Leaf(leaf) => { let support_fragments = support_fragments_for_plan(&leaf.spec, support); - generate_leaf(&leaf.spec, &support_fragments, mode, ts_date_time_types) + generate_leaf(&leaf.spec, &support_fragments, ts_date_time_types) } - ApiSpecNode::Branch(branch) => generate_tree(branch, support, mode, ts_date_time_types), + ApiSpecNode::Branch(branch) => generate_tree(branch, support, ts_date_time_types), } } @@ -1938,7 +1928,6 @@ fn collect_tree_json_models_into( fn generate_leaf( api_plan: &PlannedSpec, support_fragments: &[SupportFragmentSpec], - mode: GenerationMode, ts_date_time_types: TsDateTimeTypes, ) -> Result { reject_support_namespaces(Language::TypeScript, support_fragments)?; @@ -2013,7 +2002,6 @@ fn generate_leaf( &language_imports, support_source.as_deref(), api_plan, - mode, ) } @@ -3087,8 +3075,8 @@ fn render_module_files( language_imports: &[LanguageImportSpec], support_source: Option<&str>, api_plan: &PlannedSpec, - mode: GenerationMode, ) -> Result { + let mode = crate::nexgen_config::current().mode; let support_source = support_source.filter(|source| !source.trim().is_empty()); let support_exports = support_source.map(support_exports); let module_model_names = model_fragments @@ -3109,7 +3097,6 @@ fn render_module_files( language_imports, support_exports.as_ref(), api_plan, - mode, ); // A module whose every operation type is `$ref`d from another file declares // nothing of its own. Emitting the empty `models.ts` anyway would leave the @@ -3729,8 +3716,8 @@ fn render_models_module( language_imports: &[LanguageImportSpec], support_exports: Option<&SupportExports>, api_plan: &PlannedSpec, - mode: GenerationMode, ) -> String { + let mode = crate::nexgen_config::current().mode; let mut body = String::new(); if mode == GenerationMode::NativeApi { diff --git a/src/lib.rs b/src/lib.rs index 473ee959..4e5beebe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ pub mod error; pub mod generator; pub mod json_schema; pub mod language; +pub mod nexgen_config; pub mod parser; pub mod spec; @@ -31,13 +32,13 @@ pub struct SupportFiles { } pub struct GenerateRequest { + pub config: nexgen_config::NexgenConfig, pub language: Language, pub input_paths: Vec, pub support_paths: Vec, pub descriptor_paths: Vec, pub output_path: PathBuf, pub format: bool, - pub generate_native_api: bool, /// Java-only: the base package for generated types. Its last /// dot-separated segment must match the output directory's name. Ignored /// for other languages. @@ -48,6 +49,7 @@ pub struct GenerateRequest { } pub fn generate_to_file(request: &GenerateRequest) -> Result<()> { + let _config_scope = nexgen_config::scope(request.config); // A resolved output path with no name at all (the filesystem root, or // `..` past it) is never a real output directory: Go and Java derive // package names from its basename, and for every language it means the @@ -80,18 +82,7 @@ pub fn generate_to_file(request: &GenerateRequest) -> Result<()> { }, ts_date_time_types: request.ts_date_time_types, }; - let generated = compile_tree_to_files( - request.language, - tree, - &descriptors, - &support, - if request.generate_native_api { - GenerationMode::NativeApi - } else { - GenerationMode::DefinitionsOnly - }, - options, - )?; + let generated = compile_tree_to_files(request.language, tree, &descriptors, &support, options)?; print_warnings(&generated); write_generated_files(&request.output_path, &generated)?; @@ -111,9 +102,9 @@ pub(crate) fn compile_tree_to_files( authored_tree: ApiSpecTree, descriptors: &DescriptorIndex, support: &SupportFiles, - mode: GenerationMode, options: GenerateFilesOptions, ) -> Result { + let mode = nexgen_config::current().mode; // parse (frontend) -> validate authored intent -> select target metadata let authored_tree = planning::AuthoredValidationPass::new(descriptors, language).apply(authored_tree)?; @@ -142,13 +133,7 @@ pub(crate) fn compile_tree_to_files( // planned IR -> emitted JSON names -> render target-language files let name_resolution = planning::EmittedNameResolutionPass::new(language, &planned_tree)?; let generator_ready_tree = name_resolution.apply(planned_tree)?; - generator::generate_files_from_planned_tree( - language, - &generator_ready_tree, - support, - mode, - options, - ) + generator::generate_files_from_planned_tree(language, &generator_ready_tree, support, options) } /// The output directory's basename, used as the Go package name — matching @@ -450,13 +435,13 @@ mod tests { fn java_request(output_path: &str, java_package_name: Option<&str>) -> GenerateRequest { GenerateRequest { + config: Default::default(), language: Language::Java, input_paths: Vec::new(), support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: PathBuf::from(output_path), format: false, - generate_native_api: false, java_package_name: java_package_name.map(str::to_string), ts_date_time_types: Default::default(), } diff --git a/src/main.rs b/src/main.rs index e6025e3d..598d9408 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,8 @@ use clap::{Args, Parser, Subcommand, ValueEnum}; use nexgen::generator::TsDateTimeTypes; use nexgen::language::Language; #[cfg(feature = "advanced")] +use nexgen::nexgen_config::NexgenConfig; +#[cfg(feature = "advanced")] use nexgen::parser::write_prepared_wit_directory; #[cfg(feature = "advanced")] use nexgen::{AddMessageRequest, AddRpcRequest, add_message_to_file, add_rpc_to_file}; @@ -65,6 +67,10 @@ struct GenerateArgs { #[cfg(feature = "advanced")] #[arg(long = "native-api")] generate_native_api: bool, + /// Generate Temporal System Nexus-specific bindings. + #[cfg(feature = "advanced")] + #[arg(long = "system-nexus")] + system_nexus: bool, } #[derive(Args)] @@ -162,12 +168,33 @@ fn main() -> ExitCode { Default::default(), Some(args.package_name), )), - Commands::Python(args) => generate_to_file(&generate_request( - Language::Python, - args, - Default::default(), - None, - )), + Commands::Python(args) => { + #[cfg(feature = "advanced")] + { + let system_nexus = args.system_nexus; + let config = NexgenConfig { + mode: if args.generate_native_api { + nexgen::generator::GenerationMode::NativeApi + } else { + nexgen::generator::GenerationMode::DefinitionsOnly + }, + system_nexus, + }; + let mut request = + generate_request(Language::Python, args, Default::default(), None); + request.config = config; + generate_to_file(&request) + } + #[cfg(not(feature = "advanced"))] + { + generate_to_file(&generate_request( + Language::Python, + args, + Default::default(), + None, + )) + } + } Commands::Typescript(args) => generate_to_file(&generate_request( Language::TypeScript, args.common, @@ -208,6 +235,17 @@ fn generate_request( java_package_name: Option, ) -> GenerateRequest { GenerateRequest { + #[cfg(feature = "advanced")] + config: NexgenConfig { + mode: if args.generate_native_api { + nexgen::generator::GenerationMode::NativeApi + } else { + nexgen::generator::GenerationMode::DefinitionsOnly + }, + ..Default::default() + }, + #[cfg(not(feature = "advanced"))] + config: Default::default(), language, input_paths: args.inputs, #[cfg(feature = "advanced")] @@ -223,10 +261,6 @@ fn generate_request( format: args.format, #[cfg(not(feature = "advanced"))] format: false, - #[cfg(feature = "advanced")] - generate_native_api: args.generate_native_api, - #[cfg(not(feature = "advanced"))] - generate_native_api: false, java_package_name, ts_date_time_types, } diff --git a/src/nexgen_config.rs b/src/nexgen_config.rs new file mode 100644 index 00000000..28038e06 --- /dev/null +++ b/src/nexgen_config.rs @@ -0,0 +1,90 @@ +//! Scoped generation settings shared across the compiler. +//! +//! Configuration is thread-local so concurrent in-process callers cannot affect +//! one another. Scopes nest and restore the prior configuration during unwinding. + +use std::cell::RefCell; + +use crate::generator::GenerationMode; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct NexgenConfig { + pub mode: GenerationMode, + pub system_nexus: bool, +} + +impl Default for NexgenConfig { + fn default() -> Self { + Self { + mode: GenerationMode::DefinitionsOnly, + system_nexus: false, + } + } +} + +thread_local! { + static CONFIGS: RefCell> = RefCell::new(Vec::new()); +} + +pub fn current() -> NexgenConfig { + CONFIGS.with(|configs| configs.borrow().last().copied().unwrap_or_default()) +} + +pub fn scope(config: NexgenConfig) -> NexgenConfigScope { + CONFIGS.with(|configs| configs.borrow_mut().push(config)); + NexgenConfigScope +} + +pub struct NexgenConfigScope; + +impl Drop for NexgenConfigScope { + fn drop(&mut self) { + CONFIGS.with(|configs| { + configs + .borrow_mut() + .pop() + .expect("NexGen configuration scope must be present"); + }); + } +} + +#[cfg(test)] +mod tests { + use super::{NexgenConfig, current, scope}; + use crate::generator::GenerationMode; + + #[test] + fn restores_nested_configuration_after_unwinding() { + let outer = NexgenConfig { + mode: GenerationMode::DefinitionsOnly, + system_nexus: true, + }; + { + let _outer_scope = scope(outer); + assert_eq!(current(), outer); + let result = std::panic::catch_unwind(|| { + let _inner_scope = scope(NexgenConfig::default()); + panic!("test unwind"); + }); + assert!(result.is_err()); + assert_eq!(current(), outer); + } + assert_eq!(current(), NexgenConfig::default()); + } + + #[test] + fn is_isolated_per_thread() { + let config = NexgenConfig { + mode: GenerationMode::DefinitionsOnly, + system_nexus: true, + }; + { + let _scope = scope(config); + assert_eq!(current(), config); + assert_eq!( + std::thread::spawn(current).join().unwrap(), + NexgenConfig::default() + ); + } + } +} diff --git a/tests/doc_rendering.rs b/tests/doc_rendering.rs index b106312f..a0c7764c 100644 --- a/tests/doc_rendering.rs +++ b/tests/doc_rendering.rs @@ -72,13 +72,13 @@ fn generate(language: nexgen::language::Language, java_package_name: Option<&str let output_path = temp_dir.join(output_dir_name); fs::write(&input_path, HOSTILE_DOCUMENTATION_SCHEMA).unwrap(); generate_to_file(&GenerateRequest { + config: Default::default(), language, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: true, java_package_name: java_package_name.map(str::to_string), ts_date_time_types: Default::default(), }) diff --git a/tests/generate_dotnet.rs b/tests/generate_dotnet.rs index 5e7636e9..633736c7 100644 --- a/tests/generate_dotnet.rs +++ b/tests/generate_dotnet.rs @@ -139,13 +139,16 @@ fn generate_dotnet_files( let temp_dir = unique_output_path("dotnet-rendered"); let output_path = temp_dir.join("output"); generate_to_file(&GenerateRequest { + config: nexgen::nexgen_config::NexgenConfig { + mode: nexgen::generator::GenerationMode::NativeApi, + ..Default::default() + }, language: nexgen::language::Language::Dotnet, input_paths: input_paths.to_vec(), support_paths: Vec::new(), descriptor_paths: descriptor_paths.to_vec(), output_path: output_path.clone(), format: false, - generate_native_api: true, java_package_name: None, ts_date_time_types: Default::default(), }) diff --git a/tests/generate_go.rs b/tests/generate_go.rs index 38fc432d..11ddc41a 100644 --- a/tests/generate_go.rs +++ b/tests/generate_go.rs @@ -214,13 +214,16 @@ fn generate_to_string_with_inputs( let temp_dir = unique_output_path("go-rendered"); let output_path = temp_dir.join("output"); generate_to_file(&GenerateRequest { + config: nexgen::nexgen_config::NexgenConfig { + mode: nexgen::generator::GenerationMode::NativeApi, + ..Default::default() + }, language, input_paths: input_paths.to_vec(), support_paths: Vec::new(), descriptor_paths: descriptor_paths.to_vec(), output_path: output_path.clone(), format: false, - generate_native_api: true, java_package_name: None, ts_date_time_types: Default::default(), })?; @@ -1025,13 +1028,13 @@ fn go_json_package_name_derives_from_output_directory_name() { let temp_dir = unique_output_path("go-json-package-name-fallback"); let output_path = temp_dir.join("widgets"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![json_input_path(&root, "chat")], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: true, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1723,13 +1726,13 @@ fn go_rejects_inputs_flattening_to_the_same_module_file() { let output_path = temp_dir.join("output"); let result = generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![temp_dir.clone()], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path, format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }); @@ -1761,13 +1764,13 @@ fn go_rejects_reserved_generated_name_collision() { let output_path = temp_dir.join("output"); let result = generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![temp_dir.clone()], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path, format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }); @@ -1832,13 +1835,13 @@ fn go_json_names_inline_object_union_branch() { let output_path = temp_dir.join("detail"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1870,13 +1873,13 @@ fn go_json_decodes_element_position_unions() { let output_path = temp_dir.join("bag"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1969,13 +1972,13 @@ fn go_json_recursively_converts_and_validates_element_positions() { let output_path = temp_dir.join("recursive"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2101,13 +2104,13 @@ fn go_json_validates_non_object_union_branch_constraints() { let output_path = temp_dir.join("bc"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2155,13 +2158,13 @@ properties: let output_path = temp_dir.join("note"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2200,13 +2203,13 @@ properties: let output_path = temp_dir.join("output"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2273,13 +2276,13 @@ $defs: let output_path = temp_dir.join("mixed"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2377,13 +2380,13 @@ properties: .unwrap(); let output_path = temp_dir.join("matcher"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2486,13 +2489,13 @@ services: .unwrap(); let output_path = temp_dir.join("service"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2531,13 +2534,13 @@ $defs: .unwrap(); let output_path = temp_dir.join("temporal_wire"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2624,13 +2627,13 @@ properties: .unwrap(); let output_path = temp_dir.join("native"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2690,13 +2693,13 @@ fn go_json_wave3_pairwise_runtime_matrix() { fs::write(&input_path, GO_WAVE3_MATRIX_SCHEMA).unwrap(); let output_path = temp_dir.join("audit"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2971,13 +2974,13 @@ fn go_json_cross_module_go_name_override_moves_every_reference() { let output_path = temp_dir.join("output"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_dir], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -3036,13 +3039,13 @@ fn go_json_override_moves_member_derived_names_only() { let output_path = temp_dir.join("probe"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -3101,13 +3104,13 @@ services: .unwrap(); let error = generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_dir.clone()], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: temp_dir.join("out"), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -3123,13 +3126,13 @@ services: // Java gives each module its own sub-package (`…pkg.a.page`, `…pkg.b.page`) // and emits no aggregating barrel, so the two `Page` classes stay distinct. generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_dir], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: temp_dir.join("pkg"), format: false, - generate_native_api: false, java_package_name: Some("com.example.pkg".to_string()), ts_date_time_types: Default::default(), }) @@ -3178,13 +3181,13 @@ $defs: .unwrap(); let error = generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_dir], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: temp_dir.join("out"), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -3229,13 +3232,13 @@ services: let output_path = temp_dir.join("out"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_dir], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -3288,13 +3291,13 @@ properties: .unwrap(); let output_path = temp_dir.join("fmod"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -3400,13 +3403,13 @@ properties: .unwrap(); let output_path = temp_dir.join("nullable"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -3550,13 +3553,13 @@ $defs: let output_path = temp_dir.join("out"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_dir], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -3625,13 +3628,13 @@ properties: .unwrap(); let output_path = temp_dir.join("mat"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -3764,13 +3767,13 @@ properties: .unwrap(); let output_path = temp_dir.join("bag"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -3854,13 +3857,13 @@ $defs: .unwrap(); let output_path = temp_dir.join("shapes"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -3980,13 +3983,13 @@ $defs: .unwrap(); let output_path = temp_dir.join("shapes"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -4039,13 +4042,13 @@ properties: .unwrap(); let output_path = temp_dir.join("nums"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -4131,13 +4134,13 @@ properties: .unwrap(); let output_path = temp_dir.join("elements"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -4251,13 +4254,13 @@ properties: .unwrap(); let output_path = temp_dir.join("names"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Go, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) diff --git a/tests/generate_java.rs b/tests/generate_java.rs index e79c1871..33ed9e3b 100644 --- a/tests/generate_java.rs +++ b/tests/generate_java.rs @@ -318,7 +318,7 @@ fn read_java_files(dir: &Path) -> BTreeMap { /// Regenerates one example into a temp Gradle-shaped tree so the derived Java /// package matches the checked-in output, then compares the emitted files. -fn assert_regeneration_matches(mode: &str, generate_native_api: bool) { +fn assert_regeneration_matches(mode: &str, generation_mode: nexgen::generator::GenerationMode) { let root = project_root(); for example_id in ["chat", "kb", "showcase", "temporal"] { let temp_dir = unique_output_path(&format!("java-json-{mode}-{example_id}")); @@ -327,13 +327,16 @@ fn assert_regeneration_matches(mode: &str, generate_native_api: bool) { let output_path = temp_dir.join(example_id); generate_to_file(&GenerateRequest { + config: nexgen::nexgen_config::NexgenConfig { + mode: generation_mode, + ..Default::default() + }, language: nexgen::language::Language::Java, input_paths: vec![json_input_path(&root, example_id)], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api, java_package_name: Some(format!("json_schema.{mode}.{example_id}")), ts_date_time_types: Default::default(), }) @@ -392,12 +395,15 @@ fn assert_regeneration_matches(mode: &str, generate_native_api: bool) { #[test] fn java_json_example_generation_matches_checked_in_output() { - assert_regeneration_matches("definitions", false); + assert_regeneration_matches( + "definitions", + nexgen::generator::GenerationMode::DefinitionsOnly, + ); } #[test] fn java_json_api_example_generation_matches_checked_in_output() { - assert_regeneration_matches("api", true); + assert_regeneration_matches("api", nexgen::generator::GenerationMode::NativeApi); } /// A structured inline object branch of a property-level union is named @@ -414,13 +420,13 @@ fn java_json_names_inline_object_union_branch() { let output_path = temp_dir.join("detail"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("detail".to_string()), ts_date_time_types: Default::default(), }) @@ -466,13 +472,13 @@ fn java_json_validates_non_object_union_branch_constraints() { let output_path = temp_dir.join("bc"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("bc".to_string()), ts_date_time_types: Default::default(), }) @@ -515,13 +521,13 @@ fn java_json_rejects_non_finite_numbers_in_every_serialize_position() { let output_path = temp_dir.join("finite"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("finite".to_string()), ts_date_time_types: Default::default(), }) @@ -580,13 +586,13 @@ fn java_json_emits_runtime_support_for_nested_materialized_values() { let output_path = temp_dir.join("nested"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("nested".to_string()), ts_date_time_types: Default::default(), }) @@ -649,13 +655,13 @@ fn java_json_emits_wave2_object_and_matcher_contracts() { let output_path = temp_dir.join("java_conformance"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("java_conformance".to_string()), ts_date_time_types: Default::default(), }) @@ -743,13 +749,13 @@ fn java_json_emits_deprecated_services_and_operations() { let output_path = temp_dir.join("legacy"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: true, java_package_name: Some("legacy".to_string()), ts_date_time_types: Default::default(), }) @@ -785,13 +791,13 @@ fn java_json_rejects_empty_service_descriptions() { let input_path = temp_dir.join("empty.nexusrpc.yaml"); fs::write(&input_path, JAVA_EMPTY_SERVICE_DESCRIPTION_SCHEMA).unwrap(); let error = generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: temp_dir.join("empty"), format: false, - generate_native_api: true, java_package_name: Some("empty".to_string()), ts_date_time_types: Default::default(), }) @@ -819,13 +825,13 @@ fn java_json_decodes_element_position_unions() { let output_path = temp_dir.join("bag"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("bag".to_string()), ts_date_time_types: Default::default(), }) @@ -928,13 +934,13 @@ fn java_json_cross_module_java_name_override_moves_every_reference() { let output_path = temp_dir.join("pages"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_dir], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("example.pages".to_string()), ts_date_time_types: Default::default(), }) @@ -1007,13 +1013,13 @@ services: let output_path = temp_dir.join("pkg"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_dir], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("com.example.pkg".to_string()), ts_date_time_types: Default::default(), }) @@ -1075,13 +1081,13 @@ fn java_json_nullable_property_keeps_the_branch_constraints() { let output_path = temp_dir.join("nullable_shape"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("nullable_shape".to_string()), ts_date_time_types: Default::default(), }) @@ -1151,13 +1157,13 @@ fn java_json_serialize_side_guards_match_the_parse_side() { let output_path = temp_dir.join("guards"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("guards".to_string()), ts_date_time_types: Default::default(), }) @@ -1235,13 +1241,13 @@ fn java_json_dispatches_a_non_string_discriminant_by_value() { let output_path = temp_dir.join("tagged"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("tagged".to_string()), ts_date_time_types: Default::default(), }) @@ -1294,13 +1300,13 @@ fn java_json_repaths_nested_violations_on_serialize() { let output_path = temp_dir.join("nested_path"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("nested_path".to_string()), ts_date_time_types: Default::default(), }) @@ -1353,13 +1359,13 @@ fn java_json_compiles_contains_matcher_regexes_once() { let output_path = temp_dir.join("contains_regex"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("contains_regex".to_string()), ts_date_time_types: Default::default(), }) @@ -1416,13 +1422,13 @@ fn java_json_member_javadoc_lands_on_the_getter() { let output_path = temp_dir.join("member_doc"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("member_doc".to_string()), ts_date_time_types: Default::default(), }) @@ -1478,13 +1484,13 @@ fn java_json_typed_map_union_branch_implements_the_interface() { let output_path = temp_dir.join("union_map"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("union_map".to_string()), ts_date_time_types: Default::default(), }) @@ -1554,13 +1560,13 @@ fn java_json_nullable_element_keeps_the_branch_constraints() { let output_path = temp_dir.join("nullable_element"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("nullable_element".to_string()), ts_date_time_types: Default::default(), }) @@ -1633,13 +1639,13 @@ fn java_json_writes_materialized_collection_elements_itself() { let output_path = temp_dir.join("materialized"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("materialized".to_string()), ts_date_time_types: Default::default(), }) @@ -1714,13 +1720,13 @@ fn java_json_enum_name_override_reaches_non_string_members() { let output_path = temp_dir.join("enum_names"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("enum_names".to_string()), ts_date_time_types: Default::default(), }) @@ -1794,13 +1800,13 @@ properties: let output_path = temp_dir.join("naming"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("naming".to_string()), ts_date_time_types: Default::default(), }) @@ -1863,13 +1869,13 @@ properties: .unwrap(); let output_path = temp_dir.join("spelling"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("spelling".to_string()), ts_date_time_types: Default::default(), }) @@ -1910,13 +1916,13 @@ properties: .unwrap(); let error = generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: temp_dir.join("collide"), format: false, - generate_native_api: false, java_package_name: Some("collide".to_string()), ts_date_time_types: Default::default(), }) @@ -1951,13 +1957,13 @@ properties: let output_path = temp_dir.join("fracsec"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Java, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: Some("fracsec".to_string()), ts_date_time_types: Default::default(), }) diff --git a/tests/generate_python.rs b/tests/generate_python.rs index 12c16f11..e73e3a3a 100644 --- a/tests/generate_python.rs +++ b/tests/generate_python.rs @@ -761,13 +761,16 @@ fn generate_python_to_string(input_paths: &[PathBuf], descriptor_paths: &[PathBu let temp_dir = unique_output_path("python-rendered"); let output_path = temp_dir.join("output"); generate_to_file(&GenerateRequest { + config: nexgen::nexgen_config::NexgenConfig { + mode: nexgen::generator::GenerationMode::NativeApi, + ..Default::default() + }, language: nexgen::language::Language::Python, input_paths: input_paths.to_vec(), support_paths: Vec::new(), descriptor_paths: descriptor_paths.to_vec(), output_path: output_path.clone(), format: false, - generate_native_api: true, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -788,13 +791,13 @@ fn generate_python_package_files( let temp_dir = unique_output_path("python-package"); let output_path = temp_dir.join("output"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Python, input_paths: input_paths.to_vec(), support_paths: Vec::new(), descriptor_paths: descriptor_paths.to_vec(), output_path: output_path.clone(), format: false, - generate_native_api: true, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -816,6 +819,9 @@ fn generate_formatted_python_output(root: &Path, example_id: &str, output_path: output_path.to_str().unwrap(), "--native-api", ]); + if example_id == PRIMARY_EXAMPLE_ID { + command.arg("--system-nexus"); + } let status = command.status().unwrap(); assert!(status.success()); @@ -1606,13 +1612,13 @@ fn python_json_names_inline_object_union_branch() { let output_path = temp_dir.join("detail"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Python, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1643,13 +1649,13 @@ fn python_json_validates_non_object_union_branch_constraints() { let output_path = temp_dir.join("bc"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Python, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1691,13 +1697,13 @@ fn python_json_enforces_remaining_scalar_and_typed_extra_contracts() { let output_path = temp_dir.join("contract_package"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Python, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1747,13 +1753,13 @@ $defs: let output_path = temp_dir.join("contract_package"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Python, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: true, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1800,13 +1806,13 @@ fn python_json_union_serializer_validates_before_dispatching() { let output_path = temp_dir.join("bag_package"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Python, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1866,13 +1872,13 @@ fn python_json_annotates_element_position_unions() { let output_path = temp_dir.join("bag"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Python, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1956,13 +1962,13 @@ fn python_json_cross_module_py_name_override_moves_every_reference() { let output_path = temp_dir.join("output"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Python, input_paths: vec![input_dir], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2018,13 +2024,13 @@ fn python_json_rejects_same_type_name_in_two_modules() { .unwrap(); let request = |output: &str| GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Python, input_paths: vec![input_dir.clone()], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: temp_dir.join(output), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }; @@ -2091,13 +2097,13 @@ services: let output_path = temp_dir.join("out"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Python, input_paths: vec![input_dir], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2143,13 +2149,13 @@ fn python_json_property_names_never_shadow_converter_locals() { let output_path = temp_dir.join("shadow_package"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Python, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2226,13 +2232,13 @@ fn python_json_model_properties_use_union_none_and_defaults_preserve_presence() let output_path = temp_dir.join("default_package"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Python, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2272,20 +2278,20 @@ fn python_json_model_properties_use_union_none_and_defaults_preserve_presence() fn generate_unformatted_python_package( temp_dir: &Path, schema_source: &str, - generate_native_api: bool, + config: nexgen::nexgen_config::NexgenConfig, ) -> PathBuf { fs::create_dir_all(temp_dir).unwrap(); let input_path = temp_dir.join("probe.nexusrpc.yaml"); fs::write(&input_path, schema_source).unwrap(); let output_path = temp_dir.join("probe_package"); generate_to_file(&GenerateRequest { + config, language: nexgen::language::Language::Python, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2325,7 +2331,7 @@ $defs: description: 'Trailing quote "' type: string "##, - false, + nexgen::nexgen_config::NexgenConfig::default(), ); let models = fs::read_to_string(output_path.join("models.py")).unwrap(); @@ -2380,7 +2386,7 @@ $defs: properties: a: { type: string } "##, - false, + nexgen::nexgen_config::NexgenConfig::default(), ); let services = fs::read_to_string(output_path.join("services.py")).unwrap(); @@ -2421,7 +2427,7 @@ $defs: uniqueItems: true items: { type: string } "##, - false, + nexgen::nexgen_config::NexgenConfig::default(), ); let models = fs::read_to_string(output_path.join("models.py")).unwrap(); @@ -2455,7 +2461,7 @@ $defs: dd: { type: string, format: date } tt: { type: string, format: time } "##, - false, + nexgen::nexgen_config::NexgenConfig::default(), ); let definitions = fs::read_to_string(output_path.join("_definitions.py")).unwrap(); @@ -2517,7 +2523,7 @@ $defs: maximum: 9007199254740992 i: { type: integer } "##, - false, + nexgen::nexgen_config::NexgenConfig::default(), ); let models = fs::read_to_string(output_path.join("models.py")).unwrap(); @@ -2589,7 +2595,7 @@ $defs: items: { type: number } contains: { enum: [1.5, 2] } "##, - false, + nexgen::nexgen_config::NexgenConfig::default(), ); let models = fs::read_to_string(output_path.join("models.py")).unwrap(); @@ -2643,13 +2649,13 @@ $defs: ) .unwrap(); let error = generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::Python, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: temp_dir.join("out"), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2680,7 +2686,7 @@ $defs: - { type: string } - { type: integer } "##, - false, + nexgen::nexgen_config::NexgenConfig::default(), ); let models = fs::read_to_string(output_path.join("models.py")).unwrap(); diff --git a/tests/generate_typescript.rs b/tests/generate_typescript.rs index c06232ad..a32a40bf 100644 --- a/tests/generate_typescript.rs +++ b/tests/generate_typescript.rs @@ -615,13 +615,16 @@ fn generate_typescript_to_string(input_paths: &[PathBuf], descriptor_paths: &[Pa let temp_dir = unique_output_path("typescript-rendered"); let output_path = temp_dir.join("output"); generate_to_file(&GenerateRequest { + config: nexgen::nexgen_config::NexgenConfig { + mode: nexgen::generator::GenerationMode::NativeApi, + ..Default::default() + }, language: nexgen::language::Language::TypeScript, input_paths: input_paths.to_vec(), support_paths: Vec::new(), descriptor_paths: descriptor_paths.to_vec(), output_path: output_path.clone(), format: false, - generate_native_api: true, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1157,13 +1160,13 @@ fn typescript_json_names_inline_object_union_branch() { let output_path = temp_dir.join("detail"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1200,13 +1203,13 @@ fn typescript_json_validates_non_object_union_branch_constraints() { let output_path = temp_dir.join("bc"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1247,13 +1250,13 @@ fn typescript_json_recursively_converts_union_array_branches() { let output_path = temp_dir.join("probe"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: nexgen::generator::TsDateTimeTypes::Date, }) @@ -1306,13 +1309,13 @@ fn typescript_json_rejects_non_finite_numbers_at_every_position() { let output_path = temp_dir.join("probe"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1341,13 +1344,13 @@ fn typescript_json_detects_nested_runtime_support() { let output_path = temp_dir.join("probe"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1375,13 +1378,13 @@ fn typescript_json_validates_native_temporals_before_serializing() { let output_path = temp_dir.join("probe"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: repr, }) @@ -1435,13 +1438,13 @@ fn typescript_json_guards_nullable_array_elements_during_serialize_validation() let output_path = temp_dir.join("probe"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1477,13 +1480,13 @@ fn typescript_json_maps_element_position_unions() { let output_path = temp_dir.join("bag"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1516,13 +1519,13 @@ fn typescript_json_one_sided_operation_type_info() { let output_path = temp_dir.join("jobs"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1553,13 +1556,13 @@ fn typescript_json_operation_type_info_follows_ts_name_override() { let output_path = temp_dir.join("pages"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1592,13 +1595,13 @@ fn typescript_json_cross_module_ts_name_override_moves_every_reference() { let output_path = temp_dir.join("output"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_dir], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1650,13 +1653,13 @@ fn typescript_json_override_moves_member_derived_names_only() { let output_path = temp_dir.join("probe"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1692,13 +1695,13 @@ fn typescript_json_rejects_same_type_name_in_two_modules() { fs::write(input_dir.join("b/page.json"), page).unwrap(); let request = |output: &str| GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_dir.clone()], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: temp_dir.join(output), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }; @@ -1777,13 +1780,13 @@ fn typescript_json_service_module_without_own_types_imports_instead_of_reemittin let input_dir = write_service_only_module_closure(&temp_dir); let output_path = temp_dir.join("out"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_dir], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -1836,13 +1839,13 @@ fn typescript_json_emits_complete_matchers_and_typed_mixed_extras() { let output_path = temp_dir.join("probe"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: nexgen::generator::TsDateTimeTypes::Temporal, }) @@ -1993,13 +1996,13 @@ fn typescript_json_emits_complete_property_name_matcher() { fs::write(&input_path, TYPESCRIPT_PROPERTY_NAMES_SCHEMA).unwrap(); let output_path = temp_dir.join("names"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2025,13 +2028,13 @@ fn typescript_json_materializes_closed_values_and_nullable_defaults() { fs::write(&input_path, TYPESCRIPT_MATERIALIZED_CLOSED_SCHEMA).unwrap(); let output_path = temp_dir.join("closed"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: nexgen::generator::TsDateTimeTypes::Temporal, }) @@ -2061,13 +2064,13 @@ fn typescript_json_deprecates_types_fields_services_and_operations() { fs::write(&input_path, TYPESCRIPT_DEPRECATION_SCHEMA).unwrap(); let output_path = temp_dir.join("jobs"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2089,13 +2092,13 @@ fn typescript_json_wave3_pairwise_runtime_matrix() { fs::write(&input_path, TYPESCRIPT_WAVE3_MATRIX_SCHEMA).unwrap(); let output_path = temp_dir.join("audit"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: nexgen::generator::TsDateTimeTypes::Temporal, }) @@ -2409,13 +2412,13 @@ fn typescript_json_wave7_discrete_defects_typecheck_and_run() { fs::write(&input_path, TYPESCRIPT_WAVE7_SCHEMA).unwrap(); let output_path = temp_dir.join("probe"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2597,13 +2600,13 @@ fn typescript_json_dispatches_cross_module_ref_union_branches() { fs::write(&main_path, TYPESCRIPT_CROSS_MODULE_MAIN).unwrap(); let output_path = temp_dir.join("closure"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![main_path, shapes_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2683,13 +2686,13 @@ fn typescript_json_guards_nullable_elements_in_array_keywords() { fs::write(&input_path, TYPESCRIPT_NULLABLE_ELEMENT_SCHEMA).unwrap(); let output_path = temp_dir.join("bag"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2792,13 +2795,13 @@ properties: .unwrap(); let output_path = temp_dir.join("pin"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: Default::default(), }) @@ -2881,13 +2884,13 @@ fn typescript_json_truncates_over_capacity_fractional_seconds() { fs::write(&input_path, TYPESCRIPT_FRACTIONAL_SECOND_SCHEMA).unwrap(); let output_path = temp_dir.join("clock"); generate_to_file(&GenerateRequest { + config: Default::default(), language: nexgen::language::Language::TypeScript, input_paths: vec![input_path], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: None, ts_date_time_types: *repr, }) diff --git a/tests/toolchain/mod.rs b/tests/toolchain/mod.rs index 4c8e9a1e..1df0561c 100644 --- a/tests/toolchain/mod.rs +++ b/tests/toolchain/mod.rs @@ -150,13 +150,13 @@ impl Workspace { fs::create_dir_all(parent).map_err(|error| error.to_string())?; } generate_to_file(&GenerateRequest { + config: Default::default(), language: target.language(), input_paths: schemas.to_vec(), support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: false, java_package_name: (target == Target::Java).then(|| format!("conformance.{dir}")), ts_date_time_types, }) diff --git a/xtask/src/build_examples.rs b/xtask/src/build_examples.rs index f6a4596a..32009a79 100644 --- a/xtask/src/build_examples.rs +++ b/xtask/src/build_examples.rs @@ -6,6 +6,7 @@ use heck::ToSnakeCase; use nexgen::error::{Error, Result}; use nexgen::generator::{GenerationMode, TsDateTimeTypes}; use nexgen::language::Language; +use nexgen::nexgen_config::NexgenConfig; use nexgen::{GenerateRequest, generate_to_file}; #[derive(Clone)] @@ -381,18 +382,22 @@ fn build_example(repo_root: &Path, language: Language, example_id: &str) -> Resu let output_path = example_output_path(repo_root, language, example_id); let language_root = advanced_language_root(repo_root, language); reset_example_output_directory(&language_root, &output_path)?; - generate_to_file(&GenerateRequest { + let generate_request = GenerateRequest { + config: NexgenConfig { + mode: GenerationMode::NativeApi, + system_nexus: language == Language::Python && example_id == "workflow-service", + }, language, input_paths, support_paths: Vec::new(), descriptor_paths: vec![repo_root.join("advanced/samples/descriptors/temporal_api.bin")], output_path: output_path.clone(), format: false, - generate_native_api: true, java_package_name: (language == Language::Java) .then(|| example_directory_name(language, example_id)), ts_date_time_types: Default::default(), - })?; + }; + generate_to_file(&generate_request)?; format_example_output(&language_root, language, &output_path)?; println!("Built {} with nexgen", output_path.display()); Ok(()) @@ -441,13 +446,16 @@ fn build_json_example_variant( let output_path = json_example_output_path(repo_root, language, output_id, mode); reset_example_output_directory(&root, &output_path)?; generate_to_file(&GenerateRequest { + config: NexgenConfig { + mode, + ..Default::default() + }, language, input_paths: vec![input_path.clone()], support_paths: Vec::new(), descriptor_paths: Vec::new(), output_path: output_path.clone(), format: false, - generate_native_api: mode == GenerationMode::NativeApi, java_package_name: (language == Language::Java) .then(|| json_example_java_package(&dir_name, mode)), ts_date_time_types, diff --git a/xtask/src/validate.rs b/xtask/src/validate.rs index 31a42831..c4a6dec9 100644 --- a/xtask/src/validate.rs +++ b/xtask/src/validate.rs @@ -68,7 +68,7 @@ fn validate_python(repo_root: &Path) -> Result<()> { run(&root, "uv", &["sync", "--locked"])?; run(&root, "uv", &["run", "ruff", "check", "."])?; run(&root, "uv", &["run", "ruff", "format", "--check", "."])?; - run(&root, "uv", &["run", "basedpyright"])?; + run(&root, "uv", &["run", "basedpyright", "--warnings"])?; run(&root, "uv", &["run", "pytest"])?; } Ok(())