ssc_codegen 0.35.0__tar.gz → 0.36.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/PKG-INFO +2 -2
  2. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/pyproject.toml +1 -1
  3. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/golang/http_libs/nethttp.py +7 -0
  4. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/golang/rest.py +77 -16
  5. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/golang/runtime.py +1 -0
  6. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/golang/visitor.py +18 -1
  7. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/javascript/rest.py +19 -9
  8. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/.gitignore +0 -0
  9. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/LICENSE +0 -0
  10. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/README.md +0 -0
  11. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/__init__.py +0 -0
  12. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/_logging.py +0 -0
  13. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/ast/__init__.py +0 -0
  14. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/ast/array.py +0 -0
  15. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/ast/base.py +0 -0
  16. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/ast/cast.py +0 -0
  17. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/ast/control.py +0 -0
  18. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/ast/extract.py +0 -0
  19. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/ast/function.py +0 -0
  20. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/ast/helpers.py +0 -0
  21. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/ast/jsondef.py +0 -0
  22. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/ast/module.py +0 -0
  23. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/ast/predicate_containers.py +0 -0
  24. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/ast/predicate_ops.py +0 -0
  25. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/ast/regex.py +0 -0
  26. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/ast/rest.py +0 -0
  27. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/ast/selectors.py +0 -0
  28. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/ast/string.py +0 -0
  29. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/ast/struct.py +0 -0
  30. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/ast/typedef.py +0 -0
  31. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/ast/types.py +0 -0
  32. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/core/__init__.py +0 -0
  33. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/core/contexts.py +0 -0
  34. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/core/expressions.py +0 -0
  35. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/core/format.py +0 -0
  36. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/core/linter.py +0 -0
  37. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/core/module_handler.py +0 -0
  38. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/core/predicates.py +0 -0
  39. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/core/reader.py +0 -0
  40. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/core/rest_artifacts.py +0 -0
  41. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/core/struct_parser.py +0 -0
  42. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/core/type_checking.py +0 -0
  43. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/exceptions.py +0 -0
  44. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/explore.py +0 -0
  45. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/generation/__init__.py +0 -0
  46. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/generation/builder.py +0 -0
  47. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/generation/runtime.py +0 -0
  48. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/health.py +0 -0
  49. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/main.py +0 -0
  50. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/naming.py +0 -0
  51. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/parsers/__init__.py +0 -0
  52. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/parsers/curl.py +0 -0
  53. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/parsers/http.py +0 -0
  54. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/regex_utils.py +0 -0
  55. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/request_spec.py +0 -0
  56. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/__init__.py +0 -0
  57. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/golang/__init__.py +0 -0
  58. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/golang/http_libs/__init__.py +0 -0
  59. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/golang/http_libs/base.py +0 -0
  60. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/golang/literals.py +0 -0
  61. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/golang/regex.py +0 -0
  62. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/javascript/__init__.py +0 -0
  63. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/javascript/http_libs/__init__.py +0 -0
  64. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/javascript/http_libs/axios.py +0 -0
  65. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/javascript/http_libs/base.py +0 -0
  66. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/javascript/http_libs/fetch.py +0 -0
  67. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/javascript/visitor.py +0 -0
  68. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/profile.py +0 -0
  69. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/python/__init__.py +0 -0
  70. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/python/html_libs/__init__.py +0 -0
  71. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/python/html_libs/base.py +0 -0
  72. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/python/html_libs/bs4.py +0 -0
  73. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/python/html_libs/lxml.py +0 -0
  74. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/python/html_libs/parsel.py +0 -0
  75. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/python/html_libs/slax.py +0 -0
  76. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/python/http_libs/__init__.py +0 -0
  77. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/python/http_libs/aiohttp.py +0 -0
  78. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/python/http_libs/base.py +0 -0
  79. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/python/http_libs/httpx.py +0 -0
  80. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/python/http_libs/requests.py +0 -0
  81. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/python/rest.py +0 -0
  82. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/python/visitor.py +0 -0
  83. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/resolver.py +0 -0
  84. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/targets/spec.py +0 -0
  85. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/traversal/__init__.py +0 -0
  86. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/traversal/context.py +0 -0
  87. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/traversal/utils.py +0 -0
  88. {ssc_codegen-0.35.0 → ssc_codegen-0.36.1}/ssc_codegen/traversal/walker.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: ssc_codegen
3
- Version: 0.35.0
3
+ Version: 0.36.1
4
4
  Summary: Python-dsl code converter to html parser for web scraping
5
5
  Project-URL: Documentation, https://github.com/vypivshiy/selector_schema_codegen#readme
6
6
  Project-URL: Issues, https://github.com/vypivshiy/selector_schema_codegen/issues
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ssc_codegen"
3
- version = "0.35.0"
3
+ version = "0.36.1"
4
4
  description = "Python-dsl code converter to html parser for web scraping "
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -38,6 +38,13 @@ _NETHTTP_REST_RUNTIME: list[str] = [
38
38
  "\t\t\t\t}",
39
39
  "\t\t\t}",
40
40
  "\t\t}",
41
+ "\t\tif len(opts.Cookies) > 0 {",
42
+ "\t\t\tfor name, vs := range opts.Cookies {",
43
+ "\t\t\t\tfor _, v := range vs {",
44
+ "\t\t\t\t\treq.AddCookie(&http.Cookie{Name: name, Value: v})",
45
+ "\t\t\t\t}",
46
+ "\t\t\t}",
47
+ "\t\t}",
41
48
  '\t\tif opts.Body != "" {',
42
49
  "\t\t\treq.Body = io.NopCloser(strings.NewReader(opts.Body))",
43
50
  "\t\t}",
@@ -90,17 +90,18 @@ def render_body_json(tmpl: PlaceholderTemplate) -> str:
90
90
  return f"fmt.Sprintf({_go_str(fmt_str)}, {', '.join(args)})"
91
91
 
92
92
 
93
- def render_form_body(body: dict[str, PlaceholderTemplate]) -> str:
94
- """Render a form-urlencoded dict body as ``url.Values{...}.Encode()``.
93
+ def render_url_values(d: dict[str, PlaceholderTemplate]) -> str:
94
+ """Render a ``dict[str, PlaceholderTemplate]`` as ``url.Values{...}.Encode()``.
95
95
 
96
- Keys are literal strings; values may contain placeholders (rendered via
97
- ``fmt.Sprintf`` through ``render_url``). ``url.Values`` properly escapes
98
- both keys and values per RFC 3986.
96
+ Shared by form-body and query-string rendering. Keys are literal
97
+ strings; values may contain placeholders (rendered via ``fmt.Sprintf``
98
+ through ``render_url``). ``url.Values`` properly escapes both keys
99
+ and values per RFC 3986. Empty dict → ``'""'``.
99
100
  """
100
- if not body:
101
+ if not d:
101
102
  return '""'
102
103
  parts: list[str] = []
103
- for k, tmpl in body.items():
104
+ for k, tmpl in d.items():
104
105
  val = (
105
106
  render_url(tmpl) if tmpl.has_placeholders else _go_str(tmpl.source)
106
107
  )
@@ -108,6 +109,43 @@ def render_form_body(body: dict[str, PlaceholderTemplate]) -> str:
108
109
  return f"url.Values{{{', '.join(parts)}}}.Encode()"
109
110
 
110
111
 
112
+ def render_form_body(body: dict[str, PlaceholderTemplate]) -> str:
113
+ """Form-urlencoded body via ``url.Values.Encode()``."""
114
+ return render_url_values(body)
115
+
116
+
117
+ def render_full_url(spec: RequestHttp) -> str:
118
+ """URL with query string from ``spec.params`` appended.
119
+
120
+ When params is empty, returns ``render_url(spec.url)`` unchanged.
121
+ Otherwise appends ``+ "?" + <url.Values.Encode()>`` to the base URL.
122
+ """
123
+ base = render_url(spec.url)
124
+ if not spec.params:
125
+ return base
126
+ return f'({base} + "?" + {render_url_values(spec.params)})'
127
+
128
+
129
+ def _render_cookie_pairs(
130
+ spec: RequestHttp,
131
+ ) -> list[tuple[str, str]]:
132
+ """Render each cookie as ``(key_expr, value_expr)`` pair.
133
+
134
+ Value uses ``fmt.Sprintf`` if it contains placeholders (via ``render_url``),
135
+ otherwise a plain string literal. Returns ``[]`` when ``spec.cookies`` is
136
+ empty. Used by both the inline HTML-@request path (``req.AddCookie``) and
137
+ the REST path (``sscReqOpts.Cookies``).
138
+ """
139
+ pairs: list[tuple[str, str]] = []
140
+ for k, tmpl in spec.cookies.items():
141
+ key = _go_str(k)
142
+ value = (
143
+ render_url(tmpl) if tmpl.has_placeholders else _go_str(tmpl.source)
144
+ )
145
+ pairs.append((key, value))
146
+ return pairs
147
+
148
+
111
149
  # ===========================================================================
112
150
  # Placeholder parameter signature
113
151
  # ===========================================================================
@@ -154,9 +192,10 @@ def _build_opts(spec: RequestHttp, indent: str) -> list[str]:
154
192
  """Build sscReqOpts construction lines."""
155
193
  lines: list[str] = []
156
194
  has_headers = bool(spec.headers)
195
+ has_cookies = bool(spec.cookies)
157
196
  has_body = spec.body_kind not in ("empty", None) and spec.body is not None
158
197
 
159
- if not has_headers and not has_body:
198
+ if not has_headers and not has_body and not has_cookies:
160
199
  return [f"{indent}nil"]
161
200
 
162
201
  lines.append(f"{indent}&sscReqOpts{{")
@@ -176,6 +215,9 @@ def _build_opts(spec: RequestHttp, indent: str) -> list[str]:
176
215
  val = _go_str(tmpl.source)
177
216
  parts.append(f"{_go_str(k)}: []string{{{val}}}")
178
217
  lines.append(f"{indent}\tHeaders: sscHeaders{{{', '.join(parts)}}},")
218
+ if has_cookies:
219
+ parts = [f"{k}: []string{{{v}}}" for k, v in _render_cookie_pairs(spec)]
220
+ lines.append(f"{indent}\tCookies: sscHeaders{{{', '.join(parts)}}},")
179
221
  lines.append(f"{indent}}}")
180
222
  return lines
181
223
 
@@ -185,9 +227,14 @@ def emit_method_fetch(
185
227
  ctx: WalkContext,
186
228
  http: GoHttpLibStrategy,
187
229
  ) -> list[str]:
188
- """Emit a Go Fetch method for HTML-parser structs with ``@request``.
230
+ """Emit a Go HTTP-entry function for HTML-parser structs with ``@request``.
189
231
 
190
- Signature: ``func Fetch(ctx context.Context, client *http.Client, <phs>) (*Name, error)``
232
+ Signature: ``func New<Struct><Method>(ctx, client, <phs>, opts ...) (*Name, error)``
233
+
234
+ Free function (not a receiver method) — semantically a sibling of
235
+ ``New<Name>(input string)`` that fetches the input from HTTP instead.
236
+ Struct-name prefix guarantees uniqueness across schemas in the same
237
+ package (default ``New<Name>Fetch``; ``@request name=X`` → ``New<Name>X``).
191
238
 
192
239
  Idiomatic Go: cancellation via ``context.Context``, async via ``go f(ctx)``
193
240
  on caller side. Headers/body inlined (no ``sscReqOpts`` dependency).
@@ -198,15 +245,16 @@ def emit_method_fetch(
198
245
  struct = node.parent
199
246
  assert isinstance(struct, StructBase)
200
247
  name = to_pascal_case(struct.name)
201
- method_name = to_pascal_case(node.name) if node.name else "Fetch"
248
+ method_suffix = to_pascal_case(node.name) if node.name else "Fetch"
249
+ func_name = f"New{name}{method_suffix}"
202
250
  ph_params = placeholder_params(spec)
203
- url_expr = render_url(spec.url)
251
+ url_expr = render_full_url(spec)
204
252
 
205
253
  i1 = ctx.indent
206
254
  i2 = i1 + ctx.indent_char
207
255
 
208
256
  lines: list[str] = [
209
- f"{i1}func {method_name}(ctx context.Context, client {http.client_type}{ph_params}, opts ...sscReqOpt) (*{name}, error) {{",
257
+ f"{i1}func {func_name}(ctx context.Context, client {http.client_type}{ph_params}, opts ...sscReqOpt) (*{name}, error) {{",
210
258
  f"{i2}req, err := http.NewRequestWithContext(ctx, {_go_str(spec.method)}, {url_expr}, nil)",
211
259
  f"{i2}if err != nil {{",
212
260
  f"{i2}\treturn nil, err",
@@ -220,6 +268,13 @@ def emit_method_fetch(
220
268
  )
221
269
  lines.append(f"{i2}req.Header.Set({_go_str(k)}, {val})")
222
270
 
271
+ # Cookies inline (per-cookie AddCookie — values may interpolate
272
+ # placeholders via fmt.Sprintf produced by _render_cookie_pairs).
273
+ for key, val in _render_cookie_pairs(spec):
274
+ lines.append(
275
+ f"{i2}req.AddCookie(&http.Cookie{{Name: {key}, Value: {val}}})"
276
+ )
277
+
223
278
  # Body inline (json/raw or form).
224
279
  if spec.body_kind in ("json", "raw") and spec.body is not None:
225
280
  if isinstance(spec.body, PlaceholderTemplate):
@@ -293,6 +348,7 @@ def emit_method_rest(
293
348
  node: MethodRest,
294
349
  ctx: WalkContext,
295
350
  http: GoHttpLibStrategy,
351
+ rcv: str,
296
352
  ) -> list[str]:
297
353
  """Emit a Go REST method that calls sscRestCall.
298
354
 
@@ -300,6 +356,10 @@ def emit_method_rest(
300
356
  - typed value → ``(*SchemaJson, error)`` or ``([]SchemaJson, error)``
301
357
  - void → ``(struct{}, error)``
302
358
 
359
+ Receiver method on the (empty) marker struct — namespaced by type so
360
+ multiple REST structs in one package don't collide. Caller writes
361
+ ``NewApiX().Fetch(client, opts...)`` (factory + receiver).
362
+
303
363
  Errors propagate as typed structs: ``@error`` variants via matchers,
304
364
  ``*UnknownErr`` for unmatched HTTP >= 400, ``*TransportErr`` for network.
305
365
  Caller uses ``errors.As`` to discriminate.
@@ -307,10 +367,11 @@ def emit_method_rest(
307
367
  spec = node.http_request.with_renamed_placeholders(to_camel_case)
308
368
  struct = node.parent
309
369
  assert isinstance(struct, StructBase)
370
+ name = to_pascal_case(struct.name)
310
371
  method_name = to_pascal_case(node.name) if node.name else "Fetch"
311
372
  ph_params = placeholder_params(spec)
312
373
  matchers_var = f"{to_snake_case(struct.name)}Matchers"
313
- url_expr = render_url(spec.url)
374
+ url_expr = render_full_url(spec)
314
375
 
315
376
  # Resolve return type from AST response_schema (no `any`).
316
377
  if node.response_schema:
@@ -332,9 +393,9 @@ def emit_method_rest(
332
393
 
333
394
  lines: list[str] = []
334
395
 
335
- # Function signature.
396
+ # Function signature (receiver method on marker struct).
336
397
  lines.append(
337
- f"{i1}func {method_name}(client {http.client_type}{ph_params}, opts ...sscReqOpt) ({ret_type}, error) {{"
398
+ f"{i1}func ({rcv} {name}) {method_name}(client {http.client_type}{ph_params}, opts ...sscReqOpt) ({ret_type}, error) {{"
338
399
  )
339
400
 
340
401
  # Build _opts from DSL, apply per-call user opts, then call sscRestCall.
@@ -490,6 +490,7 @@ BASE_REST_RUNTIME: list[str] = [
490
490
  "",
491
491
  "type sscReqOpts struct {",
492
492
  "\tHeaders sscHeaders",
493
+ "\tCookies sscHeaders",
493
494
  "\tBody string",
494
495
  "}",
495
496
  "",
@@ -596,6 +596,12 @@ class GoVisitor(BaseWalker):
596
596
  lines.append(f"type {name} struct {{")
597
597
  lines.append("}")
598
598
  lines.append("")
599
+ # Empty-struct factory: caller writes New<Name>().Method(...) —
600
+ # the struct is a pure namespace marker (no state), receiver
601
+ # methods namespaced by type avoid collisions across schemas in
602
+ # the same package.
603
+ lines.append(f"func New{name}() {name} {{ return {name}{{}} }}")
604
+ lines.append("")
599
605
  for child in node.body:
600
606
  lines.extend(self.walk(child, ctx))
601
607
  return lines
@@ -1884,8 +1890,17 @@ class GoVisitor(BaseWalker):
1884
1890
  spec = node.http_request
1885
1891
  if spec.body_kind == "form" and isinstance(spec.body, dict):
1886
1892
  self._builder.require_import('"net/url"')
1893
+ if spec.params:
1894
+ self._builder.require_import('"net/url"')
1887
1895
  self._builder.require_import('"' + self._http.import_path + '"')
1888
- return rest.emit_method_rest(node, ctx, self._http)
1896
+ struct = node.parent
1897
+ name = (
1898
+ to_pascal_case(struct.name)
1899
+ if isinstance(struct, StructBase)
1900
+ else "X"
1901
+ )
1902
+ rcv = _receiver(name)
1903
+ return rest.emit_method_rest(node, ctx, self._http, rcv)
1889
1904
 
1890
1905
  def visit_method_fetch(
1891
1906
  self, node: MethodFetch, ctx: WalkContext
@@ -1899,6 +1914,8 @@ class GoVisitor(BaseWalker):
1899
1914
  spec = node.http_request
1900
1915
  if spec.body_kind == "form" and isinstance(spec.body, dict):
1901
1916
  self._builder.require_import('"net/url"')
1917
+ if spec.params:
1918
+ self._builder.require_import('"net/url"')
1902
1919
  if node.response_path:
1903
1920
  self._builder.require_import('"github.com/tidwall/gjson"')
1904
1921
  return rest.emit_method_fetch(node, ctx, self._http)
@@ -390,14 +390,20 @@ def emit_method_rest(
390
390
  t = f"{t}[]"
391
391
  bracket = f"[params.{p.name}]" if p.is_optional else f"params.{p.name}"
392
392
  lines.append(f"{i1} * @param {{{t}}} {bracket}")
393
- lines.append(f"{i1} * @param {{Object}} [opts] per-call request options (headers, etc.)")
393
+ lines.append(
394
+ f"{i1} * @param {{Object}} [opts] per-call request options (headers, etc.)"
395
+ )
394
396
  lines.append(f"{i1} * @returns {{Promise<{return_union}>}}")
395
397
  lines.append(f"{i1} */")
396
- lines.append(f"{i1}static async {method_name}(client{ph_param}, opts = {{}}) {{")
398
+ lines.append(
399
+ f"{i1}static async {method_name}(client{ph_param}, opts = {{}}) {{"
400
+ )
397
401
  lines.extend(pre_lines)
398
402
  lines.append(f"{i2}const _kw = {opts_obj};")
399
403
  lines.append(f"{i2}for (const [_k, _v] of Object.entries(opts)) {{")
400
- lines.append(f"{i2} if (typeof _kw[_k] === 'object' && _kw[_k] !== null && typeof _v === 'object' && _v !== null) {{")
404
+ lines.append(
405
+ f"{i2} if (typeof _kw[_k] === 'object' && _kw[_k] !== null && typeof _v === 'object' && _v !== null) {{"
406
+ )
401
407
  lines.append(f"{i2} _kw[_k] = {{..._kw[_k], ..._v}};")
402
408
  lines.append(f"{i2} }} else {{")
403
409
  lines.append(f"{i2} _kw[_k] = _v;")
@@ -443,7 +449,9 @@ def emit_method_fetch(
443
449
  rl.append(f"{i2}return new {struct_name}(_body);")
444
450
  return rl
445
451
 
446
- lines: list[str] = [f"{i1}static async {method_name}(client{ph_param}, opts = {{}}) {{"]
452
+ lines: list[str] = [
453
+ f"{i1}static async {method_name}(client{ph_param}, opts = {{}}) {{"
454
+ ]
447
455
  lines.extend(pre_lines)
448
456
 
449
457
  # Build _kw from DSL-specified request options (method/url stay out).
@@ -471,7 +479,9 @@ def emit_method_fetch(
471
479
  kw_obj = "{" + ", ".join(kw_parts) + "}" if kw_parts else "{}"
472
480
  lines.append(f"{i2}const _kw = {kw_obj};")
473
481
  lines.append(f"{i2}for (const [_k, _v] of Object.entries(opts)) {{")
474
- lines.append(f"{i2} if (typeof _kw[_k] === 'object' && _kw[_k] !== null && typeof _v === 'object' && _v !== null) {{")
482
+ lines.append(
483
+ f"{i2} if (typeof _kw[_k] === 'object' && _kw[_k] !== null && typeof _v === 'object' && _v !== null) {{"
484
+ )
475
485
  lines.append(f"{i2} _kw[_k] = {{..._kw[_k], ..._v}};")
476
486
  lines.append(f"{i2} }} else {{")
477
487
  lines.append(f"{i2} _kw[_k] = _v;")
@@ -495,15 +505,15 @@ def emit_method_fetch(
495
505
  if headers_expr:
496
506
  kw_parts.append(f"headers: {headers_expr}")
497
507
  if spec.cookies:
498
- kw_parts.append(
499
- f"// cookies: {render_obj(spec.cookies)},"
500
- )
508
+ kw_parts.append(f"// cookies: {render_obj(spec.cookies)},")
501
509
  if body_expr:
502
510
  kw_parts.append(f"data: {body_expr}")
503
511
  kw_obj = "{" + ", ".join(kw_parts) + "}" if kw_parts else "{}"
504
512
  lines.append(f"{i2}const _kw = {kw_obj};")
505
513
  lines.append(f"{i2}for (const [_k, _v] of Object.entries(opts)) {{")
506
- lines.append(f"{i2} if (typeof _kw[_k] === 'object' && _kw[_k] !== null && typeof _v === 'object' && _v !== null) {{")
514
+ lines.append(
515
+ f"{i2} if (typeof _kw[_k] === 'object' && _kw[_k] !== null && typeof _v === 'object' && _v !== null) {{"
516
+ )
507
517
  lines.append(f"{i2} _kw[_k] = {{..._kw[_k], ..._v}};")
508
518
  lines.append(f"{i2} }} else {{")
509
519
  lines.append(f"{i2} _kw[_k] = _v;")
File without changes
File without changes
File without changes