diff --git a/Cargo.lock b/Cargo.lock index f5e5a29..409464d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -173,14 +173,15 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" dependencies = [ "async-task", "concurrent-queue", "fastrand", "futures-lite", + "pin-project-lite", "slab", ] @@ -300,7 +301,7 @@ dependencies = [ "anyhow", "arrayvec", "log", - "nom", + "nom 7.1.3", "num-rational", "v_frame", ] @@ -398,9 +399,9 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytemuck" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" dependencies = [ "bytemuck_derive", ] @@ -505,14 +506,15 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", "windows-link", ] @@ -1153,6 +1155,15 @@ dependencies = [ "syn", ] +[[package]] +name = "isolang" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe50d48c77760c55188549098b9a7f6e37ae980c586a24693d6b01c3b2010c3c" +dependencies = [ + "phf", +] + [[package]] name = "itertools" version = "0.12.1" @@ -1162,6 +1173,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -1495,6 +1515,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + [[package]] name = "noop_proc_macro" version = "0.3.0" @@ -1951,6 +1980,24 @@ dependencies = [ "serde", ] +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.10" @@ -2107,9 +2154,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.37.4" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ce8c88de324ff838700f36fb6ab86c96df0e3c4ab6ef3a9b2044465cce1369" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ "memchr", ] @@ -2179,7 +2226,7 @@ dependencies = [ "built", "cfg-if", "interpolate_name", - "itertools", + "itertools 0.12.1", "libc", "libfuzzer-sys", "log", @@ -2476,6 +2523,12 @@ dependencies = [ "quote", ] +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" version = "0.4.9" @@ -2630,6 +2683,7 @@ dependencies = [ "memchr", "ntapi", "objc2-core-foundation", + "serde", "windows 0.57.0", ] @@ -2656,6 +2710,7 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" name = "telemetry" version = "0.1.0" dependencies = [ + "anyhow", "ash", "chrono", "detect-desktop-environment", @@ -2666,6 +2721,8 @@ dependencies = [ "serde_json", "sys-locale", "sysinfo", + "timeago", + "versions", "wgpu", "winit", ] @@ -2746,6 +2803,16 @@ dependencies = [ "weezl", ] +[[package]] +name = "timeago" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1710e589de0a76aaf295cd47a6699f6405737dbfd3cf2b75c92d000b548d0e6" +dependencies = [ + "chrono", + "isolang", +] + [[package]] name = "tiny-skia" version = "0.11.4" @@ -2782,9 +2849,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900f6c86a685850b1bc9f6223b20125115ee3f31e01207d81655bbcc0aea9231" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", @@ -2803,9 +2870,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.25" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10558ed0bd2a1562e630926a2d1f0b98c827da99fabd3fe20920a59642504485" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", "serde", @@ -2928,6 +2995,17 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "versions" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80a7e511ce1795821207a837b7b1c8d8aca0c648810966ad200446ae58f6667f" +dependencies = [ + "itertools 0.14.0", + "nom 8.0.0", + "serde", +] + [[package]] name = "vk-parse" version = "0.15.0" @@ -2951,7 +3029,7 @@ dependencies = [ "heck 0.4.1", "indexmap", "libloading", - "nom", + "nom 7.1.3", "once_cell", "parking_lot", "proc-macro2", @@ -3079,9 +3157,9 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf" +checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" dependencies = [ "cc", "downcast-rs", @@ -3093,9 +3171,9 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.8" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f" +checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" dependencies = [ "bitflags 2.9.0", "rustix", @@ -3116,9 +3194,9 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.31.8" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93029cbb6650748881a00e4922b076092a6a08c11e7fbdb923f064b23968c5d" +checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182" dependencies = [ "rustix", "wayland-client", @@ -3127,9 +3205,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.6" +version = "0.32.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc" +checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" dependencies = [ "bitflags 2.9.0", "wayland-backend", @@ -3139,9 +3217,9 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ccaacc76703fefd6763022ac565b590fcade92202492381c95b2edfdf7d46b3" +checksum = "4fd38cdad69b56ace413c6bcc1fbf5acc5e2ef4af9d5f8f1f9570c0c83eae175" dependencies = [ "bitflags 2.9.0", "wayland-backend", @@ -3152,9 +3230,9 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248a02e6f595aad796561fa82d25601bd2c8c3b145b1c7453fc8f94c1a58f8b2" +checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf" dependencies = [ "bitflags 2.9.0", "wayland-backend", diff --git a/subcrates/telemetry/Cargo.toml b/subcrates/telemetry/Cargo.toml index dfb77fe..81ffb9b 100644 --- a/subcrates/telemetry/Cargo.toml +++ b/subcrates/telemetry/Cargo.toml @@ -4,8 +4,9 @@ version = "0.1.0" edition = "2024" [dependencies] +anyhow = "1.0.98" ash = "0.38.0" -chrono = "0.4.40" +chrono = { version = "0.4.40", features = ["serde"] } detect-desktop-environment = "1.2.0" hidapi = "2.6.3" humansize = "2.1.3" @@ -13,7 +14,9 @@ metal = { version = "0.32.0", optional = true } serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.140" sys-locale = "0.3.2" -sysinfo = "0.34.2" +sysinfo = { version = "0.34.2", features = ["serde"] } +timeago = "0.4.2" +versions = { version = "7.0.0", features = ["serde"] } wgpu = "25.0.0" winit = "0.30.9" diff --git a/subcrates/telemetry/build.rs b/subcrates/telemetry/build.rs index fc44b31..d012c62 100644 --- a/subcrates/telemetry/build.rs +++ b/subcrates/telemetry/build.rs @@ -11,23 +11,11 @@ fn main() { .and_then(|o| String::from_utf8(o.stdout).ok()) .unwrap_or_else(|| "unknown".into()); - let commit_short_hash = Command::new("git") - .args(["rev-parse", "--short", "HEAD"]) - .output() - .ok() - .and_then(|o| String::from_utf8(o.stdout).ok()) - .unwrap_or_else(|| "unknown".into()); - + writeln!(f, "#[allow(dead_code)]").unwrap(); writeln!( f, "pub const GIT_COMMIT_HASH: &str = \"{}\";", commit_hash.trim() ) .unwrap(); - writeln!( - f, - "pub const GIT_COMMIT_SHORT_HASH: &str = \"{}\";", - commit_short_hash.trim() - ) - .unwrap(); } diff --git a/subcrates/telemetry/src/languages.json b/subcrates/telemetry/src/languages.json deleted file mode 100644 index 6c9fc28..0000000 --- a/subcrates/telemetry/src/languages.json +++ /dev/null @@ -1,684 +0,0 @@ -{ - "aa": "Afar", - "ab": "Abkhazian", - "ace": "Acehnese", - "ach": "Acoli", - "ada": "Adangme", - "ady": "Adyghe", - "ae": "Avestan", - "aeb": "Tunisian Arabic", - "af": "Afrikaans", - "afh": "Afrihili", - "agq": "Aghem", - "ain": "Ainu", - "ak": "Akan", - "akk": "Akkadian", - "akz": "Alabama", - "ale": "Aleut", - "aln": "Gheg Albanian", - "alt": "Southern Altai", - "am": "Amharic", - "an": "Aragonese", - "ang": "Old English", - "ann": "Obolo", - "anp": "Angika", - "ar": "Arabic", - "ar-001": "Modern Standard Arabic", - "arc": "Aramaic", - "arn": "Mapuche", - "aro": "Araona", - "arp": "Arapaho", - "arq": "Algerian Arabic", - "ars": "Najdi Arabic", - "ars-alt-menu": "Arabic, Najdi", - "arw": "Arawak", - "ary": "Moroccan Arabic", - "arz": "Egyptian Arabic", - "as": "Assamese", - "asa": "Asu", - "ase": "American Sign Language", - "ast": "Asturian", - "atj": "Atikamekw", - "av": "Avaric", - "avk": "Kotava", - "awa": "Awadhi", - "ay": "Aymara", - "az": "Azerbaijani", - "az-alt-short": "Azeri", - "ba": "Bashkir", - "bal": "Baluchi", - "ban": "Balinese", - "bar": "Bavarian", - "bas": "Basaa", - "bax": "Bamun", - "bbc": "Batak Toba", - "bbj": "Ghomala", - "be": "Belarusian", - "bej": "Beja", - "bem": "Bemba", - "bew": "Betawi", - "bez": "Bena", - "bfd": "Bafut", - "bfq": "Badaga", - "bg": "Bulgarian", - "bgc": "Haryanvi", - "bgn": "Western Balochi", - "bho": "Bhojpuri", - "bi": "Bislama", - "bik": "Bikol", - "bin": "Bini", - "bjn": "Banjar", - "bkm": "Kom", - "bla": "Siksika", - "blo": "Anii", - "blt": "Tai Dam", - "bm": "Bambara", - "bn": "Bangla", - "bo": "Tibetan", - "bpy": "Bishnupriya", - "bqi": "Bakhtiari", - "br": "Breton", - "bra": "Braj", - "brh": "Brahui", - "brx": "Bodo", - "bs": "Bosnian", - "bss": "Akoose", - "bua": "Buriat", - "bug": "Buginese", - "bum": "Bulu", - "byn": "Blin", - "byv": "Medumba", - "ca": "Catalan", - "cad": "Caddo", - "car": "Carib", - "cay": "Cayuga", - "cch": "Atsam", - "ccp": "Chakma", - "ce": "Chechen", - "ceb": "Cebuano", - "cgg": "Chiga", - "ch": "Chamorro", - "chb": "Chibcha", - "chg": "Chagatai", - "chk": "Chuukese", - "chm": "Mari", - "chn": "Chinook Jargon", - "cho": "Choctaw", - "chp": "Chipewyan", - "chr": "Cherokee", - "chy": "Cheyenne", - "cic": "Chickasaw", - "ckb": "Central Kurdish", - "ckb-alt-menu": "Kurdish, Central", - "ckb-alt-variant": "Kurdish, Sorani", - "clc": "Chilcotin", - "co": "Corsican", - "cop": "Coptic", - "cps": "Capiznon", - "cr": "Cree", - "cr-alt-long": "Woods Cree", - "crg": "Michif", - "crh": "Crimean Tatar", - "crj": "Southern East Cree", - "crk": "Plains Cree", - "crl": "Northern East Cree", - "crm": "Moose Cree", - "crr": "Carolina Algonquian", - "crs": "Seselwa Creole French", - "cs": "Czech", - "csb": "Kashubian", - "csw": "Swampy Cree", - "cu": "Church Slavic", - "cv": "Chuvash", - "cy": "Welsh", - "da": "Danish", - "dak": "Dakota", - "dar": "Dargwa", - "dav": "Taita", - "de": "German", - "de-AT": "Austrian German", - "de-CH": "Swiss High German", - "del": "Delaware", - "den": "Slave", - "dgr": "Dogrib", - "din": "Dinka", - "dje": "Zarma", - "doi": "Dogri", - "dsb": "Lower Sorbian", - "dtp": "Central Dusun", - "dua": "Duala", - "dum": "Middle Dutch", - "dv": "Divehi", - "dyo": "Jola-Fonyi", - "dyu": "Dyula", - "dz": "Dzongkha", - "dzg": "Dazaga", - "ebu": "Embu", - "ee": "Ewe", - "efi": "Efik", - "egl": "Emilian", - "egy": "Ancient Egyptian", - "eka": "Ekajuk", - "el": "Greek", - "elx": "Elamite", - "en": "English", - "en-AU": "Australian English", - "en-CA": "Canadian English", - "en-GB": "British English", - "en-GB-alt-short": "UK English", - "en-US": "American English", - "en-US-alt-short": "US English", - "enm": "Middle English", - "eo": "Esperanto", - "es": "Spanish", - "es-419": "Latin American Spanish", - "es-ES": "European Spanish", - "es-MX": "Mexican Spanish", - "esu": "Central Yupik", - "et": "Estonian", - "eu": "Basque", - "ewo": "Ewondo", - "ext": "Extremaduran", - "fa": "Persian", - "fa-AF": "Dari", - "fan": "Fang", - "fat": "Fanti", - "ff": "Fula", - "fi": "Finnish", - "fil": "Filipino", - "fit": "Tornedalen Finnish", - "fj": "Fijian", - "fo": "Faroese", - "fon": "Fon", - "fr": "French", - "fr-CA": "Canadian French", - "fr-CH": "Swiss French", - "frc": "Cajun French", - "frm": "Middle French", - "fro": "Old French", - "frp": "Arpitan", - "frr": "Northern Frisian", - "frs": "Eastern Frisian", - "fur": "Friulian", - "fy": "Western Frisian", - "ga": "Irish", - "gaa": "Ga", - "gag": "Gagauz", - "gan": "Gan Chinese", - "gay": "Gayo", - "gba": "Gbaya", - "gbz": "Zoroastrian Dari", - "gd": "Scottish Gaelic", - "gez": "Geez", - "gil": "Gilbertese", - "gl": "Galician", - "glk": "Gilaki", - "gmh": "Middle High German", - "gn": "Guarani", - "goh": "Old High German", - "gon": "Gondi", - "gor": "Gorontalo", - "got": "Gothic", - "grb": "Grebo", - "grc": "Ancient Greek", - "gsw": "Swiss German", - "gu": "Gujarati", - "guc": "Wayuu", - "gur": "Frafra", - "guz": "Gusii", - "gv": "Manx", - "gwi": "Gwichʼin", - "ha": "Hausa", - "hai": "Haida", - "hak": "Hakka Chinese", - "haw": "Hawaiian", - "hax": "Southern Haida", - "he": "Hebrew", - "hi": "Hindi", - "hi-Latn": "Hindi (Latin)", - "hi-Latn-alt-variant": "Hinglish", - "hif": "Fiji Hindi", - "hil": "Hiligaynon", - "hit": "Hittite", - "hmn": "Hmong", - "hnj": "Hmong Njua", - "ho": "Hiri Motu", - "hr": "Croatian", - "hsb": "Upper Sorbian", - "hsn": "Xiang Chinese", - "ht": "Haitian Creole", - "hu": "Hungarian", - "hup": "Hupa", - "hur": "Halkomelem", - "hy": "Armenian", - "hz": "Herero", - "ia": "Interlingua", - "iba": "Iban", - "ibb": "Ibibio", - "id": "Indonesian", - "ie": "Interlingue", - "ig": "Igbo", - "ii": "Sichuan Yi", - "ik": "Inupiaq", - "ikt": "Western Canadian Inuktitut", - "ilo": "Iloko", - "inh": "Ingush", - "io": "Ido", - "is": "Icelandic", - "it": "Italian", - "iu": "Inuktitut", - "izh": "Ingrian", - "ja": "Japanese", - "jam": "Jamaican Creole English", - "jbo": "Lojban", - "jgo": "Ngomba", - "jmc": "Machame", - "jpr": "Judeo-Persian", - "jrb": "Judeo-Arabic", - "jut": "Jutish", - "jv": "Javanese", - "ka": "Georgian", - "kaa": "Kara-Kalpak", - "kab": "Kabyle", - "kac": "Kachin", - "kaj": "Jju", - "kam": "Kamba", - "kaw": "Kawi", - "kbd": "Kabardian", - "kbl": "Kanembu", - "kcg": "Tyap", - "kde": "Makonde", - "kea": "Kabuverdianu", - "ken": "Kenyang", - "kfo": "Koro", - "kg": "Kongo", - "kgp": "Kaingang", - "kha": "Khasi", - "kho": "Khotanese", - "khq": "Koyra Chiini", - "khw": "Khowar", - "ki": "Kikuyu", - "kiu": "Kirmanjki", - "kj": "Kuanyama", - "kk": "Kazakh", - "kkj": "Kako", - "kl": "Kalaallisut", - "kln": "Kalenjin", - "km": "Khmer", - "kmb": "Kimbundu", - "kn": "Kannada", - "ko": "Korean", - "koi": "Komi-Permyak", - "kok": "Konkani", - "kos": "Kosraean", - "kpe": "Kpelle", - "kr": "Kanuri", - "krc": "Karachay-Balkar", - "kri": "Krio", - "krj": "Kinaray-a", - "krl": "Karelian", - "kru": "Kurukh", - "ks": "Kashmiri", - "ksb": "Shambala", - "ksf": "Bafia", - "ksh": "Colognian", - "ku": "Kurdish", - "kum": "Kumyk", - "kut": "Kutenai", - "kv": "Komi", - "kw": "Cornish", - "kwk": "Kwakʼwala", - "kxv": "Kuvi", - "ky": "Kyrgyz", - "ky-alt-variant": "Kirghiz", - "la": "Latin", - "lad": "Ladino", - "lag": "Langi", - "lah": "Western Panjabi", - "lam": "Lamba", - "lb": "Luxembourgish", - "lez": "Lezghian", - "lfn": "Lingua Franca Nova", - "lg": "Ganda", - "li": "Limburgish", - "lij": "Ligurian", - "lil": "Lillooet", - "liv": "Livonian", - "lkt": "Lakota", - "lmo": "Lombard", - "ln": "Lingala", - "lo": "Lao", - "lol": "Mongo", - "lou": "Louisiana Creole", - "loz": "Lozi", - "lrc": "Northern Luri", - "lsm": "Saamia", - "lt": "Lithuanian", - "ltg": "Latgalian", - "lu": "Luba-Katanga", - "lua": "Luba-Lulua", - "lui": "Luiseno", - "lun": "Lunda", - "luo": "Luo", - "lus": "Mizo", - "luy": "Luyia", - "lv": "Latvian", - "lzh": "Literary Chinese", - "lzz": "Laz", - "mad": "Madurese", - "maf": "Mafa", - "mag": "Magahi", - "mai": "Maithili", - "mak": "Makasar", - "man": "Mandingo", - "mas": "Masai", - "mde": "Maba", - "mdf": "Moksha", - "mdr": "Mandar", - "men": "Mende", - "mer": "Meru", - "mfe": "Morisyen", - "mg": "Malagasy", - "mga": "Middle Irish", - "mgh": "Makhuwa-Meetto", - "mgo": "Metaʼ", - "mh": "Marshallese", - "mi": "Māori", - "mic": "Mi'kmaw", - "min": "Minangkabau", - "mk": "Macedonian", - "ml": "Malayalam", - "mn": "Mongolian", - "mnc": "Manchu", - "mni": "Manipuri", - "moe": "Innu-aimun", - "moh": "Mohawk", - "mos": "Mossi", - "mr": "Marathi", - "mrj": "Western Mari", - "ms": "Malay", - "mt": "Maltese", - "mua": "Mundang", - "mul": "Multiple languages", - "mus": "Creek", - "mus-alt-official": "Mvskoke", - "mwl": "Mirandese", - "mwr": "Marwari", - "mwv": "Mentawai", - "my": "Burmese", - "my-alt-variant": "Myanmar Language", - "mye": "Myene", - "myv": "Erzya", - "mzn": "Mazanderani", - "na": "Nauru", - "nan": "Min Nan Chinese", - "nap": "Neapolitan", - "naq": "Nama", - "nb": "Norwegian Bokmål", - "nd": "North Ndebele", - "nds": "Low German", - "nds-NL": "West Low German", - "ne": "Nepali", - "new": "Newari", - "ng": "Ndonga", - "nia": "Nias", - "niu": "Niuean", - "njo": "Ao Naga", - "nl": "Dutch", - "nl-BE": "Flemish", - "nmg": "Kwasio", - "nn": "Norwegian Nynorsk", - "nnh": "Ngiemboon", - "no": "Norwegian", - "nog": "Nogai", - "non": "Old Norse", - "nov": "Novial", - "nqo": "N’Ko", - "nr": "South Ndebele", - "nso": "Northern Sotho", - "nus": "Nuer", - "nv": "Navajo", - "nwc": "Classical Newari", - "ny": "Nyanja", - "nym": "Nyamwezi", - "nyn": "Nyankole", - "nyo": "Nyoro", - "nzi": "Nzima", - "oc": "Occitan", - "oj": "Ojibwa", - "ojb": "Northwestern Ojibwa", - "ojc": "Central Ojibwa", - "ojs": "Oji-Cree", - "ojw": "Western Ojibwa", - "oka": "Okanagan", - "om": "Oromo", - "or": "Odia", - "os": "Ossetic", - "osa": "Osage", - "ota": "Ottoman Turkish", - "pa": "Punjabi", - "pag": "Pangasinan", - "pal": "Pahlavi", - "pam": "Pampanga", - "pap": "Papiamento", - "pau": "Palauan", - "pcd": "Picard", - "pcm": "Nigerian Pidgin", - "pdc": "Pennsylvania German", - "pdt": "Plautdietsch", - "peo": "Old Persian", - "pfl": "Palatine German", - "phn": "Phoenician", - "pi": "Pali", - "pis": "Pijin", - "pl": "Polish", - "pms": "Piedmontese", - "pnt": "Pontic", - "pon": "Pohnpeian", - "pqm": "Maliseet-Passamaquoddy", - "prg": "Prussian", - "pro": "Old Provençal", - "ps": "Pashto", - "ps-alt-variant": "Pushto", - "pt": "Portuguese", - "pt-BR": "Brazilian Portuguese", - "pt-PT": "European Portuguese", - "qu": "Quechua", - "quc": "Kʼicheʼ", - "qug": "Chimborazo Highland Quichua", - "raj": "Rajasthani", - "rap": "Rapanui", - "rar": "Rarotongan", - "rgn": "Romagnol", - "rhg": "Rohingya", - "rif": "Riffian", - "rm": "Romansh", - "rn": "Rundi", - "ro": "Romanian", - "ro-MD": "Moldavian", - "rof": "Rombo", - "rom": "Romany", - "rtm": "Rotuman", - "ru": "Russian", - "rue": "Rusyn", - "rug": "Roviana", - "rup": "Aromanian", - "rw": "Kinyarwanda", - "rwk": "Rwa", - "sa": "Sanskrit", - "sad": "Sandawe", - "sah": "Yakut", - "sam": "Samaritan Aramaic", - "saq": "Samburu", - "sas": "Sasak", - "sat": "Santali", - "saz": "Saurashtra", - "sba": "Ngambay", - "sbp": "Sangu", - "sc": "Sardinian", - "scn": "Sicilian", - "sco": "Scots", - "sd": "Sindhi", - "sdc": "Sassarese Sardinian", - "sdh": "Southern Kurdish", - "se": "Northern Sami", - "se-alt-menu": "Sami, Northern", - "see": "Seneca", - "seh": "Sena", - "sei": "Seri", - "sel": "Selkup", - "ses": "Koyraboro Senni", - "sg": "Sango", - "sga": "Old Irish", - "sgs": "Samogitian", - "sh": "Serbo-Croatian", - "shi": "Tachelhit", - "shn": "Shan", - "shu": "Chadian Arabic", - "si": "Sinhala", - "sid": "Sidamo", - "sk": "Slovak", - "sl": "Slovenian", - "slh": "Southern Lushootseed", - "sli": "Lower Silesian", - "sly": "Selayar", - "sm": "Samoan", - "sma": "Southern Sami", - "sma-alt-menu": "Sami, Southern", - "smj": "Lule Sami", - "smj-alt-menu": "Sami, Lule", - "smn": "Inari Sami", - "smn-alt-menu": "Sami, Inari", - "sms": "Skolt Sami", - "sms-alt-menu": "Sami, Skolt", - "sn": "Shona", - "snk": "Soninke", - "so": "Somali", - "sog": "Sogdien", - "sq": "Albanian", - "sr": "Serbian", - "sr-ME": "Montenegrin", - "srn": "Sranan Tongo", - "srr": "Serer", - "ss": "Swati", - "ssy": "Saho", - "st": "Southern Sotho", - "stq": "Saterland Frisian", - "str": "Straits Salish", - "su": "Sundanese", - "suk": "Sukuma", - "sus": "Susu", - "sux": "Sumerian", - "sv": "Swedish", - "sw": "Swahili", - "sw-CD": "Congo Swahili", - "swb": "Comorian", - "syc": "Classical Syriac", - "syr": "Syriac", - "szl": "Silesian", - "ta": "Tamil", - "tce": "Southern Tutchone", - "tcy": "Tulu", - "te": "Telugu", - "tem": "Timne", - "teo": "Teso", - "ter": "Tereno", - "tet": "Tetum", - "tg": "Tajik", - "tgx": "Tagish", - "th": "Thai", - "tht": "Tahltan", - "ti": "Tigrinya", - "tig": "Tigre", - "tiv": "Tiv", - "tk": "Turkmen", - "tkl": "Tokelau", - "tkr": "Tsakhur", - "tl": "Tagalog", - "tlh": "Klingon", - "tli": "Tlingit", - "tly": "Talysh", - "tmh": "Tamashek", - "tn": "Tswana", - "to": "Tongan", - "tog": "Nyasa Tonga", - "tok": "Toki Pona", - "tpi": "Tok Pisin", - "tr": "Turkish", - "tru": "Turoyo", - "trv": "Taroko", - "trw": "Torwali", - "ts": "Tsonga", - "tsd": "Tsakonian", - "tsi": "Tsimshian", - "tt": "Tatar", - "ttm": "Northern Tutchone", - "ttt": "Muslim Tat", - "tum": "Tumbuka", - "tvl": "Tuvalu", - "tw": "Twi", - "twq": "Tasawaq", - "ty": "Tahitian", - "tyv": "Tuvinian", - "tzm": "Central Atlas Tamazight", - "udm": "Udmurt", - "ug": "Uyghur", - "ug-alt-variant": "Uighur", - "uga": "Ugaritic", - "uk": "Ukrainian", - "umb": "Umbundu", - "und": "Unknown language", - "ur": "Urdu", - "uz": "Uzbek", - "vai": "Vai", - "ve": "Venda", - "vec": "Venetian", - "vep": "Veps", - "vi": "Vietnamese", - "vls": "West Flemish", - "vmf": "Main-Franconian", - "vmw": "Makhuwa", - "vo": "Volapük", - "vot": "Votic", - "vro": "Võro", - "vun": "Vunjo", - "wa": "Walloon", - "wae": "Walser", - "wal": "Wolaytta", - "war": "Waray", - "was": "Washo", - "wbp": "Warlpiri", - "wo": "Wolof", - "wuu": "Wu Chinese", - "xal": "Kalmyk", - "xh": "Xhosa", - "xmf": "Mingrelian", - "xnr": "Kangri", - "xog": "Soga", - "yao": "Yao", - "yap": "Yapese", - "yav": "Yangben", - "ybb": "Yemba", - "yi": "Yiddish", - "yo": "Yoruba", - "yrl": "Nheengatu", - "yue": "Cantonese", - "yue-alt-menu": "Chinese, Cantonese", - "za": "Zhuang", - "zap": "Zapotec", - "zbl": "Blissymbols", - "zea": "Zeelandic", - "zen": "Zenaga", - "zgh": "Standard Moroccan Tamazight", - "zh": "Chinese", - "zh-alt-long": "Mandarin Chinese", - "zh-alt-menu": "Chinese, Mandarin", - "zh-Hans": "Simplified Chinese", - "zh-Hans-alt-long": "Simplified Mandarin Chinese", - "zh-Hant": "Traditional Chinese", - "zh-Hant-alt-long": "Traditional Mandarin Chinese", - "zu": "Zulu", - "zun": "Zuni", - "zxx": "No linguistic content", - "zza": "Zaza" -} \ No newline at end of file diff --git a/subcrates/telemetry/src/lib.rs b/subcrates/telemetry/src/lib.rs index eea59e5..6c1647e 100644 --- a/subcrates/telemetry/src/lib.rs +++ b/subcrates/telemetry/src/lib.rs @@ -1,677 +1,9 @@ use serde::Serialize; use serde_json::{Value, json}; - -pub mod gpu { - use serde::Serialize; - use serde_json::{Value, json}; - use wgpu; - use winit::{ - application::ApplicationHandler, - event::WindowEvent, - event_loop::{ActiveEventLoop, EventLoop}, - window::WindowId, - }; - - #[derive(Debug, Serialize)] - pub struct DriverInfo { - version: String, - name: String, - } - - #[derive(Debug, Serialize)] - pub struct AdapterInfo { - vendor: String, - model: String, - driver: DriverInfo, - vram: String, - display: DisplayInfo, - } - - #[derive(Debug, Serialize)] - pub struct GPUInfo { - supported_backends: Vec, - gpus: Vec, - } - - #[derive(Debug, Serialize, Default)] - pub struct DisplayInfo { - resolution: String, - refresh_rate: String, - } - - impl ApplicationHandler for DisplayInfo { - fn resumed(&mut self, event_loop: &ActiveEventLoop) { - if let Some(monitor) = event_loop.primary_monitor() { - let size = monitor.size(); - let refresh_rate = monitor.refresh_rate_millihertz(); - - self.resolution = format!("{}x{}", size.width, size.height); - self.refresh_rate = if let Some(refresh) = refresh_rate { - format!("{} hz", refresh / 1000) - } else { - String::from("Unknown") - } - } else { - self.resolution = String::from("Unknown"); - self.refresh_rate = String::from("Unknown"); - } - - event_loop.exit(); - } - - fn window_event( - &mut self, - _event_loop: &ActiveEventLoop, - _window_id: WindowId, - _event: WindowEvent, - ) { - } - } - - mod vram { - use ash::vk::{API_VERSION_1_2, ApplicationInfo, InstanceCreateInfo, MemoryHeapFlags}; - use humansize::{DECIMAL, make_format}; - - #[cfg(not(target_os = "macos"))] - pub fn get_metal() -> u64 { - 0 - } - - #[cfg(target_os = "macos")] - pub fn get_metal() -> u64 { - use metal::Device as MetalDevice; - - let device = MetalDevice::system_default().expect("No Metal-compatible GPU found"); - device.recommended_max_working_set_size() - } - - pub fn get_vulkan(device_id: u32) -> u64 { - let entry = unsafe { ash::Entry::load().unwrap() }; - let app_info = ApplicationInfo { - p_application_name: std::ptr::null(), - application_version: 0, - p_engine_name: std::ptr::null(), - engine_version: 0, - api_version: API_VERSION_1_2, - ..Default::default() - }; - - let create_info = InstanceCreateInfo { - p_application_info: &app_info, - ..Default::default() - }; - - let instance = unsafe { entry.create_instance(&create_info, None).unwrap() }; - - let physical_devices = unsafe { instance.enumerate_physical_devices().unwrap() }; - let mut total_vram = 0; - - for device in physical_devices { - let memory_properties = - unsafe { instance.get_physical_device_memory_properties(device) }; - let device_properties = unsafe { instance.get_physical_device_properties(device) }; - - if device_id != device_properties.device_id { - continue; - } - - for heap in memory_properties.memory_heaps { - if heap.flags.contains(MemoryHeapFlags::DEVICE_LOCAL) { - total_vram += heap.size; - } - } - - break; - } - - total_vram - } - - pub fn get(vendor: u32, device_id: u32) -> String { - let formatter = make_format(DECIMAL); - - match vendor { - 0x10DE | 0x1002 | 0x8086 | 0x5143 => formatter(get_vulkan(device_id)), - 0x1010 => formatter(get_metal()), - _ => String::from("Unknown"), - } - } - } - - fn vendor_name(vendor: u32) -> &'static str { - match vendor { - 0x10DE => "NVIDIA", - 0x1002 => "AMD(Advanced Micro Devices), Inc.", - 0x8086 => "Intel(integrated electronics)", - 0x13B5 => "ARM(Advanced RISC Machines)", - 0x5143 => "Qualcomm(Quality Communications)", - 0x1010 => "Apple Inc.", - _ => "Unknown", - } - } - - pub fn get_struct() -> GPUInfo { - let mut gpu_data: Vec = Vec::new(); - let mut backends: Vec = Vec::new(); - let instance_descriptor = wgpu::InstanceDescriptor { - backends: wgpu::Backends::all(), - flags: wgpu::InstanceFlags::empty(), - backend_options: Default::default(), - }; - let instance = wgpu::Instance::new(&instance_descriptor); - let event_loop = EventLoop::new().unwrap(); - let mut app = DisplayInfo::default(); - - event_loop.run_app(&mut app).unwrap(); - - for adapter in instance.enumerate_adapters(wgpu::Backends::all()) { - let info = adapter.get_info(); - - if !backends.contains(&info.backend.to_string()) { - backends.push(info.backend.to_string()); - } - if info.driver.is_empty() || info.driver_info.is_empty() { - continue; - } - - gpu_data.push(AdapterInfo { - vendor: vendor_name(info.vendor).to_string(), - model: info.name, - driver: DriverInfo { - version: info.driver_info, - name: info.driver, - }, - vram: vram::get(info.vendor, info.device), - display: DisplayInfo { - resolution: app.resolution.to_string(), - refresh_rate: app.refresh_rate.to_string(), - }, - }); - } - - GPUInfo { - supported_backends: backends, - gpus: gpu_data, - } - } - - pub fn get_json() -> Value { - json!(get_struct()) - } -} - -pub mod cpu { - use serde::Serialize; - use serde_json::{Value, json}; - use std::collections::HashMap; - use sysinfo::{CpuRefreshKind, RefreshKind, System}; - - #[derive(Debug, Serialize)] - pub struct ProcessorInfo { - vendor: String, - brand: String, - total_system_cores: String, - threads: u16, - architecture: String, - byte_order: String, - } - - #[derive(Debug, Serialize)] - pub struct CPUInfo { - cpus: Vec, - } - - pub fn get_struct() -> CPUInfo { - let system = System::new_with_specifics( - RefreshKind::nothing().with_cpu(CpuRefreshKind::everything()), - ); - - let mut processors = Vec::new(); - let mut processor_count: HashMap = HashMap::new(); - - for cpu in system.cpus() { - let brand = cpu.brand().trim(); - let total_cores = System::physical_core_count(); - - if processor_count.contains_key(brand) { - processor_count.get_mut(brand).unwrap().threads += 1; - continue; - } - processor_count.insert( - brand.to_string(), - ProcessorInfo { - vendor: cpu.vendor_id().trim().to_string(), - brand: brand.to_string(), - total_system_cores: if let Some(cores) = total_cores { - cores.to_string() - } else { - String::from("Unknown") - }, - threads: 1, - architecture: System::cpu_arch().trim().to_string(), - byte_order: if cfg!(target_endian = "little") { - String::from("little-endian") - } else { - String::from("big-endian") - }, - }, - ); - } - - for (_brand, info) in processor_count { - processors.push(info); - } - - CPUInfo { cpus: processors } - } - pub fn get_json() -> Value { - json!(get_struct()) - } -} - -pub mod memory { - use humansize::{DECIMAL, make_format}; - use serde::Serialize; - use serde_json::{Value, json}; - use sysinfo::System; - - #[derive(Debug, Serialize)] - pub struct PhysicalInfo { - total: String, - used: String, - free: String, - available: String, - } - - #[derive(Debug, Serialize)] - pub struct VirtualInfo { - total: String, - used: String, - free: String, - } - - #[derive(Debug, Serialize)] - pub struct MemoryInfo { - physical: PhysicalInfo, - virtual_swap: VirtualInfo, - } - - pub fn get_struct() -> MemoryInfo { - let system = System::new_all(); - let formatter = make_format(DECIMAL); - - MemoryInfo { - physical: PhysicalInfo { - total: formatter(system.total_memory()), - used: formatter(system.used_memory()), - free: formatter(system.free_memory()), - available: formatter(system.available_memory()), - }, - virtual_swap: VirtualInfo { - total: formatter(system.total_swap()), - used: formatter(system.used_swap()), - free: formatter(system.free_swap()), - }, - } - } - - pub fn get_json() -> Value { - json!(get_struct()) - } -} - -pub mod os { - use detect_desktop_environment::DesktopEnvironment; - use serde::Serialize; - use serde_json::{Value, json}; - use sysinfo::System; - - #[derive(Debug, Serialize)] - pub struct OSInfo { - name: String, - edition: String, - version: String, - architecture: String, - kernel: String, - de: String, - } - - pub fn get_struct() -> OSInfo { - OSInfo { - name: System::name().unwrap_or(String::from("Unknown")), - edition: System::long_os_version().unwrap_or(String::from("Unknown")), - version: System::os_version().unwrap_or(String::from("Unknown")), - architecture: if cfg!(target_pointer_width = "64") { - String::from("64 bit") - } else if cfg!(target_pointer_width = "32") { - String::from("32 bit") - } else { - String::from("16 bit") - }, - kernel: System::kernel_long_version(), - de: match DesktopEnvironment::detect() { - Some(de) => format!("{de:?}"), - None => String::from(""), - }, - } - } - - pub fn get_json() -> Value { - json!(get_struct()) - } -} - -pub mod storage { - use humansize::{DECIMAL, make_format}; - use serde::Serialize; - use serde_json::{Value, json}; - use sysinfo::Disks; - - #[derive(Debug, Serialize)] - pub struct StorageInfo { - name: String, - mount_point: String, - kind: String, - space_left: String, - total: String, - } - - pub fn get_list() -> Vec { - let disks = Disks::new_with_refreshed_list(); - let mut drive_data = Vec::new(); - let formatter = make_format(DECIMAL); - - for disk in disks.list() { - drive_data.push(StorageInfo { - name: format!("{:?}", disk.name()).trim_matches('\"').to_string(), - mount_point: format!("{:?}", disk.mount_point()) - .replace("\\", "") - .trim_matches('\"') - .to_string(), - kind: format!("{:?}", disk.kind()), - space_left: formatter(disk.available_space()), - total: formatter(disk.total_space()), - }); - } - - drive_data - } - - pub fn get_json() -> Value { - json!(get_list()) - } -} - -pub mod network { - use serde::Serialize; - use serde_json::{Value, json}; - use sysinfo::Networks; - - #[derive(Debug, Serialize)] - pub struct NetworkInfo { - adapters: Vec, - } - - pub fn get_struct() -> NetworkInfo { - let networks = Networks::new_with_refreshed_list(); - let mut adapters = Vec::new(); - - for (interface_name, _network_data) in &networks { - adapters.push(interface_name.to_string()); - } - - NetworkInfo { adapters } - } - - pub fn get_json() -> Value { - json!(get_struct()) - } -} - -pub mod external { - use serde::Serialize; - use serde_json::{Value, json}; - use std::{cmp::Ordering::Equal, collections::HashMap}; - use sysinfo::{System, Users}; - - #[derive(Debug, Serialize)] - pub struct SoftwareInfo { - name: String, - count: usize, - } - - #[derive(Debug, Serialize)] - pub struct ExternalInfo { - softwares: Vec, - } - - pub fn get_struct() -> ExternalInfo { - let mut system = System::new_all(); - system.refresh_all(); - - let users = &Users::new_with_refreshed_list(); - let mut grouped_processes: HashMap> = HashMap::new(); - - for process in system.processes().values() { - let name = process.name().to_str().unwrap().to_string(); - let user_id = process.user_id(); - let is_user_owned = user_id.is_some_and(|uid| { - users - .list() - .iter() - .any(|u| u.id() == uid && u.name() != "root") - }); - - if is_user_owned && !name.trim().is_empty() && !name.starts_with('[') { - grouped_processes - .entry(name) - .or_default() - .push(process.pid().as_u32() as i32); - } - } - - let mut softwares = Vec::new(); - let mut grouped_vec: Vec<_> = grouped_processes.into_iter().collect(); - grouped_vec.sort_by(|a, b| match b.1.len().cmp(&a.1.len()) { - Equal => a.0.to_lowercase().cmp(&b.0.to_lowercase()), - other => other, - }); - - for (name, pids) in grouped_vec { - softwares.push(SoftwareInfo { - name, - count: pids.len(), - }); - } - - ExternalInfo { softwares } - } - - pub fn get_json() -> Value { - json!(get_struct()) - } -} - -pub mod devices { - use serde::Serialize; - use serde_json::{Value, json}; - extern crate hidapi; - use hidapi::HidApi; - use std::collections::HashMap; - - #[derive(Debug, Serialize)] - pub struct DeviceInfo { - manufacturer: String, - products: Vec, - } - - #[derive(Debug, Serialize)] - pub struct DevicesInfo { - devices: Vec, - } - - pub fn get_struct() -> DevicesInfo { - let api = HidApi::new().unwrap(); - let mut grouped_devices: HashMap> = HashMap::new(); - - for device in api.device_list() { - let manufacturer = device - .manufacturer_string() - .unwrap_or("Unknown") - .to_string() - .to_lowercase(); - let product = device.product_string().unwrap_or("Unknown").to_string(); - - if !manufacturer.trim().is_empty() && !product.trim().is_empty() { - let entry = grouped_devices.entry(manufacturer).or_default(); - - if !entry.contains(&product) { - entry.push(product); - } - } - } - - let mut devices = Vec::new(); - let mut grouped_vec: Vec<_> = grouped_devices.into_iter().collect(); - grouped_vec.sort_by(|a, b| a.0.to_lowercase().cmp(&b.0.to_lowercase())); - - for (manufacturer, products) in grouped_vec { - devices.push(DeviceInfo { - manufacturer, - products, - }); - } - - DevicesInfo { devices } - } - - pub fn get_json() -> Value { - json!(get_struct()) - } -} - -pub mod meta { - use serde::Serialize; - use serde_json::{Value, from_str, json}; - use std::{collections::HashMap, fs}; - use sys_locale::get_locale; - - include!(concat!(env!("OUT_DIR"), "/built.rs")); - - #[derive(Debug, Serialize)] - pub struct MetaInfo { - version: String, - commit_hash: String, - commit_short_hash: String, - locale: String, - language: String, - territory: String, - } - - pub fn get_struct() -> MetaInfo { - let default = String::from("Unknown"); - let locale = get_locale().unwrap_or_else(|| String::from("en-US")); - let split_locale: Vec<&str> = locale.split("-").collect(); - - let language_json = fs::read_to_string("subcrates/telemetry/src/languages.json").unwrap(); - let parsed_languages = from_str::>(&language_json).unwrap(); - let language_map = parsed_languages.get(split_locale[0]).unwrap_or(&default); - - let territory_json = - fs::read_to_string("subcrates/telemetry/src/territories.json").unwrap(); - let parsed_territories = from_str::>(&territory_json).unwrap(); - let territory_map = parsed_territories.get(split_locale[1]).unwrap_or(&default); - - MetaInfo { - version: env!("CARGO_PKG_VERSION").to_string(), - commit_hash: GIT_COMMIT_HASH.to_string(), - commit_short_hash: GIT_COMMIT_SHORT_HASH.to_string(), - locale, - language: language_map.to_string(), - territory: territory_map.to_string(), - } - } - - pub fn get_json() -> Value { - json!(get_struct()) - } -} - -pub mod uptime { - use chrono::{DateTime, Utc}; - use serde::Serialize; - use serde_json::{Value, json}; - use std::time::Duration as StdDuration; - use sysinfo::System; - - #[derive(Debug, Serialize)] - pub struct DateInfo { - zone: String, - date: String, - time: String, - } - - #[derive(Debug, Serialize)] - pub struct UptimeInfo { - boot: DateInfo, - now: DateInfo, - relative: String, - } - - pub fn format_duration(duration: StdDuration) -> String { - let mut parts = Vec::new(); - let mut remaining = duration.as_secs(); - let units = [ - (" months", 30 * 24 * 60 * 60), - (" weeks", 7 * 24 * 60 * 60), - (" days", 24 * 60 * 60), - (" hours", 60 * 60), - (" minutes", 60), - (" seconds", 1), - ]; - - for &(label, unit_secs) in &units { - match remaining / unit_secs { - 0 => continue, - value => { - parts.push(format!("{}{}", value, label)); - remaining %= unit_secs; - } - } - } - - if parts.is_empty() { - parts.push("0 seconds".to_string()); - } - - parts.join(", ") - } - - pub fn get_struct() -> UptimeInfo { - let boot_time: DateTime = - DateTime::::from_timestamp(System::boot_time() as i64, 0) - .expect("Invalid or out-of-range timestamp"); - let relative_time = StdDuration::from_secs((Utc::now() - boot_time).num_seconds() as u64); - - UptimeInfo { - boot: DateInfo { - zone: String::from("UTC"), - date: boot_time.format("%Y-%m-%d").to_string(), - time: boot_time.format("%H:%M:%S").to_string(), - }, - now: DateInfo { - zone: String::from("UTC"), - date: Utc::now().format("%Y-%m-%d").to_string(), - time: Utc::now().format("%H:%M:%S").to_string(), - }, - relative: format_duration(relative_time).to_string(), - } - } - - pub fn get_json() -> Value { - json!(get_struct()) - } -} +#[macro_use] +mod modules; +use crate::modules::{cpu, devices, external, gpu, memory, meta, network, os, storage, uptime}; +use anyhow::Result; #[derive(Debug, Serialize)] pub struct TelemetryInfo { @@ -692,23 +24,25 @@ pub struct AllInfo { uptime: uptime::UptimeInfo, } -pub fn get_struct() -> TelemetryInfo { - TelemetryInfo { +#[allow(dead_code)] +pub fn get_struct() -> Result { + Ok(TelemetryInfo { telemetry: AllInfo { gpu: gpu::get_struct(), cpu: cpu::get_struct(), memory: memory::get_struct(), - os: os::get_struct(), - storage: storage::get_list(), + os: os::get_struct()?, + storage: storage::get_list()?, network: network::get_struct(), external: external::get_struct(), - devices: devices::get_struct(), - meta: meta::get_struct(), - uptime: uptime::get_struct(), + devices: devices::get_struct()?, + meta: meta::get_struct()?, + uptime: uptime::get_struct()?, }, - } + }) } -pub fn get_json() -> Value { - json!(get_struct()) +#[allow(dead_code)] +pub fn get_json() -> Result { + Ok(json!(get_struct()?)) } diff --git a/subcrates/telemetry/src/modules/cpu.rs b/subcrates/telemetry/src/modules/cpu.rs new file mode 100644 index 0000000..b40d671 --- /dev/null +++ b/subcrates/telemetry/src/modules/cpu.rs @@ -0,0 +1,69 @@ +use super::Defaults::Unknown; +use serde::Serialize; +use serde_json::{Value, json}; +use std::collections::HashMap; +use sysinfo::{CpuRefreshKind, RefreshKind, System}; + +#[derive(Debug, Serialize)] +pub struct ProcessorInfo { + vendor: String, + brand: String, + total_system_cores: String, + threads: u16, + architecture: String, + byte_order: String, +} + +#[derive(Debug, Serialize)] +pub struct CPUInfo { + cpus: Vec, +} + +#[allow(dead_code)] +pub fn get_struct() -> CPUInfo { + let system = + System::new_with_specifics(RefreshKind::nothing().with_cpu(CpuRefreshKind::everything())); + + let mut processors = Vec::new(); + let mut processor_count: HashMap = HashMap::new(); + + for cpu in system.cpus() { + let brand = cpu.brand().trim(); + let total_cores = System::physical_core_count(); + + if processor_count.contains_key(brand) { + processor_count.get_mut(brand).unwrap().threads += 1; + continue; + } + processor_count.insert( + brand.to_string(), + ProcessorInfo { + vendor: cpu.vendor_id().trim().to_string(), + brand: brand.to_string(), + total_system_cores: if let Some(cores) = total_cores { + cores.to_string() + } else { + Unknown.into() + }, + threads: 1, + architecture: System::cpu_arch().trim().to_string(), + byte_order: if cfg!(target_endian = "little") { + String::from("little-endian") + } else { + String::from("big-endian") + }, + }, + ); + } + + for (_brand, info) in processor_count { + processors.push(info); + } + + CPUInfo { cpus: processors } +} + +#[allow(dead_code)] +pub fn get_json() -> Value { + json!(get_struct()) +} diff --git a/subcrates/telemetry/src/modules/devices.rs b/subcrates/telemetry/src/modules/devices.rs new file mode 100644 index 0000000..81cf169 --- /dev/null +++ b/subcrates/telemetry/src/modules/devices.rs @@ -0,0 +1,59 @@ +//use crate::custom_anyhow; +use anyhow::Result; +use serde::Serialize; +use serde_json::{Value, json}; +extern crate hidapi; +use hidapi::HidApi; +use std::collections::HashMap; + +#[derive(Debug, Serialize)] +pub struct DeviceInfo { + manufacturer: String, + products: Vec, +} + +#[derive(Debug, Serialize)] +pub struct DevicesInfo { + devices: Vec, +} + +#[allow(dead_code)] +pub fn get_struct() -> Result { + let api = HidApi::new().unwrap(); + let mut grouped_devices: HashMap> = HashMap::new(); + + for device in api.device_list() { + let manufacturer = device + .manufacturer_string() + .unwrap_or("Unknown") + .to_string() + .to_lowercase(); + let product = device.product_string().unwrap_or("Unknown").to_string(); + + if !manufacturer.trim().is_empty() && !product.trim().is_empty() { + let entry = grouped_devices.entry(manufacturer).or_default(); + + if !entry.contains(&product) { + entry.push(product); + } + } + } + + let mut devices = Vec::new(); + let mut grouped_vec: Vec<_> = grouped_devices.into_iter().collect(); + grouped_vec.sort_by(|a, b| a.0.to_lowercase().cmp(&b.0.to_lowercase())); + + for (manufacturer, products) in grouped_vec { + devices.push(DeviceInfo { + manufacturer, + products, + }); + } + + Ok(DevicesInfo { devices }) +} + +#[allow(dead_code)] +pub fn get_json() -> Result { + Ok(json!(get_struct()?)) +} diff --git a/subcrates/telemetry/src/modules/external.rs b/subcrates/telemetry/src/modules/external.rs new file mode 100644 index 0000000..7987df8 --- /dev/null +++ b/subcrates/telemetry/src/modules/external.rs @@ -0,0 +1,63 @@ +use serde::Serialize; +use serde_json::{Value, json}; +use std::{cmp::Ordering::Equal, collections::HashMap}; +use sysinfo::{System, Users}; + +#[derive(Debug, Serialize)] +pub struct SoftwareInfo { + name: String, + count: usize, +} + +#[derive(Debug, Serialize)] +pub struct ExternalInfo { + softwares: Vec, +} + +#[allow(dead_code)] +pub fn get_struct() -> ExternalInfo { + let mut system = System::new_all(); + system.refresh_all(); + + let users = &Users::new_with_refreshed_list(); + let mut grouped_processes: HashMap> = HashMap::new(); + + for process in system.processes().values() { + let name = process.name().to_str().unwrap().to_string(); + let user_id = process.user_id(); + let is_user_owned = user_id.is_some_and(|uid| { + users + .list() + .iter() + .any(|u| u.id() == uid && u.name() != "root") + }); + + if is_user_owned && !name.trim().is_empty() && !name.starts_with('[') { + grouped_processes + .entry(name) + .or_default() + .push(process.pid().as_u32() as i32); + } + } + + let mut softwares = Vec::new(); + let mut grouped_vec: Vec<_> = grouped_processes.into_iter().collect(); + grouped_vec.sort_by(|a, b| match b.1.len().cmp(&a.1.len()) { + Equal => a.0.to_lowercase().cmp(&b.0.to_lowercase()), + other => other, + }); + + for (name, pids) in grouped_vec { + softwares.push(SoftwareInfo { + name, + count: pids.len(), + }); + } + + ExternalInfo { softwares } +} + +#[allow(dead_code)] +pub fn get_json() -> Value { + json!(get_struct()) +} diff --git a/subcrates/telemetry/src/modules/formatted/architecture.rs b/subcrates/telemetry/src/modules/formatted/architecture.rs new file mode 100644 index 0000000..b875991 --- /dev/null +++ b/subcrates/telemetry/src/modules/formatted/architecture.rs @@ -0,0 +1,55 @@ +use serde::Serialize; +use std::{ + fmt::{Debug, Display, Formatter, Result as FmtResult}, + ops::Deref, +}; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize)] +pub enum TargetPointerWidth { + W32Bit, + W64Bit, +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct FmtOSArchitecture(pub TargetPointerWidth); + +impl FmtOSArchitecture { + pub const W32_BIT: Self = FmtOSArchitecture(TargetPointerWidth::W32Bit); + pub const W64_BIT: Self = FmtOSArchitecture(TargetPointerWidth::W64Bit); +} + +impl Display for FmtOSArchitecture { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!( + f, + "{}", + match self.0 { + TargetPointerWidth::W32Bit => "32 Bit", + TargetPointerWidth::W64Bit => "64 Bit", + } + ) + } +} + +impl Debug for FmtOSArchitecture { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + Display::fmt(self, f) + } +} + +impl Serialize for FmtOSArchitecture { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl Deref for FmtOSArchitecture { + type Target = TargetPointerWidth; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/subcrates/telemetry/src/modules/formatted/bytes.rs b/subcrates/telemetry/src/modules/formatted/bytes.rs new file mode 100644 index 0000000..598fc81 --- /dev/null +++ b/subcrates/telemetry/src/modules/formatted/bytes.rs @@ -0,0 +1,38 @@ +use serde::Serialize; +use std::{ + fmt::{Debug, Display, Formatter, Result as FmtResult}, + ops::Deref, +}; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct FmtBytes(pub u64); + +impl Display for FmtBytes { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + use humansize::{DECIMAL, format_size}; + write!(f, "{}", format_size(self.0, DECIMAL)) + } +} + +impl Debug for FmtBytes { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + Display::fmt(self, f) + } +} + +impl Serialize for FmtBytes { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl Deref for FmtBytes { + type Target = u64; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/subcrates/telemetry/src/modules/formatted/date_time.rs b/subcrates/telemetry/src/modules/formatted/date_time.rs new file mode 100644 index 0000000..8c26003 --- /dev/null +++ b/subcrates/telemetry/src/modules/formatted/date_time.rs @@ -0,0 +1,125 @@ +use chrono::{DateTime, Local, TimeZone, Utc}; +use serde::Serialize; +use std::{ + fmt::{Debug, Display, Formatter, Result as FMTResult}, + ops::Deref, +}; + +#[derive(Debug, Serialize)] +pub struct DateTimeInfo { + date: String, + time: String, +} + +#[derive(Clone)] +pub struct FmtDateTime(pub DateTime); + +impl Display for FmtDateTime +where + Tz: TimeZone, + Tz::Offset: std::fmt::Display, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> FMTResult { + write!( + f, + "{:#?}", + DateTimeInfo { + date: self.0.format("%Y-%m-%d").to_string(), + time: self.0.format("%H:%M:%S").to_string() + } + ) + } +} + +impl Debug for FmtDateTime +where + Tz::Offset: std::fmt::Display, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> FMTResult { + Display::fmt(self, f) + } +} + +impl Serialize for FmtDateTime +where + DateTime: Display, +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl Deref for FmtDateTime { + type Target = DateTime; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Eq for FmtDateTime where DateTime: Eq {} +impl PartialEq for FmtDateTime +where + DateTime: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl PartialOrd for FmtDateTime +where + DateTime: PartialOrd, +{ + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for FmtDateTime +where + DateTime: Ord, +{ + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.0.cmp(&other.0) + } +} + +impl From> for FmtDateTime { + fn from(dt: FmtDateTime) -> Self { + FmtDateTime(dt.0.with_timezone(&Local)) + } +} + +impl From> for FmtDateTime { + fn from(dt: FmtDateTime) -> Self { + FmtDateTime(dt.0.with_timezone(&Utc)) + } +} + +#[allow(dead_code)] +pub trait IntoDateTime { + fn into_utc(self) -> FmtDateTime; + fn into_local(self) -> FmtDateTime; +} + +impl IntoDateTime for FmtDateTime { + fn into_utc(self) -> FmtDateTime { + FmtDateTime(self.0.with_timezone(&Utc)) + } + fn into_local(self) -> FmtDateTime { + self + } +} + +impl IntoDateTime for FmtDateTime { + fn into_local(self) -> FmtDateTime { + FmtDateTime(self.0.with_timezone(&Local)) + } + fn into_utc(self) -> FmtDateTime { + self + } +} diff --git a/subcrates/telemetry/src/modules/formatted/desktop_environment.rs b/subcrates/telemetry/src/modules/formatted/desktop_environment.rs new file mode 100644 index 0000000..7f636c1 --- /dev/null +++ b/subcrates/telemetry/src/modules/formatted/desktop_environment.rs @@ -0,0 +1,59 @@ +use detect_desktop_environment::DesktopEnvironment; +use serde::{Serialize, Serializer}; +use std::{ + fmt::{Debug, Display, Formatter, Result as FmtResult}, + ops::Deref, +}; + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct SerdeDE(pub DesktopEnvironment); + +impl Serialize for SerdeDE { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str(&format!("{:?}", self.0)) + } +} + +impl SerdeDE { + pub fn detect() -> Option { + DesktopEnvironment::detect().map(SerdeDE) + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct FmtDE(pub SerdeDE); + +impl FmtDE { + pub fn detect() -> Option { + SerdeDE::detect().map(FmtDE) + } +} + +impl Display for FmtDE { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "{:?}", self.0.0) + } +} + +impl Debug for FmtDE { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + Display::fmt(self, f) + } +} + +impl Serialize for FmtDE { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl Deref for FmtDE { + type Target = SerdeDE; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/subcrates/telemetry/src/modules/formatted/mod.rs b/subcrates/telemetry/src/modules/formatted/mod.rs new file mode 100644 index 0000000..38987d8 --- /dev/null +++ b/subcrates/telemetry/src/modules/formatted/mod.rs @@ -0,0 +1,7 @@ +pub mod architecture; +pub mod bytes; +pub mod date_time; +pub mod desktop_environment; +pub mod offset_time; +pub mod relative_time; +pub mod version; diff --git a/subcrates/telemetry/src/modules/formatted/offset_time.rs b/subcrates/telemetry/src/modules/formatted/offset_time.rs new file mode 100644 index 0000000..36d4215 --- /dev/null +++ b/subcrates/telemetry/src/modules/formatted/offset_time.rs @@ -0,0 +1,68 @@ +use chrono::{DateTime, Local, Offset}; +use serde::Serialize; +use std::{ + cmp::Ordering, + fmt::{Debug, Display, Formatter, Result as FmtResult}, + ops::Deref, +}; + +#[derive(Copy, Clone)] +pub struct FmtOffsetTime(pub DateTime); + +impl Display for FmtOffsetTime { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "UTC{}", self.0.offset()) + } +} + +impl Debug for FmtOffsetTime { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + Display::fmt(self, f) + } +} + +impl Serialize for FmtOffsetTime { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl Deref for FmtOffsetTime { + type Target = DateTime; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Eq for FmtOffsetTime {} +impl PartialEq for FmtOffsetTime { + fn eq(&self, other: &Self) -> bool { + self.0.offset().fix() == other.0.offset().fix() + } +} + +impl PartialOrd for FmtOffsetTime { + fn partial_cmp(&self, other: &Self) -> Option { + Some( + self.0 + .offset() + .fix() + .local_minus_utc() + .cmp(&other.0.offset().fix().local_minus_utc()), + ) + } +} + +impl Ord for FmtOffsetTime { + fn cmp(&self, other: &Self) -> Ordering { + self.0 + .offset() + .fix() + .local_minus_utc() + .cmp(&other.0.offset().fix().local_minus_utc()) + } +} diff --git a/subcrates/telemetry/src/modules/formatted/relative_time.rs b/subcrates/telemetry/src/modules/formatted/relative_time.rs new file mode 100644 index 0000000..0e08c62 --- /dev/null +++ b/subcrates/telemetry/src/modules/formatted/relative_time.rs @@ -0,0 +1,40 @@ +use serde::Serialize; +use std::{ + fmt::{Debug, Display, Formatter, Result as FmtResult}, + ops::Deref, + time::Duration, +}; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct FmtRelativeTime(pub Duration); + +impl Display for FmtRelativeTime { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + let formatter = timeago::Formatter::new(); + + write!(f, "{}", formatter.convert(self.0)) + } +} + +impl Debug for FmtRelativeTime { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + Display::fmt(self, f) + } +} + +impl Serialize for FmtRelativeTime { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl Deref for FmtRelativeTime { + type Target = Duration; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/subcrates/telemetry/src/modules/formatted/version.rs b/subcrates/telemetry/src/modules/formatted/version.rs new file mode 100644 index 0000000..ccd741e --- /dev/null +++ b/subcrates/telemetry/src/modules/formatted/version.rs @@ -0,0 +1,38 @@ +use serde::Serialize; +use std::{ + fmt::{Debug, Display, Formatter, Result as FmtResult}, + ops::Deref, +}; +use versions::Versioning; + +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct FmtVersion(pub Versioning); + +impl Display for FmtVersion { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "{}", self.0) + } +} + +impl Debug for FmtVersion { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + Display::fmt(self, f) + } +} + +impl Serialize for FmtVersion { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl Deref for FmtVersion { + type Target = Versioning; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/subcrates/telemetry/src/modules/gpu/mod.rs b/subcrates/telemetry/src/modules/gpu/mod.rs new file mode 100644 index 0000000..af1e374 --- /dev/null +++ b/subcrates/telemetry/src/modules/gpu/mod.rs @@ -0,0 +1,131 @@ +use super::Defaults::Unknown; +use serde::Serialize; +use serde_json::{Value, json}; +use wgpu; +use winit::{ + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::WindowId, +}; + +mod vram; + +#[derive(Debug, Serialize)] +pub struct DriverInfo { + version: String, + name: String, +} + +#[derive(Debug, Serialize)] +pub struct AdapterInfo { + vendor: String, + model: String, + driver: DriverInfo, + vram: String, + display: DisplayInfo, +} + +#[derive(Debug, Serialize)] +pub struct GPUInfo { + supported_backends: Vec, + gpus: Vec, +} + +#[derive(Debug, Serialize, Default)] +pub struct DisplayInfo { + resolution: String, + refresh_rate: String, +} + +impl ApplicationHandler for DisplayInfo { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + if let Some(monitor) = event_loop.primary_monitor() { + let size = monitor.size(); + let refresh_rate = monitor.refresh_rate_millihertz(); + + self.resolution = format!("{}x{}", size.width, size.height); + self.refresh_rate = if let Some(refresh) = refresh_rate { + format!("{} hz", refresh / 1000) + } else { + Unknown.into() + } + } else { + self.resolution = Unknown.into(); + self.refresh_rate = Unknown.into(); + } + + event_loop.exit(); + } + + fn window_event( + &mut self, + _event_loop: &ActiveEventLoop, + _window_id: WindowId, + _event: WindowEvent, + ) { + } +} + +fn vendor_name(vendor: u32) -> &'static str { + match vendor { + 0x10DE => "NVIDIA", + 0x1002 => "AMD(Advanced Micro Devices), Inc.", + 0x8086 => "Intel(integrated electronics)", + 0x13B5 => "ARM(Advanced RISC Machines)", + 0x5143 => "Qualcomm(Quality Communications)", + 0x1010 => "Apple Inc.", + _ => "Unknown", + } +} + +#[allow(dead_code)] +pub fn get_struct() -> GPUInfo { + let mut gpu_data: Vec = Vec::new(); + let mut backends: Vec = Vec::new(); + let instance_descriptor = wgpu::InstanceDescriptor { + backends: wgpu::Backends::all(), + flags: wgpu::InstanceFlags::empty(), + backend_options: Default::default(), + }; + let instance = wgpu::Instance::new(&instance_descriptor); + let event_loop = EventLoop::new().unwrap(); + let mut app = DisplayInfo::default(); + + event_loop.run_app(&mut app).unwrap(); + + for adapter in instance.enumerate_adapters(wgpu::Backends::all()) { + let info = adapter.get_info(); + + if !backends.contains(&info.backend.to_string()) { + backends.push(info.backend.to_string()); + } + if info.driver.is_empty() || info.driver_info.is_empty() { + continue; + } + + gpu_data.push(AdapterInfo { + vendor: vendor_name(info.vendor).to_string(), + model: info.name, + driver: DriverInfo { + version: info.driver_info, + name: info.driver, + }, + vram: vram::get(info.vendor, info.device), + display: DisplayInfo { + resolution: app.resolution.to_string(), + refresh_rate: app.refresh_rate.to_string(), + }, + }); + } + + GPUInfo { + supported_backends: backends, + gpus: gpu_data, + } +} + +#[allow(dead_code)] +pub fn get_json() -> Value { + json!(get_struct()) +} diff --git a/subcrates/telemetry/src/modules/gpu/vram.rs b/subcrates/telemetry/src/modules/gpu/vram.rs new file mode 100644 index 0000000..0fd854c --- /dev/null +++ b/subcrates/telemetry/src/modules/gpu/vram.rs @@ -0,0 +1,67 @@ +use super::super::Defaults::Unknown; +use ash::vk::{API_VERSION_1_2, ApplicationInfo, InstanceCreateInfo, MemoryHeapFlags}; +use humansize::{DECIMAL, make_format}; + +#[cfg(not(target_os = "macos"))] +pub fn get_metal() -> u64 { + 0 +} + +#[cfg(target_os = "macos")] +pub fn get_metal() -> u64 { + use metal::Device as MetalDevice; + + let device = MetalDevice::system_default().expect("No Metal-compatible GPU found"); + device.recommended_max_working_set_size() +} + +pub fn get_vulkan(device_id: u32) -> u64 { + let entry = unsafe { ash::Entry::load().unwrap() }; + let app_info = ApplicationInfo { + p_application_name: std::ptr::null(), + application_version: 0, + p_engine_name: std::ptr::null(), + engine_version: 0, + api_version: API_VERSION_1_2, + ..Default::default() + }; + + let create_info = InstanceCreateInfo { + p_application_info: &app_info, + ..Default::default() + }; + + let instance = unsafe { entry.create_instance(&create_info, None).unwrap() }; + + let physical_devices = unsafe { instance.enumerate_physical_devices().unwrap() }; + let mut total_vram = 0; + + for device in physical_devices { + let memory_properties = unsafe { instance.get_physical_device_memory_properties(device) }; + let device_properties = unsafe { instance.get_physical_device_properties(device) }; + + if device_id != device_properties.device_id { + continue; + } + + for heap in memory_properties.memory_heaps { + if heap.flags.contains(MemoryHeapFlags::DEVICE_LOCAL) { + total_vram += heap.size; + } + } + + break; + } + + total_vram +} + +pub fn get(vendor: u32, device_id: u32) -> String { + let formatter = make_format(DECIMAL); + + match vendor { + 0x10DE | 0x1002 | 0x8086 | 0x5143 => formatter(get_vulkan(device_id)), + 0x1010 => formatter(get_metal()), + _ => Unknown.into(), + } +} diff --git a/subcrates/telemetry/src/modules/macros/custom_anyhow.rs b/subcrates/telemetry/src/modules/macros/custom_anyhow.rs new file mode 100644 index 0000000..7b0ca67 --- /dev/null +++ b/subcrates/telemetry/src/modules/macros/custom_anyhow.rs @@ -0,0 +1,11 @@ +#[macro_export] +macro_rules! custom_anyhow { + ($fmt:literal $(, $arg:expr)* $(,)?) => { + ::anyhow::anyhow!( + concat!($fmt, " (file: {}, line: {})"), + $($arg,)* + file!(), + line!() + ) + }; +} diff --git a/subcrates/telemetry/src/modules/macros/mod.rs b/subcrates/telemetry/src/modules/macros/mod.rs new file mode 100644 index 0000000..ec3320c --- /dev/null +++ b/subcrates/telemetry/src/modules/macros/mod.rs @@ -0,0 +1 @@ +pub mod custom_anyhow; diff --git a/subcrates/telemetry/src/modules/memory.rs b/subcrates/telemetry/src/modules/memory.rs new file mode 100644 index 0000000..0dd39b0 --- /dev/null +++ b/subcrates/telemetry/src/modules/memory.rs @@ -0,0 +1,49 @@ +use crate::modules::FmtBytes; +use serde::Serialize; +use serde_json::{Value, json}; +use sysinfo::System; + +#[derive(Debug, Serialize)] +pub struct PhysicalInfo { + total: FmtBytes, + used: FmtBytes, + free: FmtBytes, + available: FmtBytes, +} + +#[derive(Debug, Serialize)] +pub struct VirtualInfo { + total: FmtBytes, + used: FmtBytes, + free: FmtBytes, +} + +#[derive(Debug, Serialize)] +pub struct MemoryInfo { + physical: PhysicalInfo, + virtual_swap: VirtualInfo, +} + +#[allow(dead_code)] +pub fn get_struct() -> MemoryInfo { + let system = System::new_all(); + + MemoryInfo { + physical: PhysicalInfo { + total: FmtBytes(system.total_memory()), + used: FmtBytes(system.used_memory()), + free: FmtBytes(system.free_memory()), + available: FmtBytes(system.available_memory()), + }, + virtual_swap: VirtualInfo { + total: FmtBytes(system.total_swap()), + used: FmtBytes(system.used_swap()), + free: FmtBytes(system.free_swap()), + }, + } +} + +#[allow(dead_code)] +pub fn get_json() -> Value { + json!(get_struct()) +} diff --git a/subcrates/telemetry/src/modules/meta.rs b/subcrates/telemetry/src/modules/meta.rs new file mode 100644 index 0000000..c0a91f2 --- /dev/null +++ b/subcrates/telemetry/src/modules/meta.rs @@ -0,0 +1,33 @@ +use crate::{custom_anyhow, modules::FmtVersion}; +use anyhow::Result; +use serde::Serialize; +use serde_json::{Value, json}; +use sys_locale::get_locale; +use versions::Versioning; + +include!(concat!(env!("OUT_DIR"), "/built.rs")); + +#[derive(Debug, Serialize)] +pub struct MetaInfo { + version: FmtVersion, + commit_hash: String, + locale: String, +} + +#[allow(dead_code)] +pub fn get_struct() -> Result { + let locale = get_locale().unwrap_or_else(|| String::from("en-US")); + let version = Versioning::new(env!("CARGO_PKG_VERSION")) + .ok_or(custom_anyhow!("Invalid version").context("Invalid Semver version"))?; + + Ok(MetaInfo { + version: FmtVersion(version), + commit_hash: GIT_COMMIT_HASH.to_string(), + locale, + }) +} + +#[allow(dead_code)] +pub fn get_json() -> Result { + Ok(json!(get_struct()?)) +} diff --git a/subcrates/telemetry/src/modules/mod.rs b/subcrates/telemetry/src/modules/mod.rs new file mode 100644 index 0000000..2d3ade2 --- /dev/null +++ b/subcrates/telemetry/src/modules/mod.rs @@ -0,0 +1,42 @@ +use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; + +pub mod cpu; +pub mod devices; +pub mod external; +pub mod formatted; +pub mod gpu; +pub mod macros; +pub mod memory; +pub mod meta; +pub mod network; +pub mod os; +pub mod storage; +pub mod uptime; + +#[allow(unused_imports)] +pub use formatted::{ + architecture::FmtOSArchitecture, + bytes::FmtBytes, + date_time::{FmtDateTime, IntoDateTime}, + desktop_environment::FmtDE, + offset_time::FmtOffsetTime, + relative_time::FmtRelativeTime, + version::FmtVersion, +}; + +#[derive(Debug)] +pub enum Defaults { + Unknown, +} + +impl Display for Defaults { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "{:?}", self) + } +} + +impl From for String { + fn from(value: Defaults) -> Self { + value.to_string() + } +} diff --git a/subcrates/telemetry/src/modules/network.rs b/subcrates/telemetry/src/modules/network.rs new file mode 100644 index 0000000..4d8dc47 --- /dev/null +++ b/subcrates/telemetry/src/modules/network.rs @@ -0,0 +1,25 @@ +use serde::Serialize; +use serde_json::{Value, json}; +use sysinfo::Networks; + +#[derive(Debug, Serialize)] +pub struct NetworkInfo { + adapters: Vec, +} + +#[allow(dead_code)] +pub fn get_struct() -> NetworkInfo { + let networks = Networks::new_with_refreshed_list(); + let mut adapters = Vec::new(); + + for (interface_name, _network_data) in &networks { + adapters.push(interface_name.to_string()); + } + + NetworkInfo { adapters } +} + +#[allow(dead_code)] +pub fn get_json() -> Value { + json!(get_struct()) +} diff --git a/subcrates/telemetry/src/modules/os.rs b/subcrates/telemetry/src/modules/os.rs new file mode 100644 index 0000000..dd3a4e2 --- /dev/null +++ b/subcrates/telemetry/src/modules/os.rs @@ -0,0 +1,54 @@ +use super::Defaults::Unknown; +use crate::{ + custom_anyhow, + modules::{FmtDE, FmtOSArchitecture}, +}; +use anyhow::Result; +use serde::Serialize; +use serde_json::{Value, json}; +use sysinfo::System; + +#[derive(Debug, Serialize)] +pub struct OSInfo { + name: String, + edition: String, + version: String, + architecture: FmtOSArchitecture, + kernel: Option, + desktop_environment: Option, +} + +#[allow(dead_code)] +pub fn get_struct() -> Result { + let architecture = if cfg!(target_pointer_width = "64") { + Ok(FmtOSArchitecture::W64_BIT) + } else if cfg!(target_pointer_width = "32") { + Ok(FmtOSArchitecture::W32_BIT) + } else { + Err(custom_anyhow!("Unsupported pointer width").context("16 bit systems are not supported")) + }?; + let kernel = if cfg!(target_os = "linux") || cfg!(target_os = "macos") { + Some(System::kernel_long_version()) + } else { + None + }; + let desktop_environment = if cfg!(target_os = "linux") { + FmtDE::detect() + } else { + None + }; + + Ok(OSInfo { + name: System::name().unwrap_or(Unknown.into()), + edition: System::long_os_version().unwrap_or(Unknown.into()), + version: System::os_version().unwrap_or(Unknown.into()), + architecture, + kernel, + desktop_environment, + }) +} + +#[allow(dead_code)] +pub fn get_json() -> Result { + Ok(json!(get_struct()?)) +} diff --git a/subcrates/telemetry/src/modules/storage.rs b/subcrates/telemetry/src/modules/storage.rs new file mode 100644 index 0000000..62c9a23 --- /dev/null +++ b/subcrates/telemetry/src/modules/storage.rs @@ -0,0 +1,46 @@ +use crate::{custom_anyhow, modules::FmtBytes}; +use anyhow::Result; +use serde::Serialize; +use serde_json::{Value, json}; +pub use sysinfo::DiskKind; +use sysinfo::Disks; + +#[derive(Debug, Serialize)] +pub struct StorageInfo { + name: String, + mount_point: String, + disk_kind: DiskKind, + space_left: FmtBytes, + total: FmtBytes, +} + +#[allow(dead_code)] +pub fn get_list() -> Result> { + let disks = Disks::new_with_refreshed_list(); + let mut drive_data = Vec::new(); + + for disk in disks.list() { + drive_data.push(StorageInfo { + name: format!("{:?}", disk.name()).trim_matches('\"').to_string(), + mount_point: format!("{:?}", disk.mount_point()) + .replace("\\", "") + .trim_matches('\"') + .to_string(), + disk_kind: disk.kind(), + space_left: FmtBytes(disk.available_space()), + total: FmtBytes(disk.total_space()), + }); + } + + if drive_data.is_empty() { + return Err(custom_anyhow!("No storage drives found") + .context("Drive_data is empty, expected disks.list() to return non-zero at line 39")); + } + + Ok(drive_data) +} + +#[allow(dead_code)] +pub fn get_json() -> Result { + Ok(json!(get_list()?)) +} diff --git a/subcrates/telemetry/src/modules/uptime.rs b/subcrates/telemetry/src/modules/uptime.rs new file mode 100644 index 0000000..1c0dbe6 --- /dev/null +++ b/subcrates/telemetry/src/modules/uptime.rs @@ -0,0 +1,54 @@ +use crate::{ + custom_anyhow, + modules::{FmtDateTime, FmtOffsetTime, FmtRelativeTime}, +}; +use anyhow::Result; +use chrono::{DateTime, Local, Utc}; +use serde::Serialize; +use serde_json::{Value, json}; +use std::time::Duration as StdDuration; +use sysinfo::System; + +#[derive(Debug, Serialize)] +struct DateInfo { + time_offset: FmtOffsetTime, + local_date_time: FmtDateTime, + utc_date_time: FmtDateTime, +} + +#[derive(Debug, Serialize)] +pub struct UptimeInfo { + boot: DateInfo, + now: DateInfo, + relative: FmtRelativeTime, +} + +#[allow(dead_code)] +pub fn get_struct() -> Result { + let boot_time_utc = DateTime::::from_timestamp(System::boot_time() as i64, 0).ok_or( + custom_anyhow!("Invalid, or out of range timestamp") + .context("Invalid timestamp: check seconds < 8_220_000_000_000"), + )?; + let boot_time_local: DateTime = boot_time_utc.with_timezone(&Local); + let relative_time = + StdDuration::from_secs((Local::now() - boot_time_local).num_seconds() as u64); + + Ok(UptimeInfo { + boot: DateInfo { + time_offset: FmtOffsetTime(Local::now()), + local_date_time: FmtDateTime(boot_time_local), + utc_date_time: FmtDateTime(boot_time_utc), + }, + now: DateInfo { + time_offset: FmtOffsetTime(Local::now()), + local_date_time: FmtDateTime(Local::now()), + utc_date_time: FmtDateTime(Utc::now()), + }, + relative: FmtRelativeTime(relative_time), + }) +} + +#[allow(dead_code)] +pub fn get_json() -> Result { + Ok(json!(get_struct()?)) +} diff --git a/subcrates/telemetry/src/territories.json b/subcrates/telemetry/src/territories.json deleted file mode 100644 index a062735..0000000 --- a/subcrates/telemetry/src/territories.json +++ /dev/null @@ -1,318 +0,0 @@ -{ - "001": "world", - "002": "Africa", - "003": "North America", - "005": "South America", - "009": "Oceania", - "011": "Western Africa", - "013": "Central America", - "014": "Eastern Africa", - "015": "Northern Africa", - "017": "Middle Africa", - "018": "Southern Africa", - "019": "Americas", - "021": "Northern America", - "029": "Caribbean", - "030": "Eastern Asia", - "034": "Southern Asia", - "035": "Southeast Asia", - "039": "Southern Europe", - "053": "Australasia", - "054": "Melanesia", - "057": "Micronesian Region", - "061": "Polynesia", - "142": "Asia", - "143": "Central Asia", - "145": "Western Asia", - "150": "Europe", - "151": "Eastern Europe", - "154": "Northern Europe", - "155": "Western Europe", - "202": "Sub-Saharan Africa", - "419": "Latin America", - "AC": "Ascension Island", - "AD": "Andorra", - "AE": "United Arab Emirates", - "AF": "Afghanistan", - "AG": "Antigua & Barbuda", - "AI": "Anguilla", - "AL": "Albania", - "AM": "Armenia", - "AO": "Angola", - "AQ": "Antarctica", - "AR": "Argentina", - "AS": "American Samoa", - "AT": "Austria", - "AU": "Australia", - "AW": "Aruba", - "AX": "Åland Islands", - "AZ": "Azerbaijan", - "BA": "Bosnia & Herzegovina", - "BA-alt-short": "Bosnia", - "BB": "Barbados", - "BD": "Bangladesh", - "BE": "Belgium", - "BF": "Burkina Faso", - "BG": "Bulgaria", - "BH": "Bahrain", - "BI": "Burundi", - "BJ": "Benin", - "BL": "St Barthélemy", - "BM": "Bermuda", - "BN": "Brunei", - "BO": "Bolivia", - "BQ": "Caribbean Netherlands", - "BR": "Brazil", - "BS": "Bahamas", - "BT": "Bhutan", - "BV": "Bouvet Island", - "BW": "Botswana", - "BY": "Belarus", - "BZ": "Belize", - "CA": "Canada", - "CC": "Cocos (Keeling) Islands", - "CC-alt-short": "Cocos Islands", - "CD": "Congo - Kinshasa", - "CD-alt-variant": "Congo (DRC)", - "CF": "Central African Republic", - "CG": "Congo - Brazzaville", - "CG-alt-variant": "Congo (Republic)", - "CH": "Switzerland", - "CI": "Côte d’Ivoire", - "CI-alt-variant": "Ivory Coast", - "CK": "Cook Islands", - "CL": "Chile", - "CM": "Cameroon", - "CN": "China", - "CO": "Colombia", - "CP": "Clipperton Island", - "CQ": "Sark", - "CR": "Costa Rica", - "CU": "Cuba", - "CV": "Cape Verde", - "CV-alt-variant": "Cabo Verde", - "CW": "Curaçao", - "CX": "Christmas Island", - "CY": "Cyprus", - "CZ": "Czechia", - "CZ-alt-variant": "Czech Republic", - "DE": "Germany", - "DG": "Diego Garcia", - "DJ": "Djibouti", - "DK": "Denmark", - "DM": "Dominica", - "DO": "Dominican Republic", - "DZ": "Algeria", - "EA": "Ceuta & Melilla", - "EC": "Ecuador", - "EE": "Estonia", - "EG": "Egypt", - "EH": "Western Sahara", - "ER": "Eritrea", - "ES": "Spain", - "ET": "Ethiopia", - "EU": "European Union", - "EZ": "Eurozone", - "FI": "Finland", - "FJ": "Fiji", - "FK": "Falkland Islands", - "FK-alt-variant": "Falkland Islands (Islas Malvinas)", - "FM": "Micronesia", - "FO": "Faroe Islands", - "FR": "France", - "GA": "Gabon", - "GB": "United Kingdom", - "GB-alt-short": "UK", - "GD": "Grenada", - "GE": "Georgia", - "GF": "French Guiana", - "GG": "Guernsey", - "GH": "Ghana", - "GI": "Gibraltar", - "GL": "Greenland", - "GM": "Gambia", - "GN": "Guinea", - "GP": "Guadeloupe", - "GQ": "Equatorial Guinea", - "GR": "Greece", - "GS": "South Georgia & South Sandwich Islands", - "GT": "Guatemala", - "GU": "Guam", - "GW": "Guinea-Bissau", - "GY": "Guyana", - "HK": "Hong Kong SAR China", - "HK-alt-short": "Hong Kong", - "HM": "Heard & McDonald Islands", - "HN": "Honduras", - "HR": "Croatia", - "HT": "Haiti", - "HU": "Hungary", - "IC": "Canary Islands", - "ID": "Indonesia", - "IE": "Ireland", - "IL": "Israel", - "IM": "Isle of Man", - "IN": "India", - "IO": "British Indian Ocean Territory", - "IO-alt-chagos": "Chagos Archipelago", - "IQ": "Iraq", - "IR": "Iran", - "IS": "Iceland", - "IT": "Italy", - "JE": "Jersey", - "JM": "Jamaica", - "JO": "Jordan", - "JP": "Japan", - "KE": "Kenya", - "KG": "Kyrgyzstan", - "KH": "Cambodia", - "KI": "Kiribati", - "KM": "Comoros", - "KN": "St Kitts & Nevis", - "KP": "North Korea", - "KR": "South Korea", - "KW": "Kuwait", - "KY": "Cayman Islands", - "KZ": "Kazakhstan", - "LA": "Laos", - "LB": "Lebanon", - "LC": "St Lucia", - "LI": "Liechtenstein", - "LK": "Sri Lanka", - "LR": "Liberia", - "LS": "Lesotho", - "LT": "Lithuania", - "LU": "Luxembourg", - "LV": "Latvia", - "LY": "Libya", - "MA": "Morocco", - "MC": "Monaco", - "MD": "Moldova", - "ME": "Montenegro", - "MF": "St Martin", - "MG": "Madagascar", - "MH": "Marshall Islands", - "MK": "North Macedonia", - "ML": "Mali", - "MM": "Myanmar (Burma)", - "MM-alt-short": "Myanmar", - "MN": "Mongolia", - "MO": "Macao SAR China", - "MO-alt-short": "Macao", - "MP": "Northern Mariana Islands", - "MQ": "Martinique", - "MR": "Mauritania", - "MS": "Montserrat", - "MT": "Malta", - "MU": "Mauritius", - "MV": "Maldives", - "MW": "Malawi", - "MX": "Mexico", - "MY": "Malaysia", - "MZ": "Mozambique", - "NA": "Namibia", - "NC": "New Caledonia", - "NE": "Niger", - "NF": "Norfolk Island", - "NG": "Nigeria", - "NI": "Nicaragua", - "NL": "Netherlands", - "NO": "Norway", - "NP": "Nepal", - "NR": "Nauru", - "NU": "Niue", - "NZ": "New Zealand", - "NZ-alt-variant": "Aotearoa New Zealand", - "OM": "Oman", - "PA": "Panama", - "PE": "Peru", - "PF": "French Polynesia", - "PG": "Papua New Guinea", - "PH": "Philippines", - "PK": "Pakistan", - "PL": "Poland", - "PM": "St Pierre & Miquelon", - "PN": "Pitcairn Islands", - "PN-alt-short": "Pitcairn", - "PR": "Puerto Rico", - "PS": "Palestinian Territories", - "PS-alt-short": "Palestine", - "PT": "Portugal", - "PW": "Palau", - "PY": "Paraguay", - "QA": "Qatar", - "QO": "Outlying Oceania", - "RE": "Réunion", - "RO": "Romania", - "RS": "Serbia", - "RU": "Russia", - "RW": "Rwanda", - "SA": "Saudi Arabia", - "SB": "Solomon Islands", - "SC": "Seychelles", - "SD": "Sudan", - "SE": "Sweden", - "SG": "Singapore", - "SH": "St Helena", - "SI": "Slovenia", - "SJ": "Svalbard & Jan Mayen", - "SK": "Slovakia", - "SL": "Sierra Leone", - "SM": "San Marino", - "SN": "Senegal", - "SO": "Somalia", - "SR": "Suriname", - "SS": "South Sudan", - "ST": "São Tomé & Príncipe", - "SV": "El Salvador", - "SX": "Sint Maarten", - "SY": "Syria", - "SZ": "Eswatini", - "SZ-alt-variant": "Swaziland", - "TA": "Tristan da Cunha", - "TC": "Turks & Caicos Islands", - "TD": "Chad", - "TF": "French Southern Territories", - "TG": "Togo", - "TH": "Thailand", - "TJ": "Tajikistan", - "TK": "Tokelau", - "TL": "Timor-Leste", - "TL-alt-variant": "East Timor", - "TM": "Turkmenistan", - "TN": "Tunisia", - "TO": "Tonga", - "TR": "Türkiye", - "TR-alt-variant": "Turkey", - "TT": "Trinidad & Tobago", - "TV": "Tuvalu", - "TW": "Taiwan", - "TZ": "Tanzania", - "UA": "Ukraine", - "UG": "Uganda", - "UM": "US Outlying Islands", - "UN": "United Nations", - "UN-alt-short": "UN", - "US": "United States", - "US-alt-short": "US", - "UY": "Uruguay", - "UZ": "Uzbekistan", - "VA": "Vatican City", - "VC": "St Vincent & the Grenadines", - "VE": "Venezuela", - "VG": "British Virgin Islands", - "VI": "US Virgin Islands", - "VN": "Vietnam", - "VU": "Vanuatu", - "WF": "Wallis & Futuna", - "WS": "Samoa", - "XA": "Pseudo-Accents", - "XB": "Pseudo-Bidi", - "XK": "Kosovo", - "YE": "Yemen", - "YT": "Mayotte", - "ZA": "South Africa", - "ZM": "Zambia", - "ZW": "Zimbabwe", - "ZZ": "Unknown Region" - } \ No newline at end of file