diff --git a/src/lib_ccx/networking.c b/src/lib_ccx/networking.c index 4cafdb7fd..2ecd25e0e 100644 --- a/src/lib_ccx/networking.c +++ b/src/lib_ccx/networking.c @@ -45,6 +45,24 @@ const char *srv_pwd; unsigned char *srv_header; size_t srv_header_len; +#ifndef DISABLE_RUST +extern void ccxr_connect_to_srv(const char *addr, const char *port, const char *cc_desc, const char *pwd); +extern void ccxr_net_send_header(const unsigned char *data, size_t len); +extern int ccxr_net_send_cc(const unsigned char *data, int length, void *private_data, struct cc_subtitle *sub); +extern void ccxr_net_check_conn(); +extern void ccxr_net_send_epg( + const char *start, + const char *stop, + const char *title, + const char *desc, + const char *lang, + const char *category); +extern int ccxr_net_tcp_read(int socket, void *buffer, size_t length); +extern int ccxr_net_udp_read(int socket, void *buffer, size_t length, const char *src_str, const char *addr_str); +extern int ccxr_start_tcp_srv(const char *port, const char *pwd); +extern int ccxr_start_udp_srv(const char *src, const char *addr, unsigned port); +#endif + /* * Established connection to specified address. * Returns socked id @@ -84,6 +102,9 @@ int set_nonblocking(int fd); void connect_to_srv(const char *addr, const char *port, const char *cc_desc, const char *pwd) { +#ifndef DISABLE_RUST + return ccxr_connect_to_srv(addr, port, cc_desc, pwd); +#endif if (NULL == addr) { mprint("Server address is not set\n"); @@ -115,6 +136,9 @@ void connect_to_srv(const char *addr, const char *port, const char *cc_desc, con void net_send_header(const unsigned char *data, size_t len) { +#ifndef DISABLE_RUST + return ccxr_net_send_header(data, len); +#endif assert(srv_sd > 0); #if DEBUG_OUT @@ -141,6 +165,9 @@ void net_send_header(const unsigned char *data, size_t len) int net_send_cc(const unsigned char *data, int len, void *private_data, struct cc_subtitle *sub) { +#ifndef DISABLE_RUST + return ccxr_net_send_cc(data, len, private_data, sub); +#endif assert(srv_sd > 0); #if DEBUG_OUT @@ -160,6 +187,9 @@ int net_send_cc(const unsigned char *data, int len, void *private_data, struct c void net_check_conn() { +#ifndef DISABLE_RUST + return ccxr_net_check_conn(); +#endif time_t now; static time_t last_ping = 0; char c = 0; @@ -221,6 +251,9 @@ void net_send_epg( const char *lang, const char *category) { +#ifndef DISABLE_RUST + return ccxr_net_send_epg(start, stop, title, desc, lang, category); +#endif size_t st; size_t sp; size_t t; @@ -301,6 +334,9 @@ void net_send_epg( int net_tcp_read(int socket, void *buffer, size_t length) { +#ifndef DISABLE_RUST + return ccxr_net_tcp_read(socket, buffer, length); +#endif assert(buffer != NULL); assert(length > 0); @@ -333,6 +369,9 @@ int net_tcp_read(int socket, void *buffer, size_t length) int net_udp_read(int socket, void *buffer, size_t length, const char *src_str, const char *addr_str) { +#ifndef DISABLE_RUST + return ccxr_net_udp_read(socket, buffer, length, src_str, addr_str); +#endif assert(buffer != NULL); assert(length > 0); @@ -519,6 +558,9 @@ int tcp_connect(const char *host, const char *port) int start_tcp_srv(const char *port, const char *pwd) { +#ifndef DISABLE_RUST + return ccxr_start_tcp_srv(port, pwd); +#endif if (NULL == port) port = DFT_PORT; @@ -974,6 +1016,10 @@ ssize_t read_byte(int fd, char *ch) int start_upd_srv(const char *src_str, const char *addr_str, unsigned port) { +#ifndef DISABLE_RUST + return ccxr_start_udp_srv(src_str, addr_str, port); +#endif + init_sockets(); in_addr_t src; diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index d9b3d3ec0..3b49110d9 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" dependencies = [ "anstyle", "anstyle-parse", @@ -28,37 +28,37 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" dependencies = [ "anstyle", - "once_cell", - "windows-sys", + "once_cell_polyfill", + "windows-sys 0.59.0", ] [[package]] @@ -83,9 +83,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bindgen" @@ -115,7 +115,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cexpr", "clang-sys", "itertools", @@ -128,7 +128,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.99", + "syn 2.0.104", "which", ] @@ -140,15 +140,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "camino" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab" [[package]] name = "ccx_rust" @@ -184,9 +184,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "clang-sys" @@ -201,9 +201,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.31" +version = "4.5.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" +checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" dependencies = [ "clap_builder", "clap_derive", @@ -211,9 +211,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.31" +version = "4.5.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" +checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" dependencies = [ "anstream", "anstyle", @@ -223,27 +223,27 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.28" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.104", ] [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "convert_case" @@ -253,33 +253,33 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", ] [[package]] name = "derive_more" -version = "0.99.19" +version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version", - "syn 2.0.99", + "syn 2.0.104", ] [[package]] @@ -290,7 +290,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.104", ] [[package]] @@ -329,12 +329,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.60.2", ] [[package]] @@ -363,9 +363,9 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" [[package]] name = "heck" @@ -394,32 +394,33 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] name = "humantime" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -428,31 +429,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -460,67 +441,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.99", -] - [[package]] name = "idna" version = "1.0.3" @@ -534,9 +502,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -544,9 +512,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown", @@ -600,10 +568,11 @@ dependencies = [ name = "lib_ccxr" version = "0.1.0" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "crc32fast", "derive_more", "num_enum", + "socket2", "strum 0.26.3", "strum_macros 0.26.4", "thiserror", @@ -613,18 +582,18 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.170" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets", + "windows-targets 0.53.3", ] [[package]] @@ -635,21 +604,21 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "log" -version = "0.4.26" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "minimal-lexical" @@ -693,30 +662,37 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.6.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" dependencies = [ "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.6.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.104", ] [[package]] name = "once_cell" -version = "1.20.3" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "palette" @@ -790,7 +766,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.104", ] [[package]] @@ -808,6 +784,15 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -816,38 +801,37 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "prettyplease" -version = "0.2.30" +version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1ccf34da56fc294e7d4ccf69a85992b7dfb826b7cf57bac6a70bba3494cc08a" +checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" dependencies = [ "proc-macro2", - "syn 2.0.99", + "syn 2.0.104", ] [[package]] name = "proc-macro-crate" -version = "1.3.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ - "once_cell", "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -929,18 +913,18 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "rusty_ffmpeg" @@ -964,22 +948,22 @@ checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.104", ] [[package]] @@ -996,9 +980,19 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] [[package]] name = "stable_deref_trait" @@ -1034,7 +1028,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.99", + "syn 2.0.104", ] [[package]] @@ -1047,7 +1041,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.99", + "syn 2.0.104", ] [[package]] @@ -1063,9 +1057,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.99" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -1074,13 +1068,13 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.104", ] [[package]] @@ -1121,14 +1115,14 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.104", ] [[package]] name = "time" -version = "0.3.39" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", @@ -1141,15 +1135,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.20" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -1157,9 +1151,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -1176,15 +1170,15 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" [[package]] name = "toml_edit" -version = "0.19.15" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "toml_datetime", @@ -1208,12 +1202,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -1266,7 +1254,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -1275,13 +1263,37 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", ] [[package]] @@ -1290,14 +1302,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -1306,74 +1335,116 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" -version = "0.5.40" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", @@ -1383,13 +1454,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.104", "synstructure", ] @@ -1410,15 +1481,26 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.104", "synstructure", ] +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "bdbb9122ea75b11bf96e7492afb723e8a7fbe12c67417aa95e7e3d18144d37cd" dependencies = [ "yoke", "zerofrom", @@ -1427,11 +1509,11 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.104", ] diff --git a/src/rust/lib_ccxr/Cargo.lock b/src/rust/lib_ccxr/Cargo.lock index 8ec94ecf1..557d591e7 100644 --- a/src/rust/lib_ccxr/Cargo.lock +++ b/src/rust/lib_ccxr/Cargo.lock @@ -4,15 +4,15 @@ version = 4 [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "convert_case" @@ -22,27 +22,27 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", ] [[package]] name = "derive_more" -version = "0.99.19" +version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "convert_case", "proc-macro2", @@ -79,9 +79,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" [[package]] name = "heck" @@ -91,21 +91,22 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -114,31 +115,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -146,67 +127,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "idna" version = "1.0.3" @@ -220,9 +188,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -230,9 +198,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown", @@ -252,6 +220,7 @@ dependencies = [ "crc32fast", "derive_more", "num_enum", + "socket2", "strum", "strum_macros", "thiserror", @@ -259,17 +228,23 @@ dependencies = [ "url", ] +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "num-conv" @@ -279,18 +254,19 @@ checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num_enum" -version = "0.6.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" dependencies = [ "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.6.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -298,18 +274,21 @@ dependencies = [ "syn", ] -[[package]] -name = "once_cell" -version = "1.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" - [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -318,28 +297,27 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "proc-macro-crate" -version = "1.3.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ - "once_cell", "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -355,9 +333,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "semver" @@ -367,18 +345,18 @@ checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -387,9 +365,19 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys", +] [[package]] name = "stable_deref_trait" @@ -418,9 +406,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.99" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -429,9 +417,9 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", @@ -460,9 +448,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.39" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", @@ -475,15 +463,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.20" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -491,9 +479,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -501,15 +489,15 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" [[package]] name = "toml_edit" -version = "0.19.15" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "toml_datetime", @@ -533,12 +521,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -546,31 +528,98 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] -name = "winnow" -version = "0.5.40" +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "memchr", + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] -name = "write16" -version = "1.0.0" +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +dependencies = [ + "memchr", +] [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", @@ -580,9 +629,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", @@ -611,11 +660,22 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "bdbb9122ea75b11bf96e7492afb723e8a7fbe12c67417aa95e7e3d18144d37cd" dependencies = [ "yoke", "zerofrom", @@ -624,9 +684,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", diff --git a/src/rust/lib_ccxr/Cargo.toml b/src/rust/lib_ccxr/Cargo.toml index 6162aef3a..44161e973 100644 --- a/src/rust/lib_ccxr/Cargo.toml +++ b/src/rust/lib_ccxr/Cargo.toml @@ -11,10 +11,11 @@ thiserror = "1.0.69" time = { version = "0.3.39", features = ["macros", "formatting"] } derive_more = "0.99.19" url = "2.5.4" +num_enum = "0.7.3" +socket2 = "0.5.7" strum = "0.26.3" strum_macros = "0.26.4" crc32fast = "1.4.2" -num_enum = "0.6.1" [features] default = [ diff --git a/src/rust/lib_ccxr/src/encoder/txt_helpers.rs b/src/rust/lib_ccxr/src/encoder/txt_helpers.rs index a1986b9ee..8b2af4ff8 100644 --- a/src/rust/lib_ccxr/src/encoder/txt_helpers.rs +++ b/src/rust/lib_ccxr/src/encoder/txt_helpers.rs @@ -282,10 +282,10 @@ pub fn get_str_basic( info!("WARNING: Encoding is not yet supported\n"); out_buffer.clear(); out_buffer.push(0); - return 0; + 0 } else { out_buffer.push(0); - return len; + len } } diff --git a/src/rust/lib_ccxr/src/lib.rs b/src/rust/lib_ccxr/src/lib.rs index 4b02a4dfa..9917eba1b 100644 --- a/src/rust/lib_ccxr/src/lib.rs +++ b/src/rust/lib_ccxr/src/lib.rs @@ -2,6 +2,7 @@ pub mod activity; pub mod common; pub mod encoder; pub mod hardsubx; +pub mod net; pub mod subtitle; pub mod teletext; pub mod time; diff --git a/src/rust/lib_ccxr/src/net/block.rs b/src/rust/lib_ccxr/src/net/block.rs new file mode 100644 index 000000000..8f7b9f6a5 --- /dev/null +++ b/src/rust/lib_ccxr/src/net/block.rs @@ -0,0 +1,358 @@ +use super::NetError; +use crate::time::units::Timestamp; + +use num_enum::{IntoPrimitive, TryFromPrimitive}; + +use std::borrow::Cow; +use std::fmt::{self, Display, Formatter}; +use std::io::{self, Write}; + +/// Default port to be used when port number is not specified for TCP. +pub const DEFAULT_TCP_PORT: u16 = 2048; + +/// The amount of time to wait for a response before reseting the connection. +pub const NO_RESPONSE_INTERVAL: Timestamp = Timestamp::from_millis(20_000); + +/// The time interval between sending ping messages. +pub const PING_INTERVAL: Timestamp = Timestamp::from_millis(3_000); + +/// The size of the `length` section of the [`Block`]'s byte format. +/// +/// See [`BlockStream`] for more information. +pub const LEN_SIZE: usize = 10; + +/// The sequence of bytes used to denote the end of a [`Block`] in its byte format. +/// +/// See [`BlockStream`] for more information. +pub const END_MARKER: &str = "\r\n"; + +/// Represents the different kinds of commands that could be sent or received in a block. +#[derive(Copy, Clone, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)] +#[repr(u8)] +pub enum Command { + Ok = 1, + + /// Used to send password just after making a TCP connection. + Password = 2, + BinMode = 3, + + /// Used to send description just after making a TCP connection. + CcDesc = 4, + BinHeader = 5, + BinData = 6, + + /// Used to send EPG metadata across network. + EpgData = 7, + Error = 51, + UnknownCommand = 52, + WrongPassword = 53, + ConnLimit = 54, + + /// Used to send ping messages to check network connectivity. + Ping = 55, +} + +/// Represents the smallest unit of data that could be sent or received from network. +/// +/// A [`Block`] consists of a [`Command`] and some binary data associated with it. The kind of +/// block is denoted by its [`Command`]. The binary data has different formats or information based +/// on the kind of [`Command`]. +/// +/// Any subtitle data, metadata, ping, etc, that needs to be sent by network goes through in the +/// form of a [`Block`]. See [`BlockStream`] for more information on how a [`Block`] is sent or +/// received. +pub struct Block<'a> { + command: Command, + data: Cow<'a, [u8]>, +} + +impl<'a> Block<'a> { + fn new(command: Command, data: &'a [u8]) -> Block<'a> { + Block { + command, + data: Cow::from(data), + } + } + + fn new_owned(command: Command, data: Vec) -> Block<'a> { + Block { + command, + data: Cow::from(data), + } + } + + /// Returns the kind of [`Block`] denoted by its [`Command`]. + pub fn command(&self) -> Command { + self.command + } + + /// Returns the associated data of [`Block`]. + pub fn data(&self) -> &[u8] { + &self.data + } + + /// Create a new [`Ping`](Command::Ping) Block. + pub fn ping() -> Block<'a> { + Block::new_owned(Command::Ping, vec![]) + } + + /// Create a new [`BinHeader`](Command::BinHeader) Block along with `header` data. + pub fn bin_header(header: &'a [u8]) -> Block<'a> { + Block::new(Command::BinHeader, header) + } + + /// Create a new [`BinData`](Command::BinData) Block along with `data`. + pub fn bin_data(data: &'a [u8]) -> Block<'a> { + Block::new(Command::BinData, data) + } + + /// Create a new [`Password`](Command::Password) Block along with `password` data. + /// + /// The data of the returned [`Block`] will consist of `password` encoded as UTF-8 bytes which + /// is not nul-terminated. + /// + /// # Examples + /// ``` + /// # use lib_ccxr::net::Block; + /// let b = Block::password("A"); + /// assert_eq!(b.data(), &[b'A']); + /// ``` + pub fn password(password: &'a str) -> Block<'a> { + Block::new(Command::Password, password.as_bytes()) + } + + /// Create a new [`CcDesc`](Command::CcDesc) Block along with `desc` data. + /// + /// The data of the returned [`Block`] will consist of `desc` encoded as UTF-8 bytes which is + /// not nul-terminated. + /// + /// # Examples + /// ``` + /// # use lib_ccxr::net::Block; + /// let b = Block::cc_desc("Teletext"); + /// assert_eq!(b.data(), &[b'T', b'e', b'l', b'e', b't', b'e', b'x', b't']); + /// ``` + pub fn cc_desc(desc: &'a str) -> Block<'a> { + Block::new(Command::CcDesc, desc.as_bytes()) + } + + /// Create a new [`EpgData`](Command::EpgData) Block along with the related metadata used in + /// EPG. + /// + /// All the parameters are encoded as UTF-8 bytes which are nul-terminated. If a parameter is + /// [`None`], then it is considered to be equivalent to an empty String. All these + /// nul-terminated UTF-8 bytes are placed one after the other in the order of `start`, `stop`, + /// `title`, `desc`, `lang`, `category` with nul character acting as the seperator between + /// these sections. + /// + /// # Examples + /// ``` + /// # use lib_ccxr::net::Block; + /// let b = Block::epg_data("A", "B", Some("C"), None, Some("D"), None); + /// assert_eq!(b.data(), &[b'A', b'\0', b'B', b'\0', b'C', b'\0', b'\0', b'D', b'\0', b'\0']); + /// ``` + pub fn epg_data( + start: &str, + stop: &str, + title: Option<&str>, + desc: Option<&str>, + lang: Option<&str>, + category: Option<&str>, + ) -> Block<'a> { + let title = title.unwrap_or(""); + let desc = desc.unwrap_or(""); + let lang = lang.unwrap_or(""); + let category = category.unwrap_or(""); + + // Plus 1 to accomodate space for the nul character + let start_len = start.len() + 1; + let stop_len = stop.len() + 1; + let title_len = title.len() + 1; + let desc_len = desc.len() + 1; + let lang_len = lang.len() + 1; + let category_len = category.len() + 1; + + let total_len = start_len + stop_len + title_len + desc_len + lang_len + category_len; + let mut data = Vec::with_capacity(total_len); + + data.extend_from_slice(start.as_bytes()); + data.extend_from_slice("\0".as_bytes()); + data.extend_from_slice(stop.as_bytes()); + data.extend_from_slice("\0".as_bytes()); + data.extend_from_slice(title.as_bytes()); + data.extend_from_slice("\0".as_bytes()); + data.extend_from_slice(desc.as_bytes()); + data.extend_from_slice("\0".as_bytes()); + data.extend_from_slice(lang.as_bytes()); + data.extend_from_slice("\0".as_bytes()); + data.extend_from_slice(category.as_bytes()); + data.extend_from_slice("\0".as_bytes()); + + Block::new_owned(Command::EpgData, data) + } +} + +/// The [`BlockStream`] trait allows for sending and receiving [`Block`]s across the network. +/// +/// The only two implementers of [`BlockStream`] are [`SendTarget`] and [`RecvSource`] which are +/// used for sending and receiving blocks respectively. +/// +/// This trait provides an abstraction over the different interfaces of [`TcpStream`] and +/// [`UdpSocket`]. The implementers only need to implement the functionality to send and receive +/// bytes through network by [`BlockStream::send`] and [`BlockStream::recv`]. The functionality to +/// send and receive [`Block`] will be automatically made available by [`BlockStream::send_block`] +/// and [`BlockStream::recv_block`]. +/// +/// A [`Block`] is sent or received across the network using a byte format. Since a [`Block`] +/// consists of `command` and variable sized `data`, it is encoded in the following way. +/// +/// | Section | Length | Description | +/// |------------|--------------|---------------------------------------------------------------------------| +/// | command | 1 | The `command` enconded as its corresponding byte value. | +/// | length | [`LEN_SIZE`] | The length of `data` encoded as nul-terminated string form of the number. | +/// | data | length | The associated `data` bytes whose meaning is dependent on `command`. | +/// | end_marker | 2 | This value is always [`END_MARKER`], signifying end of current block. | +/// +/// The only exception to the above format is a [`Ping`] [`Block`] which is encoded only with its 1-byte +/// command section. It does not have length, data or end_marker sections. +/// +/// [`SendTarget`]: super::SendTarget +/// [`RecvSource`]: super::RecvSource +/// [`TcpStream`]: std::net::TcpStream +/// [`UdpSocket`]: std::net::UdpSocket +/// [`Ping`]: Command::Ping +pub trait BlockStream { + /// Send the bytes in `buf` across the network. + fn send(&mut self, buf: &[u8]) -> io::Result; + + /// Receive the bytes from network and place them in `buf`. + fn recv(&mut self, buf: &mut [u8]) -> io::Result; + + /// Send a [`Block`] across the network. + /// + /// It returns a [`NetError`] if some transmission failure ocurred, or else it will return a bool + /// that indicates the status of this connection. It will be `false` if the connection shutdown + /// correctly. + fn send_block(&mut self, block: &Block<'_>) -> Result { + let Block { command, data } = block; + + if self.send(&[(*command).into()])? != 1 { + return Ok(false); + } + + if *command == Command::Ping { + return Ok(true); + } + + let mut length_part = [0; LEN_SIZE]; + let _ = write!(length_part.as_mut_slice(), "{}", data.len()); + if self.send(&length_part)? != LEN_SIZE { + return Ok(false); + } + + if !data.is_empty() && self.send(data)? != data.len() { + return Ok(false); + } + + if self.send(END_MARKER.as_bytes())? != END_MARKER.len() { + return Ok(false); + } + + #[cfg(feature = "debug_out")] + eprintln!("block = {}", block); + + Ok(true) + } + + /// Receive a [`Block`] from the network. + /// + /// It returns a [`NetError`] if some transmission failure ocurred or byte format is violated. + /// It will return a [`None`] if the connection has shutdown down correctly. + fn recv_block<'a>(&mut self) -> Result>, NetError> { + let mut command_byte = [0_u8; 1]; + if self.recv(&mut command_byte)? != 1 { + return Ok(None); + } + + let command: Command = command_byte[0] + .try_into() + .map_err(|_| NetError::InvalidBytes { + location: "command", + })?; + + if command == Command::Ping { + return Ok(Some(Block::ping())); + } + + let mut length_bytes = [0u8; LEN_SIZE]; + if self.recv(&mut length_bytes)? != LEN_SIZE { + return Ok(None); + } + let end = length_bytes + .iter() + .position(|&x| x == b'\0') + .unwrap_or(LEN_SIZE); + let length: usize = String::from_utf8_lossy(&length_bytes[0..end]) + .parse() + .map_err(|_| NetError::InvalidBytes { location: "length" })?; + + let mut data = vec![0u8; length]; + if self.recv(&mut data)? != length { + return Ok(None); + } + + let mut end_marker = [0u8; END_MARKER.len()]; + if self.recv(&mut end_marker)? != END_MARKER.len() { + return Ok(None); + } + if end_marker != END_MARKER.as_bytes() { + return Err(NetError::InvalidBytes { + location: "end_marker", + }); + } + + let block = Block::new_owned(command, data); + + #[cfg(feature = "debug_out")] + eprintln!("{}", block); + + Ok(Some(block)) + } +} + +impl Display for Block<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let _ = write!(f, "[Rust] {} {} ", self.command, self.data.len()); + + if self.command != Command::BinHeader && self.command != Command::BinData { + let _ = write!(f, "{} ", &*String::from_utf8_lossy(&self.data)); + } + + let _ = write!(f, "\\r\\n"); + + Ok(()) + } +} + +impl Display for Command { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let message = match self { + Command::Ok => "OK", + Command::Password => "PASSWORD", + Command::BinMode => "BIN_MODE", + Command::CcDesc => "CC_DESC", + Command::BinHeader => "BIN_HEADER", + Command::BinData => "BIN_DATA", + Command::EpgData => "EPG_DATA", + Command::Error => "ERROR", + Command::UnknownCommand => "UNKNOWN_COMMAND", + Command::WrongPassword => "WRONG_PASSWORD", + Command::ConnLimit => "CONN_LIMIT", + Command::Ping => "PING", + }; + + let _ = write!(f, "{message}"); + + Ok(()) + } +} diff --git a/src/rust/lib_ccxr/src/net/c_functions.rs b/src/rust/lib_ccxr/src/net/c_functions.rs new file mode 100644 index 000000000..191540b65 --- /dev/null +++ b/src/rust/lib_ccxr/src/net/c_functions.rs @@ -0,0 +1,99 @@ +//! Provides pure Rust equivalent functions for some functions in `networking.c`. + +use crate::net::*; + +use std::sync::RwLock; + +static TARGET: RwLock> = RwLock::new(None); +static SOURCE: RwLock> = RwLock::new(None); + +/// Rust equivalent for `connect_to_srv` function in C. Uses Rust-native types as input and output. +pub fn connect_to_srv( + addr: &'static str, + port: Option, + cc_desc: Option<&'static str>, + pwd: Option<&'static str>, +) { + let mut send_target = TARGET.write().expect("Unable to use TARGET"); + *send_target = Some(SendTarget::new(SendTargetConfig { + target_addr: addr, + port, + password: pwd, + description: cc_desc, + })); +} + +/// Rust equivalent for `net_send_header` function in C. Uses Rust-native types as input and output. +pub fn net_send_header_rust(data: &[u8]) { + // Rename back to `net_send_header` when C function is removed from encoder + let mut send_target = TARGET.write().unwrap(); + send_target.as_mut().unwrap().send_header(data); +} + +/// Rust equivalent for `net_send_cc` function in C. Uses Rust-native types as input and output. +pub fn net_send_cc(data: &[u8]) -> bool { + let mut send_target = TARGET.write().unwrap(); + send_target.as_mut().unwrap().send_cc(data) +} + +/// Rust equivalent for `net_check_conn` function in C. Uses Rust-native types as input and output. +pub fn net_check_conn() { + let mut send_target = TARGET.write().unwrap(); + send_target.as_mut().unwrap().check_connection(); +} + +/// Rust equivalent for `net_send_epg` function in C. Uses Rust-native types as input and output. +pub fn net_send_epg( + start: &str, + stop: &str, + title: Option<&str>, + desc: Option<&str>, + lang: Option<&str>, + category: Option<&str>, +) { + let mut send_target = TARGET.write().unwrap(); + send_target + .as_mut() + .unwrap() + .send_epg_data(start, stop, title, desc, lang, category); +} + +/// Rust equivalent for `net_tcp_read` function in C. Uses Rust-native types as input and output. +pub fn net_tcp_read(buffer: &mut [u8]) -> Option { + let mut recv_source = SOURCE.write().unwrap(); + if let Ok(b) = recv_source.as_mut().unwrap().recv_header_or_cc() { + if let Some(block) = b { + buffer[..block.data().len()].copy_from_slice(block.data()); + Some(block.data().len()) + } else { + Some(0) + } + } else { + None + } +} + +/// Rust equivalent for `net_udp_read` function in C. Uses Rust-native types as input and output. +pub fn net_udp_read(buffer: &mut [u8]) -> Option { + let mut recv_source = SOURCE.write().unwrap(); + recv_source.as_mut().unwrap().recv(buffer).ok() +} + +/// Rust equivalent for `start_tcp_srv` function in C. Uses Rust-native types as input and output. +pub fn start_tcp_srv(port: Option, pwd: Option<&'static str>) { + let mut recv_source = SOURCE.write().unwrap(); + *recv_source = Some(RecvSource::new(RecvSourceConfig::Tcp { + port, + password: pwd, + })); +} + +/// Rust equivalent for `start_udp_srv` function in C. Uses Rust-native types as input and output. +pub fn start_udp_srv(src: Option<&'static str>, addr: Option<&'static str>, port: u16) { + let mut recv_source = SOURCE.write().unwrap(); + *recv_source = Some(RecvSource::new(RecvSourceConfig::Udp { + source: src, + address: addr, + port, + })); +} diff --git a/src/rust/lib_ccxr/src/net/mod.rs b/src/rust/lib_ccxr/src/net/mod.rs new file mode 100644 index 000000000..0d8697b84 --- /dev/null +++ b/src/rust/lib_ccxr/src/net/mod.rs @@ -0,0 +1,54 @@ +//! A module for sending and receiving subtitle data across the network. +//! +//! The [`SendTarget`] struct provides methods to send data to the network. It can be constructed +//! from [`SendTargetConfig`]. +//! +//! The [`RecvSource`] struct provides methods to receive data from the network. It can be +//! constructed from [`RecvSourceConfig`]. +//! +//! Any data to be sent across the network in the stored in the form of a [`Block`]. The +//! [`BlockStream`] can encode a [`Block`] as a byte sequence using a custom byte format which can +//! then be sent or received using standard networking primitives. See [`BlockStream`] to know more +//! about the custom byte format. +//! +//! # Conversion Guide +//! +//! | From | To | +//! |--------------------------------|-----------------------------------| +//! | `connect_to_srv` | [`SendTarget::new`] | +//! | `net_send_header` | [`SendTarget::send_header`] | +//! | `net_send_cc` | [`SendTarget::send_cc`] | +//! | `net_send_epg` | [`SendTarget::send_epg_data`] | +//! | `net_check_conn` | [`SendTarget::check_connection`] | +//! | `start_tcp_srv` | [`RecvSource::new`] | +//! | `start_udp_srv` | [`RecvSource::new`] | +//! | `net_tcp_read` | [`RecvSource::recv_header_or_cc`] | +//! | `net_udp_read` | [`RecvSource::send`] | +//! | `read_block` | [`BlockStream::recv_block`] | +//! | `write_block` | [`BlockStream::send_block`] | +//! | Commands in Protocol Constants | [`Command`] | +//! | `DFT_PORT` | [`DEFAULT_TCP_PORT`] | +//! | `NO_RESPONCE_INTERVAL` | [`NO_RESPONSE_INTERVAL`] | +//! | `PING_INTERVAL` | [`PING_INTERVAL`] | + +mod block; +pub mod c_functions; +mod source; +mod target; + +pub use crate::net::{block::*, source::*, target::*}; + +/// A collective [`Error`](std::error::Error) type that encompasses all the possible error cases +/// when sending, receiving or parsing data during networking operations. +#[derive(thiserror::Error, Debug)] +pub enum NetError { + /// An Error ocurred within std giving a [`io::Error`] + #[error(transparent)] + IoError(#[from] std::io::Error), + + /// The received bytes do not follow a [`Block`]'s byte format. + /// + /// See [`BlockStream`] for more information. + #[error("invalid bytes while parsing {location}")] + InvalidBytes { location: &'static str }, +} diff --git a/src/rust/lib_ccxr/src/net/source.rs b/src/rust/lib_ccxr/src/net/source.rs new file mode 100644 index 000000000..60deccc7a --- /dev/null +++ b/src/rust/lib_ccxr/src/net/source.rs @@ -0,0 +1,415 @@ +use super::{Block, BlockStream, Command, NetError, DEFAULT_TCP_PORT, PING_INTERVAL}; +use crate::time::units::Timestamp; +use crate::util::log::{fatal, info, ExitCause}; + +use socket2::{Domain, Socket, Type}; + +use std::io; +use std::io::{Read, Write}; +use std::net::{ + IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, TcpListener, TcpStream, ToSocketAddrs, + UdpSocket, +}; + +/// An enum of configuration parameters to construct [`RecvSource`]. +#[derive(Copy, Clone, Debug)] +pub enum RecvSourceConfig<'a> { + Tcp { + /// The port number where TCP socket will be bound. + /// + /// If no port number is provided then [`DEFAULT_TCP_PORT`] will be used instead. + port: Option, + + /// The password of receiving server. + /// + /// The password sent from client will be compared to this and only allow furthur + /// communication if the passwords match. + password: Option<&'a str>, + }, + Udp { + /// Source's IP address or hostname if trying to open a multicast SSM channel. + source: Option<&'a str>, + + /// The IP address or hostname where UDP socket will be bound. + address: Option<&'a str>, + + /// The port number where UDP socket will be bound. + port: u16, + }, +} + +enum SourceSocket { + Tcp(TcpStream), + Udp { + socket: UdpSocket, + source: Option, + address: Ipv4Addr, + }, +} + +/// A struct used for receiving subtitle data from the network. +/// +/// Even though it exposes methods from [`BlockStream`], it is recommended to not use them. +/// Instead use the methods provided directly by [`RecvSource`] like +/// [`RecvSource::recv_header_or_cc`]. +/// +/// Note: To create a [`RecvSource`], one must first construct a [`RecvSourceConfig`]. +/// +/// ```no_run +/// # use lib_ccxr::net::{RecvSource, RecvSourceConfig}; +/// let config = RecvSourceConfig::Tcp { +/// port: None, +/// password: Some("12345678"), +/// }; +/// let mut recv_source = RecvSource::new(config); +/// +/// // Once recv_source is constructed, we can use it to receive data. +/// let block = recv_source.recv_header_or_cc().unwrap(); +/// ``` +pub struct RecvSource { + socket: SourceSocket, + last_ping: Timestamp, +} + +impl BlockStream for RecvSource { + fn send(&mut self, buf: &[u8]) -> io::Result { + match &mut self.socket { + SourceSocket::Tcp(stream) => stream.write(buf), + SourceSocket::Udp { socket, .. } => socket.send(buf), + } + } + + fn recv(&mut self, buf: &mut [u8]) -> io::Result { + match &mut self.socket { + SourceSocket::Tcp(stream) => stream.read(buf), + SourceSocket::Udp { + socket, + source, + address, + } => { + if cfg!(target_os = "windows") { + socket.recv(buf) + } else { + let should_check_source = address.is_multicast() && source.is_some(); + if should_check_source { + loop { + if let Ok((size, src_addr)) = socket.recv_from(buf) { + if src_addr.ip() == source.unwrap() { + return Ok(size); + } + } + } + } else { + socket.recv(buf) + } + } + } + } + } +} + +impl RecvSource { + /// Create a new [`RecvSource`] from the configuration parameters of [`RecvSourceConfig`]. + /// + /// Note: This method attempts to create a server. It does not return a [`Result`]. When + /// it is unable to start a server, it crashes instantly by calling [`fatal!`]. + /// + /// This method will continously block until a connection with a client is made. + pub fn new(config: RecvSourceConfig) -> RecvSource { + match config { + RecvSourceConfig::Tcp { port, password } => { + let port = port.unwrap_or(DEFAULT_TCP_PORT); + + info!( + "\n\r----------------------------------------------------------------------\n" + ); + info!("Binding to {}\n", port); + + let addresses = [ + SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port), + SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port), + ]; + + let listener = TcpListener::bind(addresses.as_slice()).unwrap_or_else( + |_| fatal!(cause = ExitCause::Failure; "Unable to start server\n"), + ); + + if let Some(pwd) = &password { + info!("Password: {}\n", pwd); + } + + info!("Waiting for connections\n"); + + loop { + if let Ok((socket, _)) = listener.accept() { + let mut source = RecvSource { + socket: SourceSocket::Tcp(socket), + last_ping: Timestamp::from_millis(0), + }; + if check_password(&mut source, password) { + return source; + } + + info!("Connection closed\n"); + drop(source); + } + } + } + RecvSourceConfig::Udp { + source, + address, + port, + } => { + // Handle address resolution - match C behavior more closely + let address = address.map(|x| { + // First try to parse as direct IP address + if let Ok(ip) = x.parse::() { + return ip; + } + + // Handle localhost specifically + if x == "localhost" { + return Ipv4Addr::LOCALHOST; + } + + // Try DNS resolution with more lenient approach + match (x, 0).to_socket_addrs() { + Ok(addrs) => { + for s in addrs { + match s { + SocketAddr::V4(sv4) => return *sv4.ip(), + SocketAddr::V6(sv6) => { + // Convert IPv6 localhost to IPv4 + if sv6.ip().is_loopback() { + return Ipv4Addr::LOCALHOST; + } + // Try IPv4-mapped IPv6 + if let Some(ipv4) = sv6.ip().to_ipv4_mapped() { + return ipv4; + } + } + } + } + // If we get here, no suitable address was found + fatal!(cause = ExitCause::Failure; "Could not resolve udp address to IPv4") + }, + Err(_) => { + fatal!(cause = ExitCause::Failure; "Could not resolve udp address") + } + } + }).unwrap_or(Ipv4Addr::UNSPECIFIED); + + // Handle source resolution - don't try to resolve hostnames with ports + let source = source.map(|x| { + // Check if this looks like hostname:port format (which is invalid for source) + if x.contains(':') { + info!( + "Warning: Source '{}' contains port - this should be hostname only\n", + x + ); + // Try to extract just the hostname part + if let Some(hostname) = x.split(':').next() { + info!("Extracting hostname '{}' from '{}'\n", hostname, x); + return resolve_hostname_to_ipv4(hostname); + } + } + + resolve_hostname_to_ipv4(x) + }); + + // Helper function to resolve hostname to IPv4 + fn resolve_hostname_to_ipv4(hostname: &str) -> Ipv4Addr { + // First try to parse as direct IP address + if let Ok(ip) = hostname.parse::() { + info!("Source address: {}\n", ip); + return ip; + } + + // Handle localhost specifically + if hostname == "localhost" { + info!("Source address: {}\n", Ipv4Addr::LOCALHOST); + return Ipv4Addr::LOCALHOST; + } + + // Try DNS resolution + match (hostname, 0).to_socket_addrs() { + Ok(addrs) => { + for s in addrs { + info!("Resolved address: {}\n", s); + match s { + SocketAddr::V4(sv4) => { + info!("Source address: {}\n", sv4.ip()); + return *sv4.ip(); + } + SocketAddr::V6(sv6) => { + // Convert IPv6 localhost to IPv4 + if sv6.ip().is_loopback() { + info!( + "Source address: {} (converted from IPv6)\n", + Ipv4Addr::LOCALHOST + ); + return Ipv4Addr::LOCALHOST; + } + // Try IPv4-mapped IPv6 + if let Some(ipv4) = sv6.ip().to_ipv4_mapped() { + info!( + "Source address: {} (converted from IPv6)\n", + ipv4 + ); + return ipv4; + } + } + } + } + // If we get here, no suitable address was found + info!( + "Warning: Only IPv6 addresses found for {}, but IPv4 required\n", + hostname + ); + fatal!(cause = ExitCause::Failure; "Could not resolve udp source to IPv4") + } + Err(e) => { + info!("DNS resolution failed for {}: {}\n", hostname, e); + fatal!(cause = ExitCause::Failure; "Could not resolve udp source") + } + } + } + + info!( + "Source: {}\n", + source + .map(|s| s.to_string()) + .unwrap_or_else(|| "None".to_string()) + ); + info!("Source port: {}\n", port); + + let socket = Socket::new(Domain::IPV4, Type::DGRAM, None).unwrap_or_else( + |_| fatal!(cause = ExitCause::Failure; "Socket creation error"), + ); + + if address.is_multicast() { + socket.set_reuse_address(true).unwrap_or_else(|_| { + info!("Cannot set reuse address\n"); + }); + } + + let binding_address = if cfg!(target_os = "windows") && address.is_multicast() { + Ipv4Addr::UNSPECIFIED + } else { + address + }; + + socket + .bind(&SocketAddrV4::new(binding_address, port).into()) + .unwrap_or_else(|_| fatal!(cause = ExitCause::Bug; "Socket bind error")); + + if address.is_multicast() { + if let Some(src) = source { + socket.join_ssm_v4(&src, &address, &Ipv4Addr::UNSPECIFIED) + } else { + socket.join_multicast_v4(&address, &Ipv4Addr::UNSPECIFIED) + } + .unwrap_or_else( + |_| fatal!(cause = ExitCause::Bug; "Cannot join multicast group"), + ); + } + + info!( + "\n\r----------------------------------------------------------------------\n" + ); + if address == Ipv4Addr::UNSPECIFIED { + info!("\rReading from UDP socket {}\n", port); + } else if let Some(src) = source { + info!("\rReading from UDP socket {}@{}:{}\n", src, address, port); + } else { + info!("\rReading from UDP socket {}:{}\n", address, port); + } + + RecvSource { + socket: SourceSocket::Udp { + socket: socket.into(), + address, + source, + }, + last_ping: Timestamp::from_millis(0), + } + } + } + } + + /// Receive a [`BinHeader`] or [`BinData`] [`Block`]. + /// + /// Note that this method will continously block until it receives a + /// [`BinHeader`] or [`BinData`] [`Block`]. + /// + /// It returns a [`NetError`] if some transmission failure ocurred or byte format is violated. + /// It will return a [`None`] if the connection has shutdown down correctly. + /// + /// [`BinHeader`]: Command::BinHeader + /// [`BinData`]: Command::BinData + pub fn recv_header_or_cc<'a>(&mut self) -> Result>, NetError> { + let now = Timestamp::now(); + if self.last_ping.millis() == 0 { + self.last_ping = now; + } + + if now - self.last_ping > PING_INTERVAL { + self.last_ping = now; + if self.send_ping().is_err() { + fatal!(cause = ExitCause::Failure; "Unable to send keep-alive packet to client\n"); + } + } + + loop { + if let Some(block) = self.recv_block()? { + if block.command() == Command::BinHeader || block.command() == Command::BinData { + return Ok(Some(block)); + } + } else { + return Ok(None); + } + } + } + + /// Send a [`Ping`](Command::Ping) [`Block`]. + /// + /// It returns a [`NetError`] if some transmission failure ocurred, or else it will return a bool + /// that indicates the status of this connection. It will be `false` if the connection shutdown + /// correctly. + fn send_ping(&mut self) -> Result { + self.send_block(&Block::ping()) + } +} + +/// Check if the received password matches with the current password. +/// +/// This methods attempts to read a [`Password`](Command::Password) [`Block`] from `socket`. Any +/// form of error in this operation results in `false`. +/// +/// If `password` is [`None`], then no checking is done and results in `true`. +fn check_password(socket: &mut RecvSource, password: Option<&str>) -> bool { + let block = match socket.recv_block() { + Ok(Some(b)) => b, + _ => return false, + }; + + let pwd = match password { + Some(p) => p, + None => return true, + }; + + if block.command() == Command::Password && String::from_utf8_lossy(block.data()) == *pwd { + true + } else { + #[cfg(feature = "debug_out")] + { + eprintln!("[C] Wrong password"); + eprintln!("[S] PASSWORD"); + } + // TODO: Check if the below portion is really required, since the receiver is not even + // listening for a Password block + let _ = socket.send_block(&Block::password("")); + + false + } +} diff --git a/src/rust/lib_ccxr/src/net/target.rs b/src/rust/lib_ccxr/src/net/target.rs new file mode 100644 index 000000000..4e490fc2f --- /dev/null +++ b/src/rust/lib_ccxr/src/net/target.rs @@ -0,0 +1,294 @@ +use super::{ + block::{Block, BlockStream, Command, DEFAULT_TCP_PORT, NO_RESPONSE_INTERVAL, PING_INTERVAL}, + NetError, +}; +use crate::time::units::Timestamp; +use crate::util::log::{fatal, info, ExitCause}; + +use std::io; +use std::io::{Read, Write}; +use std::net::TcpStream; + +/// A struct of configuration parameters to construct [`SendTarget`]. +#[derive(Copy, Clone, Debug)] +pub struct SendTargetConfig<'a> { + /// Target's IP address or hostname. + pub target_addr: &'a str, + + /// Target's port number. + /// + /// If no port number is provided then [`DEFAULT_TCP_PORT`] will be used instead. + pub port: Option, + + /// Password to be sent after establishing connection. + pub password: Option<&'a str>, + + /// Description to sent after establishing connection. + pub description: Option<&'a str>, +} + +/// A struct used for sending subtitle data across the network. +/// +/// Even though it exposes methods from [`BlockStream`], it is recommended to not use them. +/// Instead use the methods provided directly by [`SendTarget`] like [`SendTarget::send_header`], +/// [`SendTarget::send_cc`], etc. +/// +/// To create a [`SendTarget`], one must first construct a [`SendTargetConfig`]. +/// +/// ```no_run +/// # use lib_ccxr::net::{SendTarget, SendTargetConfig}; +/// let config = SendTargetConfig { +/// target_addr: "192.168.60.133", +/// port: None, +/// password: Some("12345678"), +/// description: None, +/// }; +/// let mut send_target = SendTarget::new(config); +/// +/// // Once send_target is constructed, we can use it to send different kinds of data. +/// # let header = &[0u8; 1]; +/// send_target.send_header(header); +/// # let cc = &[0u8; 1]; +/// send_target.send_cc(cc); +/// ``` +pub struct SendTarget<'a> { + stream: Option, + config: SendTargetConfig<'a>, + header_data: Option>, + last_ping: Timestamp, + last_send_ping: Timestamp, +} + +impl BlockStream for SendTarget<'_> { + fn send(&mut self, buf: &[u8]) -> io::Result { + self.stream.as_mut().unwrap().write(buf) + } + + fn recv(&mut self, buf: &mut [u8]) -> io::Result { + self.stream.as_mut().unwrap().read(buf) + } +} + +impl<'a> SendTarget<'a> { + /// Create a new [`SendTarget`] from the configuration parameters of [`SendTargetConfig`]. + /// + /// Note: This method attempts to connect to a server. It does not return a [`Result`]. When + /// it is unable to connect to a server, it crashes instantly by calling [`fatal!`]. + pub fn new(config: SendTargetConfig<'a>) -> SendTarget<'a> { + if config.target_addr.is_empty() { + info!("Server address is not set\n"); + fatal!( + cause = ExitCause::Failure; + "Unable to connect, address passed is null\n" + ); + } + info!("Target address: {}\n", config.target_addr); // TODO remove this + info!("Target port: {}\n", config.port.unwrap_or(DEFAULT_TCP_PORT)); + let tcp_stream = TcpStream::connect(( + config.target_addr, + config.port.unwrap_or(DEFAULT_TCP_PORT), + )) + .unwrap_or_else( + |_| fatal!(cause = ExitCause::Failure; "Unable to connect (tcp connection error).\n"), + ); + + tcp_stream.set_nonblocking(true).unwrap_or_else( + |_| fatal!(cause = ExitCause::Failure; "Unable to connect (set nonblocking).\n"), + ); + + let mut send_target = SendTarget { + stream: Some(tcp_stream), + config, + header_data: None, + last_ping: Timestamp::from_millis(0), + last_send_ping: Timestamp::from_millis(0), + }; + + send_target.send_password().unwrap_or_else( + |_| fatal!(cause = ExitCause::Failure; "Unable to connect (sending password).\n"), + ); + + send_target.send_description().unwrap_or_else( + |_| fatal!(cause = ExitCause::Failure; "Unable to connect (sending cc_desc).\n"), + ); + + info!( + "Connected to {}:{}\n", + send_target.config.target_addr, + send_target.config.port.unwrap_or(DEFAULT_TCP_PORT) + ); + + send_target + } + + /// Consumes the [`SendTarget`] only returning its internal stream. + /// + /// Note: Crashes if `self.stream` is not set. + fn into_stream(self) -> TcpStream { + self.stream.expect("TcpStream must be set") + } + + /// Send a [`BinHeader`](Command::BinHeader) [`Block`] returning if the operation was successful. + pub fn send_header(&mut self, data: &[u8]) -> bool { + #[cfg(feature = "debug_out")] + { + eprintln!("Sending header (len = {}): ", data.len()); + eprintln!( + "File created by {:02X} version {:02X}{:02X}", + data[3], data[4], data[5] + ); + eprintln!("File format revision: {:02X}{:02X}", data[6], data[7]); + } + + if let Ok(true) = self.send_block(&Block::bin_header(data)) { + } else { + println!("Can't send BIN header"); + return false; + } + + if self.header_data.is_none() { + self.header_data = Some(data.into()) + } + + true + } + + /// Send a [`BinData`](Command::BinData) [`Block`] returning if the operation was successful. + pub fn send_cc(&mut self, data: &[u8]) -> bool { + #[cfg(feature = "debug_out")] + { + eprintln!("[C] Sending {} bytes", data.len()); + } + + if let Ok(true) = self.send_block(&Block::bin_data(data)) { + } else { + println!("Can't send BIN data"); + return false; + } + + true + } + + /// Send a [`EpgData`](Command::EpgData) [`Block`] returning if the operation was successful. + pub fn send_epg_data( + &mut self, + start: &str, + stop: &str, + title: Option<&str>, + desc: Option<&str>, + lang: Option<&str>, + category: Option<&str>, + ) -> bool { + let block = Block::epg_data(start, stop, title, desc, lang, category); + + #[cfg(feature = "debug_out")] + { + eprintln!("[C] Sending EPG: {} bytes", block.data().len()) + } + + if let Ok(true) = self.send_block(&block) { + } else { + eprintln!("Can't send EPG data"); + return false; + } + + true + } + + /// Send a [`Ping`](Command::Ping) [`Block`]. + /// + /// It returns a [`NetError`] if some transmission failure ocurred, or else it will return a bool + /// that indicates the status of this connection. It will be `false` if the connection shutdown + /// correctly. + fn send_ping(&mut self) -> Result { + self.send_block(&Block::ping()) + } + + /// Send a [`Password`](Command::Password) [`Block`]. + /// + /// It returns a [`NetError`] if some transmission failure ocurred, or else it will return a bool + /// that indicates the status of this connection. It will be `false` if the connection shutdown + /// correctly. + fn send_password(&mut self) -> Result { + let password = self.config.password.unwrap_or(""); + self.send_block(&Block::password(password)) + } + + /// Send a [`CcDesc`](Command::CcDesc) [`Block`]. + /// + /// It returns a [`NetError`] if some transmission failure ocurred, or else it will return a bool + /// that indicates the status of this connection. It will be `false` if the connection shutdown + /// correctly. + fn send_description(&mut self) -> Result { + let description = self.config.description.unwrap_or(""); + self.send_block(&Block::cc_desc(description)) + } + + /// Check the connection health and reset connection if necessary. + /// + /// This method determines the connection health by comparing the time since last [`Ping`] + /// [`Block`] was received with [`NO_RESPONSE_INTERVAL`]. If it exceeds the + /// [`NO_RESPONSE_INTERVAL`], the connection is reset. + /// + /// This method also sends timely [`Ping`] [`Block`]s back to the server based on the + /// [`PING_INTERVAL`]. This method will crash instantly with [`fatal!`] if it is unable to send + /// data. + /// + /// [`Ping`]: Command::Ping + pub fn check_connection(&mut self) { + let now = Timestamp::now(); + + if self.last_ping.millis() == 0 { + self.last_ping = now; + } + + loop { + if self + .recv_block() + .ok() + .flatten() + .map(|x| x.command() == Command::Ping) + .unwrap_or(false) + { + #[cfg(feature = "debug_out")] + { + eprintln!("[S] Received PING"); + } + self.last_ping = now; + } else { + break; + } + } + + if now - self.last_ping > NO_RESPONSE_INTERVAL { + eprintln!( + "[S] No PING received from the server in {} sec, reconnecting", + NO_RESPONSE_INTERVAL.seconds() + ); + + self.stream + .take() + .unwrap() + .shutdown(std::net::Shutdown::Both) + .expect("Unable to shutdown the TCP Server"); + + self.stream = Some(SendTarget::new(self.config).into_stream()); + + // `self.header_data` is only temporarily taken, since it will be refilled inside + // `send_header` function. + if let Some(header_data) = self.header_data.take() { + self.send_header(header_data.as_slice()); + } + + self.last_ping = now; + } + + if now - self.last_send_ping >= PING_INTERVAL { + if self.send_ping().is_err() { + fatal!(cause = ExitCause::Failure; "Unable to send data\n"); + } + + self.last_send_ping = now; + } + } +} diff --git a/src/rust/src/decoder/output.rs b/src/rust/src/decoder/output.rs index 1dad8c149..6faba7e61 100644 --- a/src/rust/src/decoder/output.rs +++ b/src/rust/src/decoder/output.rs @@ -70,7 +70,7 @@ impl<'a> Writer<'a> { pub fn write_done(&mut self) { if self.write_format == ccx_output_format::CCX_OF_SAMI { if let Err(err) = self.write_sami_footer() { - warn!("{}", err); + warn!("{err}"); } } else { debug!("dtvcc_write_done: no handling required"); @@ -106,10 +106,7 @@ pub fn color_to_hex(color: u8) -> (u8, u8, u8) { let red = color >> 4; let green = (color >> 2) & 0x3; let blue = color & 0x3; - debug!( - "Color: {} [{:06x}] {} {} {}", - color, color, red, green, blue - ); + debug!("Color: {color} [{color:06x}] {red} {green} {blue}"); (red, green, blue) } diff --git a/src/rust/src/decoder/service_decoder.rs b/src/rust/src/decoder/service_decoder.rs index 599db619c..3fc3dcacf 100644 --- a/src/rust/src/decoder/service_decoder.rs +++ b/src/rust/src/decoder/service_decoder.rs @@ -115,7 +115,7 @@ impl dtvcc_service_decoder { let pd = match dtvcc_window_pd::new(window.attribs.print_direction) { Ok(val) => val, Err(e) => { - warn!("{}", e); + warn!("{e}"); return; } }; @@ -218,7 +218,7 @@ impl dtvcc_service_decoder { let pd = match dtvcc_window_pd::new(window.attribs.print_direction) { Ok(val) => val, Err(e) => { - warn!("{}", e); + warn!("{e}"); return; } }; @@ -283,7 +283,7 @@ impl dtvcc_service_decoder { return -1; } - debug!("C1: [{:?}] [{}] ({})", command, name, length); + debug!("C1: [{command:?}] [{name}] ({length})"); match command { C1CodeSet::CW0 | C1CodeSet::CW1 @@ -344,7 +344,7 @@ impl dtvcc_service_decoder { for i in 0..CCX_DTVCC_MAX_WINDOWS { if windows_bitmap & 1 == 1 { let window = &mut self.windows[i as usize]; - debug!("[W{}]", i); + debug!("[W{i}]"); let window_had_content = is_true(window.is_defined) && is_true(window.visible) && is_false(window.is_empty); @@ -381,7 +381,7 @@ impl dtvcc_service_decoder { for i in 0..CCX_DTVCC_MAX_WINDOWS { if windows_bitmap & 1 == 1 { let window = &mut self.windows[i as usize]; - debug!("[W{}]", i); + debug!("[W{i}]"); if is_true(window.visible) { screen_content_changed = true; window.visible = 0; @@ -418,11 +418,11 @@ impl dtvcc_service_decoder { let window = &mut self.windows[i as usize]; if windows_bitmap & 1 == 1 && is_true(window.is_defined) { if is_false(window.visible) { - debug!("[W-{}: 0->1]", i); + debug!("[W-{i}: 0->1]"); window.visible = 1; window.update_time_show(timing); } else { - debug!("[W-{}: 1->0]", i); + debug!("[W-{i}: 1->0]"); window.visible = 0; window.update_time_hide(timing); if is_false(window.is_empty) { @@ -456,7 +456,7 @@ impl dtvcc_service_decoder { } else { for i in 0..CCX_DTVCC_MAX_WINDOWS { if windows_bitmap & 1 == 1 { - debug!("Deleting [W{}]", i); + debug!("Deleting [W{i}]"); let window = &mut self.windows[i as usize]; let window_had_content = is_true(window.is_defined) && is_true(window.visible) @@ -501,9 +501,9 @@ impl dtvcc_service_decoder { for i in 0..CCX_DTVCC_MAX_WINDOWS { if windows_bitmap & 1 == 1 { let window = &mut self.windows[i as usize]; - debug!("[W{}]", i); + debug!("[W{i}]"); if is_false(window.is_defined) { - error!("Window {} was not defined", i); + error!("Window {i} was not defined"); continue; } if is_false(window.visible) { @@ -525,10 +525,7 @@ impl dtvcc_service_decoder { block: &[c_uchar], timing: &mut ccx_common_timing_ctx, ) { - debug!( - "dtvcc_handle_DFx_DefineWindow: W[{}], attributes:", - window_id - ); + debug!("dtvcc_handle_DFx_DefineWindow: W[{window_id}], attributes:"); let window = &mut self.windows[window_id as usize]; let block = &block[..=5]; let is_command_repeated = window @@ -562,12 +559,12 @@ impl dtvcc_service_decoder { let mut do_clear_window = false; debug!("Visible: [{}]", if is_true(visible) { "Yes" } else { "No" }); - debug!("Priority: [{}]", priority); - debug!("Row count: [{}]", row_count); - debug!("Column count: [{}]", col_count); - debug!("Anchor point: [{}]", anchor_point); - debug!("Anchor vertical: [{}]", anchor_vertical); - debug!("Anchor horizontal: [{}]", anchor_horizontal); + debug!("Priority: [{priority}]"); + debug!("Row count: [{row_count}]"); + debug!("Column count: [{col_count}]"); + debug!("Anchor point: [{anchor_point}]"); + debug!("Anchor vertical: [{anchor_vertical}]"); + debug!("Anchor horizontal: [{anchor_horizontal}]"); debug!( "Relative pos: [{}]", if is_true(relative_pos) { "Yes" } else { "No" } @@ -580,8 +577,8 @@ impl dtvcc_service_decoder { "Column lock: [{}]", if is_true(col_lock) { "Yes" } else { "No" } ); - debug!("Pen style: [{}]", pen_style); - debug!("Win style: [{}]", win_style); + debug!("Pen style: [{pen_style}]"); + debug!("Win style: [{win_style}]"); // Korean samples have "anchor_vertical" and "anchor_horizontal" mixed up, // this seems to be an encoder issue, but we can workaround it @@ -640,7 +637,7 @@ impl dtvcc_service_decoder { for i in 0..CCX_DTVCC_MAX_ROWS as usize { let layout = Layout::array::(CCX_DTVCC_MAX_COLUMNS as usize); if let Err(e) = layout { - error!("dtvcc_handle_DFx_DefineWindow: Incorrect Layout, {}", e); + error!("dtvcc_handle_DFx_DefineWindow: Incorrect Layout, {e}"); } else { let ptr = unsafe { alloc(layout.unwrap()) }; if ptr.is_null() { @@ -671,7 +668,7 @@ impl dtvcc_service_decoder { for i in 0..CCX_DTVCC_MAX_ROWS as usize { let layout = Layout::array::(CCX_DTVCC_MAX_COLUMNS as usize); if let Err(e) = layout { - error!("dtvcc_handle_DFx_DefineWindow: Incorrect Layout, {}", e); + error!("dtvcc_handle_DFx_DefineWindow: Incorrect Layout, {e}"); } else { unsafe { dealloc(window.rows[i] as *mut u8, layout.unwrap()) }; } @@ -699,13 +696,9 @@ impl dtvcc_service_decoder { let italic = (block[1] >> 7) & 0x1; debug!("dtvcc_handle_SPA_SetPenAttributes: attributes: "); debug!( - "Pen size: [{}] Offset: [{}] Text tag: [{}] Font tag: [{}]", - pen_size, offset, text_tag, font_tag - ); - debug!( - "Edge type: [{}] Underline: [{}] Italic: [{}]", - edge_type, underline, italic + "Pen size: [{pen_size}] Offset: [{offset}] Text tag: [{text_tag}] Font tag: [{font_tag}]" ); + debug!("Edge type: [{edge_type}] Underline: [{underline}] Italic: [{italic}]"); let window = &mut self.windows[self.current_window as usize]; if window.pen_row == -1 { @@ -738,15 +731,9 @@ impl dtvcc_service_decoder { let bg_opacity = (block[1] >> 6) & 0x03; let edge_color = (block[2]) & 0x3f; debug!("dtvcc_handle_SPC_SetPenColor: attributes: "); - debug!( - "Foreground color: [{}] Foreground opacity: [{}]", - fg_color, fg_opacity - ); - debug!( - "Background color: [{}] Background opacity: [{}]", - bg_color, bg_opacity - ); - debug!("Edge color: [{}]", edge_color); + debug!("Foreground color: [{fg_color}] Foreground opacity: [{fg_opacity}]"); + debug!("Background color: [{bg_color}] Background opacity: [{bg_opacity}]"); + debug!("Edge color: [{edge_color}]"); let window = &mut self.windows[self.current_window as usize]; if window.pen_row == -1 { @@ -774,7 +761,7 @@ impl dtvcc_service_decoder { debug!("dtvcc_handle_SPL_SetPenLocation: attributes: "); let row = block[0] & 0x0f; let col = block[1] & 0x3f; - debug!("Row: [{}] Column: [{}]", row, col); + debug!("Row: [{row}] Column: [{col}]"); let window = &mut self.windows[self.current_window as usize]; window.pen_row = row as i32; @@ -804,16 +791,13 @@ impl dtvcc_service_decoder { let effect_speed = (block[3] >> 4) & 0x0f; debug!("dtvcc_handle_SWA_SetWindowAttributes: attributes: "); debug!( - "Fill color: [{}] Fill opacity: [{}] Border color: [{}] Border type: [{}]", - fill_color, fill_opacity, border_color, border_type + "Fill color: [{fill_color}] Fill opacity: [{fill_opacity}] Border color: [{border_color}] Border type: [{border_type}]" ); debug!( - "Justify: [{}] Scroll dir: [{}] Print dir: [{}] Word wrap: [{}]", - justify, scroll_dir, print_dir, word_wrap + "Justify: [{justify}] Scroll dir: [{scroll_dir}] Print dir: [{print_dir}] Word wrap: [{word_wrap}]" ); debug!( - "Border type: [{}] Display eff: [{}] Effect dir: [{}] Effect speed: [{}]", - border_type, display_eff, effect_dir, effect_speed + "Border type: [{border_type}] Display eff: [{display_eff}] Effect dir: [{effect_dir}] Effect speed: [{effect_speed}]" ); let window_attribts = &mut self.windows[self.current_window as usize].attribs; @@ -834,23 +818,17 @@ impl dtvcc_service_decoder { /// /// Change current window to the window id provided pub fn handle_set_current_window(&mut self, window_id: u8) { - debug!("dtvcc_handle_CWx_SetCurrentWindow: [{}]", window_id); + debug!("dtvcc_handle_CWx_SetCurrentWindow: [{window_id}]"); if is_true(self.windows[window_id as usize].is_defined) { self.current_window = window_id as i32; } else { - debug!( - "dtvcc_handle_CWx_SetCurrentWindow: window [{}] is not defined", - window_id - ); + debug!("dtvcc_handle_CWx_SetCurrentWindow: window [{window_id}] is not defined"); } } /// DLY Delay pub fn handle_delay(&mut self, tenths_of_sec: u8) { - debug!( - "dtvcc_handle_DLY_Delay: dely for {} tenths of second", - tenths_of_sec - ); + debug!("dtvcc_handle_DLY_Delay: dely for {tenths_of_sec} tenths of second"); } /// DLC Delay Cancel @@ -910,7 +888,7 @@ impl dtvcc_service_decoder { let anchor = match dtvcc_pen_anchor_point::new(window.anchor_point) { Ok(val) => val, Err(e) => { - warn!("{}", e); + warn!("{e}"); return; } }; @@ -955,7 +933,7 @@ impl dtvcc_service_decoder { "For window {}: Anchor point -> {}, size {}:{}, real position {}:{}", window.number, window.anchor_point, window.row_count, window.col_count, top, left ); - debug!("we have top [{}] and left [{}]", top, left); + debug!("we have top [{top}] and left [{left}]"); if top < 0 { top = 0 } @@ -972,7 +950,7 @@ impl dtvcc_service_decoder { } else { window.col_count }; - debug!("{}*{} will be copied to the TV.", copy_rows, copy_cols); + debug!("{copy_rows}*{copy_cols} will be copied to the TV."); unsafe { let tv = &mut *self.tv; @@ -997,7 +975,7 @@ impl dtvcc_service_decoder { let (a_x1, a_x2, a_y1, a_y2) = match window.get_dimensions() { Ok(val) => val, Err(e) => { - warn!("{}", e); + warn!("{e}"); return false; } }; @@ -1006,7 +984,7 @@ impl dtvcc_service_decoder { let (b_x1, b_x2, b_y1, b_y2) = match win_compare.get_dimensions() { Ok(val) => val, Err(e) => { - warn!("{}", e); + warn!("{e}"); return false; } }; @@ -1156,7 +1134,7 @@ impl dtvcc_service_decoder { let pd = match dtvcc_window_pd::new(window.attribs.print_direction) { Ok(val) => val, Err(e) => { - warn!("{}", e); + warn!("{e}"); return; } }; diff --git a/src/rust/src/decoder/tv_screen.rs b/src/rust/src/decoder/tv_screen.rs index e514c6a32..9090960c0 100644 --- a/src/rust/src/decoder/tv_screen.rs +++ b/src/rust/src/decoder/tv_screen.rs @@ -35,7 +35,7 @@ impl dtvcc_tv_screen { pub fn update_time_show(&mut self, time: LLONG) { let prev_time_str = get_time_str(self.time_ms_show); let curr_time_str = get_time_str(time); - debug!("Screen show time: {} -> {}", prev_time_str, curr_time_str); + debug!("Screen show time: {prev_time_str} -> {curr_time_str}"); if self.time_ms_show == -1 || self.time_ms_show > time { self.time_ms_show = time; } @@ -45,7 +45,7 @@ impl dtvcc_tv_screen { pub fn update_time_hide(&mut self, time: LLONG) { let prev_time_str = get_time_str(self.time_ms_hide); let curr_time_str = get_time_str(time); - debug!("Screen hide time: {} -> {}", prev_time_str, curr_time_str); + debug!("Screen hide time: {prev_time_str} -> {curr_time_str}"); if self.time_ms_hide == -1 || self.time_ms_hide < time { self.time_ms_hide = time; } @@ -71,7 +71,7 @@ impl dtvcc_tv_screen { .to_str() .map_err(|err| err.to_string()) }?; - debug!("dtvcc_writer_output: creating {}", filename); + debug!("dtvcc_writer_output: creating {filename}"); let file = File::create(filename).map_err(|err| err.to_string())?; writer.writer_ctx.fd = file.into_raw_fd(); @@ -138,7 +138,7 @@ impl dtvcc_tv_screen { } }; if let Err(err) = result { - warn!("{}", err); + warn!("{err}"); } } @@ -155,7 +155,7 @@ impl dtvcc_tv_screen { let mut pen_color = dtvcc_pen_color::default(); let mut pen_attribs = dtvcc_pen_attribs::default(); let (first, last) = self.get_write_interval(row_index); - debug!("First: {}, Last: {}", first, last); + debug!("First: {first}, Last: {last}"); for i in 0..last + 1 { if use_colors { @@ -231,7 +231,7 @@ impl dtvcc_tv_screen { .to_str() .map_err(|err| err.to_string())? }; - debug!("Charset: {}", charset); + debug!("Charset: {charset}"); // Look up the encoding by label (name) if let Some(encoding) = Encoding::for_label(charset.as_bytes()) { @@ -486,7 +486,7 @@ impl dtvcc_tv_screen { add_needed_scc_labels(&mut buf, total_subtitle_count, current_subtitle_count); let (first, last) = self.get_write_interval(row_index); - debug!("First: {}, Last: {}", first, last); + debug!("First: {first}, Last: {last}"); let mut bytes_written = 0; for i in 0..last + 1 { @@ -520,7 +520,7 @@ impl dtvcc_tv_screen { pub fn write_debug(&self) { let time_show = get_time_str(self.time_ms_show); let time_hide = get_time_str(self.time_ms_hide); - debug!("{} --> {}", time_show, time_hide); + debug!("{time_show} --> {time_hide}"); for row_index in 0..CCX_DTVCC_SCREENGRID_ROWS as usize { if !self.is_row_empty(row_index) { @@ -529,7 +529,7 @@ impl dtvcc_tv_screen { for sym in self.chars[row_index][first..=last].iter() { buf.push_str(&format!("{:04X},", sym.sym)); } - debug!("{}", buf); + debug!("{buf}"); } } } @@ -620,7 +620,7 @@ impl dtvcc_tv_screen { // should close older non-white color buf.extend_from_slice(b""); } else if new_pen_color.fg_color != 0x3F && open { - debug!("Colors: {}", col_index); + debug!("Colors: {col_index}"); let (mut red, mut green, mut blue) = color_to_hex(new_pen_color.fg_color as u8); red *= 255 / 3; green *= 255 / 3; diff --git a/src/rust/src/decoder/window.rs b/src/rust/src/decoder/window.rs index c051aaf43..3f2d3f398 100644 --- a/src/rust/src/decoder/window.rs +++ b/src/rust/src/decoder/window.rs @@ -163,7 +163,7 @@ impl dtvcc_window { unsafe { let layout = Layout::array::(CCX_DTVCC_MAX_COLUMNS as usize); if let Err(e) = layout { - error!("clear_row: Incorrect Layout, {}", e); + error!("clear_row: Incorrect Layout, {e}"); } else { let layout = layout.unwrap(); // deallocate previous memory diff --git a/src/rust/src/libccxr_exports/bitstream.rs b/src/rust/src/libccxr_exports/bitstream.rs index 41fb70d52..9e6392a60 100644 --- a/src/rust/src/libccxr_exports/bitstream.rs +++ b/src/rust/src/libccxr_exports/bitstream.rs @@ -307,7 +307,7 @@ mod tests { // FFI binding tests #[test] fn test_ffi_next_bits() { - let data = vec![0b10101010]; + let data = [0b10101010]; let mut c_bs = crate::bindings::bitstream { pos: data.as_ptr() as *mut u8, bpos: 8, @@ -323,7 +323,7 @@ mod tests { #[test] fn test_ffi_read_bits() { - let data = vec![0b10101010]; + let data = [0b10101010]; let mut c_bs = crate::bindings::bitstream { pos: data.as_ptr() as *mut u8, bpos: 8, @@ -339,7 +339,7 @@ mod tests { #[test] fn test_ffi_byte_alignment() { - let data = vec![0xFF]; + let data = [0xFF]; let mut c_bs = crate::bindings::bitstream { pos: data.as_ptr() as *mut u8, bpos: 8, @@ -411,7 +411,7 @@ mod tests { #[test] fn test_ffi_state_updates() { - let data = vec![0xAA, 0xBB]; + let data = [0xAA, 0xBB]; let mut c_bs = crate::bindings::bitstream { pos: data.as_ptr() as *mut u8, bpos: 8, @@ -465,7 +465,7 @@ mod bitstream_copying_tests { assert_eq!(c_stream._i_bpos, 5); // Verify pointer arithmetic - assert!(verify_pointer_bounds(&c_stream)); + assert!(verify_pointer_bounds(c_stream)); assert_eq!(c_stream.end.offset_from(c_stream.pos), 100); assert_eq!(c_stream._i_pos.offset_from(c_stream.pos), 10); @@ -496,7 +496,7 @@ mod bitstream_copying_tests { // Verify basic field conversions assert_eq!(rust_stream.bpos, 7); assert_eq!(rust_stream.bits_left, 400); - assert_eq!(rust_stream.error, true); + assert!(rust_stream.error); assert_eq!(rust_stream._i_pos, 15); assert_eq!(rust_stream._i_bpos, 2); @@ -635,7 +635,7 @@ mod bitstream_copying_tests { assert_eq!(reconstructed.bpos, 7); assert_eq!(reconstructed.bits_left, i64::MAX); - assert_eq!(reconstructed.error, true); + assert!(reconstructed.error); assert_eq!(reconstructed._i_pos, 255); assert_eq!(reconstructed._i_bpos, 7); } @@ -660,7 +660,7 @@ mod bitstream_copying_tests { let reconstructed = copy_bitstream_c_to_rust(c_s); assert_eq!(reconstructed.bits_left, -100); - assert_eq!(reconstructed.error, false); + assert!(!reconstructed.error); } } @@ -778,7 +778,7 @@ mod bitstream_copying_tests { copy_bitstream_from_rust_to_c(c_s, &rust_stream); let new_rust_stream = copy_bitstream_c_to_rust(c_s); - assert_eq!(new_rust_stream.error, true); + assert!(new_rust_stream.error); assert_eq!(new_rust_stream.data.len(), 64); assert_eq!(new_rust_stream._i_pos, 32); @@ -836,7 +836,7 @@ mod bitstream_copying_tests { let c_stream = &mut *c_s; // Verify all pointers are within bounds - assert!(verify_pointer_bounds(&c_stream)); + assert!(verify_pointer_bounds(c_stream)); // Verify we can safely access the boundaries let first_byte = *c_stream.pos; diff --git a/src/rust/src/libccxr_exports/mod.rs b/src/rust/src/libccxr_exports/mod.rs index ca65bb36f..4161949fe 100644 --- a/src/rust/src/libccxr_exports/mod.rs +++ b/src/rust/src/libccxr_exports/mod.rs @@ -1,6 +1,7 @@ //! Provides C-FFI functions that are direct equivalent of functions available in C. pub mod bitstream; +pub mod net; pub mod time; use crate::ccx_options; diff --git a/src/rust/src/libccxr_exports/net.rs b/src/rust/src/libccxr_exports/net.rs new file mode 100644 index 000000000..99c72e04f --- /dev/null +++ b/src/rust/src/libccxr_exports/net.rs @@ -0,0 +1,285 @@ +use crate::bindings::*; + +use lib_ccxr::net::c_functions::*; +use std::convert::TryInto; +use std::ffi::CStr; +use std::os::raw::{c_char, c_int, c_uchar, c_uint, c_void}; + +/// Rust equivalent for `connect_to_srv` function in C. Uses C-native types as input and output. +/// +/// # Safety +/// +/// `addr` must not be null. All the strings must end with a nul character. +#[no_mangle] +pub unsafe extern "C" fn ccxr_connect_to_srv( + addr: *const c_char, + port: *const c_char, + cc_desc: *const c_char, + pwd: *const c_char, +) { + let addr = CStr::from_ptr(addr) + .to_str() + .expect("Unable to convert Cstr to &str"); + + let port = if !port.is_null() { + Some( + CStr::from_ptr(port) + .to_str() + .expect("Unable to convert Cstr to &str") + .parse() + .expect("Unable to parse into u16"), + ) + } else { + None + }; + + let cc_desc = if !cc_desc.is_null() { + Some( + CStr::from_ptr(cc_desc) + .to_str() + .expect("Unable to convert Cstr to &str"), + ) + } else { + None + }; + + let pwd = if !pwd.is_null() { + Some( + CStr::from_ptr(pwd) + .to_str() + .expect("Unable to convert Cstr to &str"), + ) + } else { + None + }; + + connect_to_srv(addr, port, cc_desc, pwd); +} + +/// Rust equivalent for `net_send_header` function in C. Uses C-native types as input and output. +/// +/// # Safety +/// +/// `data` must not be null and should have a length of `len`. +/// [`ccxr_connect_to_srv`] or `connect_to_srv` must have been called before this function. +#[no_mangle] +pub unsafe extern "C" fn ccxr_net_send_header(data: *const c_uchar, len: usize) { + let buffer = std::slice::from_raw_parts(data, len); + net_send_header_rust(buffer); +} + +/// Rust equivalent for `net_send_cc` function in C. Uses C-native types as input and output. +/// +/// # Safety +/// +/// `data` must not be null and should have a length of `len`. +/// [`ccxr_connect_to_srv`] or `connect_to_srv` must have been called before this function. +#[no_mangle] +pub unsafe extern "C" fn ccxr_net_send_cc( + data: *const c_uchar, + len: usize, + _private_data: *const c_void, + _sub: *const cc_subtitle, +) -> c_int { + let buffer = std::slice::from_raw_parts(data, len); + if net_send_cc(buffer) { + 1 + } else { + -1 + } +} + +/// Rust equivalent for `net_check_conn` function in C. Uses C-native types as input and output. +/// +/// # Safety +/// +/// [`ccxr_connect_to_srv`] or `connect_to_srv` must have been called before this function. +#[no_mangle] +pub unsafe extern "C" fn ccxr_net_check_conn() { + net_check_conn() +} + +/// Rust equivalent for `net_send_epg` function in C. Uses C-native types as input and output. +/// +/// # Safety +/// +/// `start` and `stop` must not be null. All the strings must end with a nul character. +/// [`ccxr_connect_to_srv`] or `connect_to_srv` must have been called before this function. +#[no_mangle] +pub unsafe extern "C" fn ccxr_net_send_epg( + start: *const c_char, + stop: *const c_char, + title: *const c_char, + desc: *const c_char, + lang: *const c_char, + category: *const c_char, +) { + let start = CStr::from_ptr(start) + .to_str() + .expect("Unable to convert Cstr to &str"); + let stop = CStr::from_ptr(stop) + .to_str() + .expect("Unable to convert Cstr to &str"); + + let title = if !title.is_null() { + Some( + CStr::from_ptr(title) + .to_str() + .expect("Unable to convert Cstr to &str"), + ) + } else { + None + }; + + let desc = if !desc.is_null() { + Some( + CStr::from_ptr(desc) + .to_str() + .expect("Unable to convert Cstr to &str"), + ) + } else { + None + }; + + let lang = if !lang.is_null() { + Some( + CStr::from_ptr(lang) + .to_str() + .expect("Unable to convert Cstr to &str"), + ) + } else { + None + }; + + let category = if !category.is_null() { + Some( + CStr::from_ptr(category) + .to_str() + .expect("Unable to convert Cstr to &str"), + ) + } else { + None + }; + + net_send_epg(start, stop, title, desc, lang, category) +} + +/// Rust equivalent for `net_tcp_read` function in C. Uses C-native types as input and output. +/// +/// # Safety +/// +/// `buffer` should not be null. it should be of size `length`. +/// [`ccxr_start_tcp_srv`] or `start_tcp_srv` must have been called before this function. +#[no_mangle] +pub unsafe extern "C" fn ccxr_net_tcp_read( + _socket: c_int, + buffer: *mut c_void, + length: usize, +) -> c_int { + let buffer = std::slice::from_raw_parts_mut(buffer as *mut u8, length); + let ans = net_tcp_read(buffer); + match ans { + Some(x) => x.try_into().unwrap(), + None => -1, + } +} + +/// Rust equivalent for `net_udp_read` function in C. Uses C-native types as input and output. +/// +/// # Safety +/// +/// `buffer` should not be null. it should be of size `length`. +/// [`ccxr_start_udp_srv`] or `start_udp_srv` must have been called before this function. +#[no_mangle] +pub unsafe extern "C" fn ccxr_net_udp_read( + _socket: c_int, + buffer: *mut c_void, + length: usize, + _src_str: *const c_char, + _addr_str: *const c_char, +) -> c_int { + let buffer = std::slice::from_raw_parts_mut(buffer as *mut u8, length); + let ans = net_udp_read(buffer); + match ans { + Some(x) => x.try_into().unwrap(), + None => -1, + } +} + +/// Rust equivalent for `start_tcp_srv` function in C. Uses C-native types as input and output. +/// +/// Note that this function always returns 1 as an fd, since it will not be used anyway. +/// +/// # Safety +/// +/// `port` should be a numerical 16-bit value. All the strings must end with a nul character. +/// The output file desciptor should not be used. +#[no_mangle] +pub unsafe extern "C" fn ccxr_start_tcp_srv(port: *const c_char, pwd: *const c_char) -> c_int { + let port = if !port.is_null() { + Some( + CStr::from_ptr(port) + .to_str() + .expect("Unable to convert Cstr to &str") + .parse() + .expect("Unable to parse into u16"), + ) + } else { + None + }; + + let pwd = if !pwd.is_null() { + Some( + CStr::from_ptr(pwd) + .to_str() + .expect("Unable to convert Cstr to &str"), + ) + } else { + None + }; + + start_tcp_srv(port, pwd); + + 1 +} + +/// Rust equivalent for `start_udp_srv` function in C. Uses C-native types as input and output. +/// +/// Note that this function always returns 1 as an fd, since it will not be used anyway. +/// +/// # Safety +/// +/// `port` should be a 16-bit value. All the strings must end with a nul character. +/// The output file desciptor should not be used. +#[no_mangle] +pub unsafe extern "C" fn ccxr_start_udp_srv( + src: *const c_char, + addr: *const c_char, + port: c_uint, +) -> c_int { + let src = if !src.is_null() { + Some( + CStr::from_ptr(src) + .to_str() + .expect("Unable to convert Cstr to &str"), + ) + } else { + None + }; + + let addr = if !addr.is_null() { + Some( + CStr::from_ptr(addr) + .to_str() + .expect("Unable to convert Cstr to &str"), + ) + } else { + None + }; + + let port = port.try_into().unwrap(); + + start_udp_srv(src, addr, port); + + 1 +} diff --git a/src/rust/src/parser.rs b/src/rust/src/parser.rs index 98b3e56a2..f6a38c1d7 100644 --- a/src/rust/src/parser.rs +++ b/src/rust/src/parser.rs @@ -548,13 +548,6 @@ impl OptionsExt for Options { } } - if self.inputfile.is_none() { - fatal!( - cause = ExitCause::NoInputFiles; - "No input file specified\n" - ); - } - #[cfg(feature = "hardsubx_ocr")] { use lib_ccxr::hardsubx::*; @@ -1365,36 +1358,34 @@ impl OptionsExt for Options { self.xmltv = 2; self.xmltvliveinterval = Timestamp::from_millis(2000); - let mut _addr: String = addr.to_string(); + let _addr: String = addr.to_string(); + // Handle IPv6 addresses in [addr]:port format if let Some(saddr) = addr.strip_prefix('[') { - _addr = saddr.to_string(); - - let result = _addr.find(']'); - if result.is_none() { + if let Some(end_bracket) = saddr.find(']') { + let addr_part = &saddr[..end_bracket]; + let port_part = &saddr[end_bracket + 1..]; + self.srv_addr = Some(addr_part.to_string()); + if let Some(port) = port_part.strip_prefix(':') { + self.srv_port = Some(port.parse().unwrap()); + } + } else { fatal!( cause = ExitCause::IncompatibleParameters; - "Wrong address format, for IPv6 use [address]:port\n" + "Wrong address format, for IPv6 use [address]:port\n" ); } - let mut br = result.unwrap(); - _addr = _addr.replace(']', ""); - + } else if let Some(colon) = _addr.rfind(':') { + // Handle IPv4 or hostname:port + let (host, port) = _addr.split_at(colon); + self.srv_addr = Some(host.to_string()); + self.srv_port = Some(port[1..].parse().unwrap()); + } else { + // No port specified, treat as address only self.srv_addr = Some(_addr.clone()); - - br += 1; - if !_addr[br..].is_empty() { - self.srv_port = Some(_addr[br..].parse().unwrap()); - } + self.srv_port = None; } - - self.srv_addr = Some(_addr.clone()); - - let colon = _addr.find(':').unwrap(); - _addr = _addr.replace(':', ""); - self.srv_port = Some(_addr[(colon + 1)..].parse().unwrap()); } - if let Some(ref tcp) = args.tcp { self.tcpport = Some(*tcp); self.input_source = DataSource::Tcp; @@ -1830,7 +1821,7 @@ pub mod tests { assert!(options.cc_to_stdout); assert_eq!(options.messages_target, OutputTarget::Quiet); - assert_eq!(options.nofontcolor, true); + assert!(options.nofontcolor); } #[test]