1use super::argument::FromArgs;
7
8#[derive(Clone, Copy, Debug)]
9pub enum ParamKind {
10 PositionalOnly,
11 PositionalOrKeyword,
12 KeywordOnly,
13 VarPositional,
14 VarKeyword,
15 Flatten(Option<&'static [Param]>),
18}
19
20#[derive(Clone, Copy, Debug)]
22pub enum DefaultRepr {
23 None,
24 Bool(bool),
25 Int(i128),
26 Float(f64),
27 Str(&'static str),
28 Bytes(&'static [u8]),
29 Raw(&'static str),
31 Unrepresentable,
33}
34
35#[derive(Clone, Copy, Debug)]
36pub struct Param {
37 pub name: &'static str,
38 pub kind: ParamKind,
39 pub default: Option<DefaultRepr>,
40}
41
42impl Param {
43 #[must_use]
44 pub const fn positional_only(name: &'static str) -> Self {
45 Self {
46 name,
47 kind: ParamKind::PositionalOnly,
48 default: None,
49 }
50 }
51
52 #[must_use]
53 pub const fn positional_or_keyword(name: &'static str) -> Self {
54 Self {
55 name,
56 kind: ParamKind::PositionalOrKeyword,
57 default: None,
58 }
59 }
60
61 #[must_use]
62 pub const fn keyword_only(name: &'static str, default: Option<DefaultRepr>) -> Self {
63 Self {
64 name,
65 kind: ParamKind::KeywordOnly,
66 default,
67 }
68 }
69
70 #[must_use]
71 pub const fn var_positional(name: &'static str) -> Self {
72 Self {
73 name,
74 kind: ParamKind::VarPositional,
75 default: None,
76 }
77 }
78
79 #[must_use]
80 pub const fn var_keyword(name: &'static str) -> Self {
81 Self {
82 name,
83 kind: ParamKind::VarKeyword,
84 default: None,
85 }
86 }
87
88 #[must_use]
89 pub const fn flatten(params: Option<&'static [Self]>) -> Self {
90 Self {
91 name: "",
92 kind: ParamKind::Flatten(params),
93 default: None,
94 }
95 }
96}
97
98#[derive(Clone, Copy, Debug)]
104pub struct SigArg {
105 pub name: &'static str,
106 pub params: Option<&'static [Param]>,
107}
108
109impl SigArg {
110 #[must_use]
111 pub const fn from_arg<T: FromArgs>(name: &'static str) -> Self {
112 Self {
113 name,
114 params: T::PARAMS,
115 }
116 }
117
118 #[must_use]
120 pub const fn marker(name: &'static str) -> Self {
121 Self { name, params: None }
122 }
123
124 #[must_use]
127 pub const fn implicit<T: FromArgs>() -> Self {
128 Self {
129 name: "",
130 params: Some(match T::PARAMS {
131 Some(ps) => ps,
132 None => &[],
133 }),
134 }
135 }
136}
137
138const fn name_eq(name: &str, bytes: &[u8]) -> bool {
139 let got = name.as_bytes();
140 if got.len() != bytes.len() {
141 return false;
142 }
143 let mut i = 0;
144 while i < got.len() {
145 if got[i] != bytes[i] {
146 return false;
147 }
148 i += 1;
149 }
150 true
151}
152
153#[must_use]
155const fn is_bare_funcargs(params: &[Param]) -> bool {
156 params.len() == 2
157 && matches!(params[0].kind, ParamKind::VarPositional)
158 && name_eq(params[0].name, b"args")
159 && matches!(params[1].kind, ParamKind::VarKeyword)
160 && name_eq(params[1].name, b"kwargs")
161}
162
163const fn params_representable(params: &[Param]) -> bool {
164 let mut i = 0;
165 while i < params.len() {
166 if let Some(DefaultRepr::Unrepresentable) = params[i].default {
167 return false;
168 }
169 if let ParamKind::Flatten(Some(inner)) = params[i].kind
170 && !params_representable(inner)
171 {
172 return false;
173 }
174 i += 1;
175 }
176 true
177}
178
179#[must_use]
182pub const fn real_signature(params: Option<&[Param]>) -> Option<&[Param]> {
183 match params {
184 Some(ps) if !is_bare_funcargs(ps) && params_representable(ps) => Some(ps),
185 _ => None,
186 }
187}
188
189#[must_use]
191pub const fn choose_class_params<'a>(
192 preferred: Option<&'a [Param]>,
193 alternate: Option<&'a [Param]>,
194) -> Option<&'a [Param]> {
195 match real_signature(preferred) {
196 Some(ps) => Some(ps),
197 None => real_signature(alternate),
198 }
199}
200
201#[must_use]
203pub const fn has_signature(args: &[SigArg]) -> bool {
204 let mut i = 0;
205 while i < args.len() {
206 if args[i].params.is_none() && args[i].name.is_empty() {
207 return false;
208 }
209 i += 1;
210 }
211 true
212}
213
214#[must_use]
215pub const fn signature_prefix_len(name: &str, args: &[SigArg]) -> usize {
216 write_signature_prefix(&mut [], name, args)
217}
218
219#[must_use]
220pub const fn signature_prefix_bytes<const N: usize>(name: &str, args: &[SigArg]) -> [u8; N] {
221 let mut buf = [0u8; N];
222 let written = write_signature_prefix(&mut buf, name, args);
223 assert!(written == N);
224 buf
225}
226
227#[must_use]
228pub const fn internal_doc_len(name: &str, args: &[SigArg], doc: &str) -> usize {
229 write_internal_doc(&mut [], name, args, doc)
230}
231
232#[must_use]
233pub const fn internal_doc_bytes<const N: usize>(name: &str, args: &[SigArg], doc: &str) -> [u8; N] {
234 let mut buf = [0u8; N];
235 let written = write_internal_doc(&mut buf, name, args, doc);
236 assert!(written == N);
237 buf
238}
239
240struct St {
241 n: usize,
242 emitted: bool,
243 po_left: usize,
244 var_pos_seen: bool,
245 star_emitted: bool,
246}
247
248const fn write_signature_prefix(buf: &mut [u8], name: &str, args: &[SigArg]) -> usize {
249 let mut n = put_str(buf, 0, name);
250 n = put_byte(buf, n, b'(');
251 let st = write_args(
252 buf,
253 St {
254 n,
255 emitted: false,
256 po_left: count_po_args(args),
257 var_pos_seen: false,
258 star_emitted: false,
259 },
260 args,
261 );
262 put_str(buf, st.n, ")\n--\n\n")
263}
264
265const fn write_internal_doc(buf: &mut [u8], name: &str, args: &[SigArg], doc: &str) -> usize {
266 let n = write_signature_prefix(buf, name, args);
267 put_str(buf, n, doc)
268}
269
270const fn put(buf: &mut [u8], i: usize, bytes: &[u8]) -> usize {
271 let mut k = 0;
272 while k < bytes.len() {
273 let at = i + k;
274 if at < buf.len() {
275 buf[at] = bytes[k];
276 }
277 k += 1;
278 }
279 i + bytes.len()
280}
281
282const fn put_str(buf: &mut [u8], i: usize, s: &str) -> usize {
283 put(buf, i, s.as_bytes())
284}
285
286const fn put_byte(buf: &mut [u8], i: usize, b: u8) -> usize {
287 put(buf, i, &[b])
288}
289
290const fn count_po_params(params: &[Param]) -> usize {
291 let mut n = 0;
292 let mut i = 0;
293 while i < params.len() {
294 match params[i].kind {
295 ParamKind::PositionalOnly => n += 1,
296 ParamKind::Flatten(Some(inner)) => n += count_po_params(inner),
297 _ => {}
298 }
299 i += 1;
300 }
301 n
302}
303
304const fn count_po_args(args: &[SigArg]) -> usize {
305 let mut n = 0;
306 let mut i = 0;
307 while i < args.len() {
308 match args[i].params {
309 None => n += 1,
310 Some(ps) => n += count_po_params(ps),
311 }
312 i += 1;
313 }
314 n
315}
316
317const fn emit_sep(buf: &mut [u8], st: St) -> St {
318 if st.emitted {
319 St {
320 n: put_str(buf, st.n, ", "),
321 ..st
322 }
323 } else {
324 st
325 }
326}
327
328const fn emit_text(buf: &mut [u8], st: St, text: &str) -> St {
329 let st = emit_sep(buf, st);
330 St {
331 n: put_str(buf, st.n, text),
332 emitted: true,
333 ..st
334 }
335}
336
337const fn emit_named(
338 buf: &mut [u8],
339 st: St,
340 prefix: &str,
341 name: &str,
342 default: Option<DefaultRepr>,
343) -> St {
344 let st = emit_sep(buf, st);
345 let n = put_str(buf, st.n, prefix);
346 let n = put_str(buf, n, name);
347 let n = if let Some(default) = default {
348 let n = put_byte(buf, n, b'=');
349 put_default(buf, n, default)
350 } else {
351 n
352 };
353 St {
354 n,
355 emitted: true,
356 ..st
357 }
358}
359
360const HEX: &[u8; 16] = b"0123456789abcdef";
361
362const fn put_hex_byte(buf: &mut [u8], i: usize, b: u8) -> usize {
363 let n = put_str(buf, i, "\\x");
364 let n = put_byte(buf, n, HEX[(b >> 4) as usize]);
365 put_byte(buf, n, HEX[(b & 0xf) as usize])
366}
367
368const fn put_quoted(buf: &mut [u8], mut i: usize, bytes: &[u8], utf8: bool) -> usize {
369 i = put_byte(buf, i, b'\'');
370 let mut k = 0;
371 while k < bytes.len() {
372 let b = bytes[k];
373 if b == b'\\' {
374 i = put_str(buf, i, "\\\\");
375 } else if b == b'\'' {
376 i = put_str(buf, i, "\\'");
377 } else if b == b'\n' {
378 i = put_str(buf, i, "\\n");
379 } else if b == b'\r' {
380 i = put_str(buf, i, "\\r");
381 } else if b == b'\t' {
382 i = put_str(buf, i, "\\t");
383 } else if b < 0x20 || b == 0x7f || (!utf8 && b >= 0x80) {
384 i = put_hex_byte(buf, i, b);
385 } else {
386 i = put_byte(buf, i, b);
387 }
388 k += 1;
389 }
390 put_byte(buf, i, b'\'')
391}
392
393const fn put_u128(buf: &mut [u8], i: usize, mut v: u128) -> usize {
394 if v == 0 {
395 return put_byte(buf, i, b'0');
396 }
397 let mut digits = [0u8; 40];
398 let mut n = 0;
399 while v > 0 {
400 digits[n] = b'0' + (v % 10) as u8;
401 v /= 10;
402 n += 1;
403 }
404 let mut i = i;
405 while n > 0 {
406 n -= 1;
407 i = put_byte(buf, i, digits[n]);
408 }
409 i
410}
411
412const fn put_i128(buf: &mut [u8], i: usize, v: i128) -> usize {
413 if v < 0 {
414 let i = put_byte(buf, i, b'-');
415 put_u128(buf, i, (v as u128).wrapping_neg())
416 } else {
417 put_u128(buf, i, v as u128)
418 }
419}
420
421const fn put_exp(buf: &mut [u8], i: usize, exp: i32) -> usize {
422 let i = put_byte(buf, i, b'e');
423 let (i, exp) = if exp < 0 {
424 (put_byte(buf, i, b'-'), exp.wrapping_neg())
425 } else {
426 (put_byte(buf, i, b'+'), exp)
427 };
428 let exp = exp as u32;
429 if exp >= 10 {
430 let i = put_byte(buf, i, b'0' + (exp / 10) as u8);
431 put_byte(buf, i, b'0' + (exp % 10) as u8)
432 } else {
433 let i = put_byte(buf, i, b'0');
434 put_byte(buf, i, b'0' + exp as u8)
435 }
436}
437
438const fn put_f64(buf: &mut [u8], i: usize, v: f64) -> usize {
440 if v.is_nan() {
441 return put_str(buf, i, "nan");
442 }
443 if v.is_infinite() {
444 return put_str(buf, i, if v.is_sign_negative() { "-inf" } else { "inf" });
445 }
446 if v == 0.0 {
447 return put_str(buf, i, if v.is_sign_negative() { "-0.0" } else { "0.0" });
448 }
449 let neg = v.is_sign_negative();
450 let mut i = if neg { put_byte(buf, i, b'-') } else { i };
451 let mut x = if neg { -v } else { v };
452 let mut exp: i32 = 0;
453 while x >= 10.0 && exp < 350 {
454 x /= 10.0;
455 exp += 1;
456 }
457 while x < 1.0 && exp > -350 {
458 x *= 10.0;
459 exp -= 1;
460 }
461 let mut digits = [0u8; 17];
463 let mut n = 0;
464 while n < 17 {
465 let d = x as u8;
466 digits[n] = d;
467 x = (x - d as f64) * 10.0;
468 n += 1;
469 }
470 if x >= 5.0 {
471 let mut k = 16;
472 loop {
473 if digits[k] < 9 {
474 digits[k] += 1;
475 break;
476 }
477 digits[k] = 0;
478 if k == 0 {
479 digits[0] = 1;
480 exp += 1;
481 break;
482 }
483 k -= 1;
484 }
485 }
486 while n > 1 && digits[n - 1] == 0 {
487 n -= 1;
488 }
489 let scientific = exp < -4 || exp >= 16;
490 if scientific {
491 i = put_byte(buf, i, b'0' + digits[0]);
492 if n > 1 {
493 i = put_byte(buf, i, b'.');
494 let mut k = 1;
495 while k < n {
496 i = put_byte(buf, i, b'0' + digits[k]);
497 k += 1;
498 }
499 }
500 put_exp(buf, i, exp)
501 } else if exp >= 0 {
502 let exp_us = exp as usize;
503 let mut k = 0;
504 while k <= exp_us && k < n {
505 i = put_byte(buf, i, b'0' + digits[k]);
506 k += 1;
507 }
508 while k <= exp_us {
509 i = put_byte(buf, i, b'0');
510 k += 1;
511 }
512 i = put_byte(buf, i, b'.');
513 if n as i32 > exp + 1 {
514 let mut k = exp as usize + 1;
515 while k < n {
516 i = put_byte(buf, i, b'0' + digits[k]);
517 k += 1;
518 }
519 i
520 } else {
521 put_byte(buf, i, b'0')
522 }
523 } else {
524 i = put_str(buf, i, "0.");
525 let mut z = 0;
526 while z < -exp - 1 {
527 i = put_byte(buf, i, b'0');
528 z += 1;
529 }
530 let mut k = 0;
531 while k < n {
532 i = put_byte(buf, i, b'0' + digits[k]);
533 k += 1;
534 }
535 i
536 }
537}
538
539const fn put_default(buf: &mut [u8], i: usize, default: DefaultRepr) -> usize {
540 match default {
541 DefaultRepr::None => put_str(buf, i, "None"),
542 DefaultRepr::Bool(true) => put_str(buf, i, "True"),
543 DefaultRepr::Bool(false) => put_str(buf, i, "False"),
544 DefaultRepr::Int(v) => put_i128(buf, i, v),
545 DefaultRepr::Float(v) => put_f64(buf, i, v),
546 DefaultRepr::Str(s) => put_quoted(buf, i, s.as_bytes(), true),
547 DefaultRepr::Bytes(b) => {
548 let i = put_byte(buf, i, b'b');
549 put_quoted(buf, i, b, false)
550 }
551 DefaultRepr::Raw(s) => put_str(buf, i, s),
552 DefaultRepr::Unrepresentable => put_str(buf, i, "<unrepresentable>"),
553 }
554}
555
556const fn param_name<'a>(name: &'a str, fallback: &'a str) -> &'a str {
557 if name.is_empty() { fallback } else { name }
558}
559
560const fn write_params(buf: &mut [u8], mut st: St, params: &[Param], fallback: &str) -> St {
561 let mut i = 0;
562 while i < params.len() {
563 st = write_one(buf, st, params[i], fallback);
564 i += 1;
565 }
566 st
567}
568
569const fn write_one(buf: &mut [u8], mut st: St, param: Param, fallback: &str) -> St {
570 let name = param_name(param.name, fallback);
571 match param.kind {
572 ParamKind::Flatten(None) => st,
573 ParamKind::Flatten(Some(inner)) => write_params(buf, st, inner, ""),
574 ParamKind::PositionalOnly => {
575 st = emit_named(buf, st, "", name, param.default);
576 st.po_left -= 1;
577 if st.po_left == 0 {
578 st = emit_text(buf, st, "/");
579 }
580 st
581 }
582 ParamKind::PositionalOrKeyword => emit_named(buf, st, "", name, param.default),
583 ParamKind::KeywordOnly => {
584 if !st.var_pos_seen && !st.star_emitted {
585 st = emit_text(buf, st, "*");
586 st.star_emitted = true;
587 }
588 emit_named(buf, st, "", name, param.default)
589 }
590 ParamKind::VarPositional => {
591 st.var_pos_seen = true;
592 emit_named(buf, st, "*", name, param.default)
593 }
594 ParamKind::VarKeyword => emit_named(buf, st, "**", name, param.default),
595 }
596}
597
598const fn write_args(buf: &mut [u8], mut st: St, args: &[SigArg]) -> St {
599 let mut i = 0;
600 while i < args.len() {
601 match args[i].params {
602 None => {
603 st = write_one(
604 buf,
605 st,
606 Param {
607 name: args[i].name,
608 kind: ParamKind::PositionalOnly,
609 default: None,
610 },
611 "",
612 );
613 }
614 Some(ps) => st = write_params(buf, st, ps, args[i].name),
615 }
616 i += 1;
617 }
618 st
619}
620
621#[cfg(test)]
622mod tests {
623 use super::{DefaultRepr, Param, ParamKind, St, write_one};
624
625 fn rendered(default: DefaultRepr) -> String {
626 let mut buf = [0u8; 64];
627 let st = write_one(
628 &mut buf,
629 St {
630 n: 0,
631 emitted: false,
632 po_left: 0,
633 var_pos_seen: false,
634 star_emitted: true,
635 },
636 Param {
637 name: "x",
638 kind: ParamKind::KeywordOnly,
639 default: Some(default),
640 },
641 "",
642 );
643 let text = core::str::from_utf8(&buf[..st.n]).unwrap();
644 text.split_once('=').unwrap().1.to_owned()
645 }
646
647 #[test]
648 fn default_repr_text() {
649 assert_eq!(rendered(DefaultRepr::None), "None");
650 assert_eq!(rendered(DefaultRepr::Bool(true)), "True");
651 assert_eq!(rendered(DefaultRepr::Bool(false)), "False");
652 assert_eq!(rendered(DefaultRepr::Int(-15)), "-15");
653 assert_eq!(rendered(DefaultRepr::Int(0)), "0");
654 assert_eq!(rendered(DefaultRepr::Float(0.0)), "0.0");
655 assert_eq!(rendered(DefaultRepr::Float(-1.0)), "-1.0");
656 assert_eq!(rendered(DefaultRepr::Float(5.0)), "5.0");
657 assert_eq!(rendered(DefaultRepr::Float(1e-9)), "1e-09");
658 assert_eq!(rendered(DefaultRepr::Float(f64::INFINITY)), "inf");
659 assert_eq!(rendered(DefaultRepr::Float(f64::NEG_INFINITY)), "-inf");
660 assert_eq!(rendered(DefaultRepr::Str("a'b\n")), r"'a\'b\n'");
661 assert_eq!(rendered(DefaultRepr::Bytes(b"a'b")), r"b'a\'b'");
662 assert_eq!(rendered(DefaultRepr::Raw("sys.maxsize")), "sys.maxsize");
663 assert_eq!(rendered(DefaultRepr::Unrepresentable), "<unrepresentable>");
664 }
665}