Punycode (RFC3492) in OCaml

fix(lint): use Fmt instead of Printf/Format (E205)

Replace Printf.sprintf/printf and Format.asprintf/fprintf with
Fmt.str/pr/pf across publicsuffix, punycode, rate-limit, requests,
and openamp packages. Add fmt dependency to gen_corpus executables.

+29 -38
+1 -1
fuzz/dune
··· 6 (executable 7 (name gen_corpus) 8 (modules gen_corpus) 9 - (libraries unix)) 10 11 (rule 12 (alias runtest)
··· 6 (executable 7 (name gen_corpus) 8 (modules gen_corpus) 9 + (libraries unix fmt)) 10 11 (rule 12 (alias runtest)
+1 -1
fuzz/gen_corpus.ml
··· 14 write "seed_003" (String.make 16 '\x00'); 15 write "seed_004" (String.make 16 '\xff'); 16 write "seed_005" (String.init 256 Char.chr); 17 - Printf.printf "gen_corpus: wrote 6 seed files\n"
··· 14 write "seed_003" (String.make 16 '\x00'); 15 write "seed_004" (String.make 16 '\xff'); 16 write "seed_005" (String.init 256 Char.chr); 17 + Fmt.pr "gen_corpus: wrote 6 seed files\n"
+18 -23
test/test_punycode.ml
··· 30 let result = Punycode.encode input in 31 check string "encode" expected result 32 with Punycode.Error e -> 33 - fail (Format.asprintf "encode failed: %a" Punycode.pp_error_reason e) 34 35 let check_decode_ok expected input = 36 try ··· 39 check int "length" (Array.length expected_arr) (Array.length result); 40 Array.iteri 41 (fun i u -> 42 - check int 43 - (Printf.sprintf "char %d" i) 44 (Uchar.to_int expected_arr.(i)) 45 (Uchar.to_int u)) 46 result 47 with Punycode.Error e -> 48 - fail (Format.asprintf "decode failed: %a" Punycode.pp_error_reason e) 49 50 let check_utf8_roundtrip s = 51 try ··· 53 let decoded = Punycode.decode_utf8 encoded in 54 check string "roundtrip" s decoded 55 with Punycode.Error e -> 56 - fail (Format.asprintf "roundtrip failed: %a" Punycode.pp_error_reason e) 57 58 (* RFC 3492 Section 7.1 Test Vectors *) 59 ··· 609 let result = Punycode.encode_label "example" in 610 check string "ascii passthrough" "example" result 611 with Punycode.Error e -> 612 - fail (Format.asprintf "encode_label failed: %a" Punycode.pp_error_reason e) 613 614 let test_label_encode_german () = 615 try 616 let result = Punycode.encode_label "münchen" in 617 check string "german label" "xn--mnchen-3ya" result 618 with Punycode.Error e -> 619 - fail (Format.asprintf "encode_label failed: %a" Punycode.pp_error_reason e) 620 621 let test_label_decode_german () = 622 try 623 let result = Punycode.decode_label "xn--mnchen-3ya" in 624 check string "german decode" "münchen" result 625 with Punycode.Error e -> 626 - fail (Format.asprintf "decode_label failed: %a" Punycode.pp_error_reason e) 627 628 (* IDNA tests *) 629 let test_idna_to_ascii_simple () = ··· 631 let result = Punycode_idna.to_ascii "münchen.example.com" in 632 check string "idna to_ascii" "xn--mnchen-3ya.example.com" result 633 with Punycode_idna.Error e -> 634 - fail (Format.asprintf "to_ascii failed: %a" Punycode_idna.pp_error_reason e) 635 636 let test_idna_to_unicode_simple () = 637 try 638 let result = Punycode_idna.to_unicode "xn--mnchen-3ya.example.com" in 639 check string "idna to_unicode" "münchen.example.com" result 640 with Punycode_idna.Error e -> 641 - fail 642 - (Format.asprintf "to_unicode failed: %a" Punycode_idna.pp_error_reason e) 643 644 let test_idna_roundtrip () = 645 let original = "münchen.example.com" in ··· 648 let unicode = Punycode_idna.to_unicode ascii in 649 check string "idna roundtrip" original unicode 650 with Punycode_idna.Error e -> 651 - fail 652 - (Format.asprintf "roundtrip failed: %a" Punycode_idna.pp_error_reason e) 653 654 let test_idna_all_ascii () = 655 try 656 let result = Punycode_idna.to_ascii "www.example.com" in 657 check string "all ascii passthrough" "www.example.com" result 658 with Punycode_idna.Error e -> 659 - fail (Format.asprintf "to_ascii failed: %a" Punycode_idna.pp_error_reason e) 660 661 let test_idna_mixed_labels () = 662 try ··· 667 (String.length result > 12 668 && String.sub result (String.length result - 12) 12 = ".example.com") 669 with Punycode_idna.Error e -> 670 - fail (Format.asprintf "to_ascii failed: %a" Punycode_idna.pp_error_reason e) 671 672 (* Case annotation tests *) 673 let test_case_annotation_decode () = ··· 686 (* a should be lowercase *) 687 check bool "a lowercase" true (case_flags.(1) = Punycode.Lowercase) 688 with Punycode.Error e -> 689 - fail 690 - (Format.asprintf "decode_with_case failed: %a" Punycode.pp_error_reason e) 691 692 let test_case_annotation_encode () = 693 let codepoints = codepoints_of_hex_list [ 0x0061; 0x0062; 0x0063 ] in ··· 700 (* Should encode as "AbC-" (basic code points with case annotation) *) 701 check string "case encoded" "AbC-" result 702 with Punycode.Error e -> 703 - fail 704 - (Format.asprintf "encode_with_case failed: %a" Punycode.pp_error_reason e) 705 706 (* Edge case tests *) 707 let test_empty_input () = ··· 722 let result = Punycode.encode input in 723 check string "pure ascii" "hello-" result 724 with Punycode.Error e -> 725 - fail (Format.asprintf "encode failed: %a" Punycode.pp_error_reason e) 726 727 let test_invalid_digit () = 728 try ··· 731 with 732 | Punycode.Error (Punycode.Invalid_digit _) -> () 733 | Punycode.Error e -> 734 - fail (Format.asprintf "wrong error type: %a" Punycode.pp_error_reason e) 735 736 let test_label_too_long () = 737 let long_label = String.make 100 'a' in ··· 741 with 742 | Punycode.Error (Punycode.Label_too_long _) -> () 743 | Punycode.Error e -> 744 - fail (Format.asprintf "wrong error type: %a" Punycode.pp_error_reason e) 745 746 let test_empty_label () = 747 try ··· 750 with 751 | Punycode.Error Punycode.Empty_label -> () 752 | Punycode.Error e -> 753 - fail (Format.asprintf "wrong error type: %a" Punycode.pp_error_reason e) 754 755 (* Validation tests *) 756 let test_is_basic () =
··· 30 let result = Punycode.encode input in 31 check string "encode" expected result 32 with Punycode.Error e -> 33 + fail (Fmt.str "encode failed: %a" Punycode.pp_error_reason e) 34 35 let check_decode_ok expected input = 36 try ··· 39 check int "length" (Array.length expected_arr) (Array.length result); 40 Array.iteri 41 (fun i u -> 42 + check int (Fmt.str "char %d" i) 43 (Uchar.to_int expected_arr.(i)) 44 (Uchar.to_int u)) 45 result 46 with Punycode.Error e -> 47 + fail (Fmt.str "decode failed: %a" Punycode.pp_error_reason e) 48 49 let check_utf8_roundtrip s = 50 try ··· 52 let decoded = Punycode.decode_utf8 encoded in 53 check string "roundtrip" s decoded 54 with Punycode.Error e -> 55 + fail (Fmt.str "roundtrip failed: %a" Punycode.pp_error_reason e) 56 57 (* RFC 3492 Section 7.1 Test Vectors *) 58 ··· 608 let result = Punycode.encode_label "example" in 609 check string "ascii passthrough" "example" result 610 with Punycode.Error e -> 611 + fail (Fmt.str "encode_label failed: %a" Punycode.pp_error_reason e) 612 613 let test_label_encode_german () = 614 try 615 let result = Punycode.encode_label "münchen" in 616 check string "german label" "xn--mnchen-3ya" result 617 with Punycode.Error e -> 618 + fail (Fmt.str "encode_label failed: %a" Punycode.pp_error_reason e) 619 620 let test_label_decode_german () = 621 try 622 let result = Punycode.decode_label "xn--mnchen-3ya" in 623 check string "german decode" "münchen" result 624 with Punycode.Error e -> 625 + fail (Fmt.str "decode_label failed: %a" Punycode.pp_error_reason e) 626 627 (* IDNA tests *) 628 let test_idna_to_ascii_simple () = ··· 630 let result = Punycode_idna.to_ascii "münchen.example.com" in 631 check string "idna to_ascii" "xn--mnchen-3ya.example.com" result 632 with Punycode_idna.Error e -> 633 + fail (Fmt.str "to_ascii failed: %a" Punycode_idna.pp_error_reason e) 634 635 let test_idna_to_unicode_simple () = 636 try 637 let result = Punycode_idna.to_unicode "xn--mnchen-3ya.example.com" in 638 check string "idna to_unicode" "münchen.example.com" result 639 with Punycode_idna.Error e -> 640 + fail (Fmt.str "to_unicode failed: %a" Punycode_idna.pp_error_reason e) 641 642 let test_idna_roundtrip () = 643 let original = "münchen.example.com" in ··· 646 let unicode = Punycode_idna.to_unicode ascii in 647 check string "idna roundtrip" original unicode 648 with Punycode_idna.Error e -> 649 + fail (Fmt.str "roundtrip failed: %a" Punycode_idna.pp_error_reason e) 650 651 let test_idna_all_ascii () = 652 try 653 let result = Punycode_idna.to_ascii "www.example.com" in 654 check string "all ascii passthrough" "www.example.com" result 655 with Punycode_idna.Error e -> 656 + fail (Fmt.str "to_ascii failed: %a" Punycode_idna.pp_error_reason e) 657 658 let test_idna_mixed_labels () = 659 try ··· 664 (String.length result > 12 665 && String.sub result (String.length result - 12) 12 = ".example.com") 666 with Punycode_idna.Error e -> 667 + fail (Fmt.str "to_ascii failed: %a" Punycode_idna.pp_error_reason e) 668 669 (* Case annotation tests *) 670 let test_case_annotation_decode () = ··· 683 (* a should be lowercase *) 684 check bool "a lowercase" true (case_flags.(1) = Punycode.Lowercase) 685 with Punycode.Error e -> 686 + fail (Fmt.str "decode_with_case failed: %a" Punycode.pp_error_reason e) 687 688 let test_case_annotation_encode () = 689 let codepoints = codepoints_of_hex_list [ 0x0061; 0x0062; 0x0063 ] in ··· 696 (* Should encode as "AbC-" (basic code points with case annotation) *) 697 check string "case encoded" "AbC-" result 698 with Punycode.Error e -> 699 + fail (Fmt.str "encode_with_case failed: %a" Punycode.pp_error_reason e) 700 701 (* Edge case tests *) 702 let test_empty_input () = ··· 717 let result = Punycode.encode input in 718 check string "pure ascii" "hello-" result 719 with Punycode.Error e -> 720 + fail (Fmt.str "encode failed: %a" Punycode.pp_error_reason e) 721 722 let test_invalid_digit () = 723 try ··· 726 with 727 | Punycode.Error (Punycode.Invalid_digit _) -> () 728 | Punycode.Error e -> 729 + fail (Fmt.str "wrong error type: %a" Punycode.pp_error_reason e) 730 731 let test_label_too_long () = 732 let long_label = String.make 100 'a' in ··· 736 with 737 | Punycode.Error (Punycode.Label_too_long _) -> () 738 | Punycode.Error e -> 739 + fail (Fmt.str "wrong error type: %a" Punycode.pp_error_reason e) 740 741 let test_empty_label () = 742 try ··· 745 with 746 | Punycode.Error Punycode.Empty_label -> () 747 | Punycode.Error e -> 748 + fail (Fmt.str "wrong error type: %a" Punycode.pp_error_reason e) 749 750 (* Validation tests *) 751 let test_is_basic () =
+9 -13
test/test_punycode_idna.ml
··· 14 let result = Punycode_idna.to_ascii input in 15 check string msg expected result 16 with Punycode_idna.Error e -> 17 - fail 18 - (Format.asprintf "%s: to_ascii failed: %a" msg 19 - Punycode_idna.pp_error_reason e) 20 21 let check_to_unicode ~msg expected input = 22 try ··· 24 check string msg expected result 25 with Punycode_idna.Error e -> 26 fail 27 - (Format.asprintf "%s: to_unicode failed: %a" msg 28 - Punycode_idna.pp_error_reason e) 29 30 let check_label_to_ascii ~msg expected input = 31 try ··· 33 check string msg expected result 34 with Punycode_idna.Error e -> 35 fail 36 - (Format.asprintf "%s: label_to_ascii failed: %a" msg 37 - Punycode_idna.pp_error_reason e) 38 39 let check_label_to_unicode ~msg expected input = 40 try ··· 42 check string msg expected result 43 with Punycode_idna.Error e -> 44 fail 45 - (Format.asprintf "%s: label_to_unicode failed: %a" msg 46 Punycode_idna.pp_error_reason e) 47 48 let check_roundtrip ~msg input = ··· 52 check string msg input unicode 53 with Punycode_idna.Error e -> 54 fail 55 - (Format.asprintf "%s: roundtrip failed: %a" msg 56 - Punycode_idna.pp_error_reason e) 57 58 let check_raises_error ~msg f = 59 try 60 ignore (f ()); 61 - fail (Printf.sprintf "%s: expected Error but succeeded" msg) 62 with Punycode_idna.Error _ -> () 63 64 (* {1 to_ascii Test Vectors} *) ··· 90 labels 91 with Punycode_idna.Error e -> 92 fail 93 - (Format.asprintf "multiple IDN labels: to_ascii failed: %a" 94 Punycode_idna.pp_error_reason e) 95 96 let test_to_ascii_chinese () = ··· 108 check string "TLD preserved" "ru" (List.nth labels 1) 109 with Punycode_idna.Error e -> 110 fail 111 - (Format.asprintf "Russian domain: to_ascii failed: %a" 112 Punycode_idna.pp_error_reason e) 113 114 (* {1 to_unicode Test Vectors} *)
··· 14 let result = Punycode_idna.to_ascii input in 15 check string msg expected result 16 with Punycode_idna.Error e -> 17 + fail (Fmt.str "%s: to_ascii failed: %a" msg Punycode_idna.pp_error_reason e) 18 19 let check_to_unicode ~msg expected input = 20 try ··· 22 check string msg expected result 23 with Punycode_idna.Error e -> 24 fail 25 + (Fmt.str "%s: to_unicode failed: %a" msg Punycode_idna.pp_error_reason e) 26 27 let check_label_to_ascii ~msg expected input = 28 try ··· 30 check string msg expected result 31 with Punycode_idna.Error e -> 32 fail 33 + (Fmt.str "%s: label_to_ascii failed: %a" msg Punycode_idna.pp_error_reason 34 + e) 35 36 let check_label_to_unicode ~msg expected input = 37 try ··· 39 check string msg expected result 40 with Punycode_idna.Error e -> 41 fail 42 + (Fmt.str "%s: label_to_unicode failed: %a" msg 43 Punycode_idna.pp_error_reason e) 44 45 let check_roundtrip ~msg input = ··· 49 check string msg input unicode 50 with Punycode_idna.Error e -> 51 fail 52 + (Fmt.str "%s: roundtrip failed: %a" msg Punycode_idna.pp_error_reason e) 53 54 let check_raises_error ~msg f = 55 try 56 ignore (f ()); 57 + fail (Fmt.str "%s: expected Error but succeeded" msg) 58 with Punycode_idna.Error _ -> () 59 60 (* {1 to_ascii Test Vectors} *) ··· 86 labels 87 with Punycode_idna.Error e -> 88 fail 89 + (Fmt.str "multiple IDN labels: to_ascii failed: %a" 90 Punycode_idna.pp_error_reason e) 91 92 let test_to_ascii_chinese () = ··· 104 check string "TLD preserved" "ru" (List.nth labels 1) 105 with Punycode_idna.Error e -> 106 fail 107 + (Fmt.str "Russian domain: to_ascii failed: %a" 108 Punycode_idna.pp_error_reason e) 109 110 (* {1 to_unicode Test Vectors} *)