{"id":16405557,"url":"https://github.com/gunnarmorrigan/mqrstt","last_synced_at":"2025-04-06T09:06:48.540Z","repository":{"id":108791458,"uuid":"558623827","full_name":"GunnarMorrigan/mqrstt","owner":"GunnarMorrigan","description":"Pure rust async MQTTv5 client","archived":false,"fork":false,"pushed_at":"2024-12-06T21:43:14.000Z","size":728,"stargazers_count":40,"open_issues_count":1,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-06T09:06:44.342Z","etag":null,"topics":["async","iot","mqtt","mqtt-client","mqttv5","mqttv5-client","network-programming","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GunnarMorrigan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-10-27T23:47:15.000Z","updated_at":"2025-03-22T04:45:04.000Z","dependencies_parsed_at":null,"dependency_job_id":"4abd3867-aff1-4e63-b59d-7b5b90190a8c","html_url":"https://github.com/GunnarMorrigan/mqrstt","commit_stats":{"total_commits":254,"total_committers":3,"mean_commits":84.66666666666667,"dds":"0.17322834645669294","last_synced_commit":"7ef2413611635be2181b668405be74d6880632e9"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GunnarMorrigan%2Fmqrstt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GunnarMorrigan%2Fmqrstt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GunnarMorrigan%2Fmqrstt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GunnarMorrigan%2Fmqrstt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GunnarMorrigan","download_url":"https://codeload.github.com/GunnarMorrigan/mqrstt/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247457799,"owners_count":20941906,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","iot","mqtt","mqtt-client","mqttv5","mqttv5-client","network-programming","rust"],"created_at":"2024-10-11T06:06:18.520Z","updated_at":"2025-04-06T09:06:48.511Z","avatar_url":"https://github.com/GunnarMorrigan.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\r\n\r\n# `📟 MQRSTT`\r\n\r\n[![Crates.io](https://img.shields.io/crates/v/mqrstt.svg)](https://crates.io/crates/mqrstt)\r\n[![Docs](https://docs.rs/mqrstt/badge.svg)](https://docs.rs/mqrstt)\r\n[![dependency status](https://deps.rs/repo/github/GunnarMorrigan/mqrstt/status.svg)](https://deps.rs/repo/github/GunnarMorrigan/mqrstt)\r\n[![codecov](https://codecov.io/github/GunnarMorrigan/mqrstt/branch/main/graph/badge.svg)](https://app.codecov.io/gh/GunnarMorrigan/mqrstt)\r\n\r\n`MQRSTT` is an MQTTv5 client that provides sync and async (smol and tokio) implementation.\r\n\r\nBecause this crate aims to be runtime agnostic the user is required to provide their own data stream.\r\nThe stream has to implement the smol or tokio [`AsyncRead`] and [`AsyncWrite`] traits.\r\n\r\n\u003c/div\u003e\r\n\r\n## Features\r\n- MQTT v5\r\n- Runtime agnostic (Smol, Tokio)\r\n- TLS/TCP\r\n- Lean\r\n- Keep alive depends on actual communication\r\n- This tokio implemention has been fuzzed using cargo-fuzz!\r\n  \r\n### To do\r\n- Even More testing\r\n- Add TLS examples to repository\r\n\r\n## MSRV\r\nFrom 0.3 the tokio and smol variants will require MSRV: 1.75 due to async fn in trait feature.\r\n\r\n## TCP \u0026 TLS Examples\r\n\r\n### Notes:\r\n- Your handler should not wait too long\r\n- Create a new connection when an error or disconnect is encountered\r\n- Handlers only get incoming packets\r\n\r\n\r\n### Smol example:\r\n```rust\r\nuse mqrstt::{\r\n    packets::{self, Packet},\r\n    AsyncEventHandler, MqttClient, NetworkBuilder, NetworkStatus,\r\n};\r\npub struct PingPong {\r\n    pub client: MqttClient,\r\n}\r\nimpl AsyncEventHandler for PingPong {\r\n    // Handlers only get INCOMING packets. This can change later.\r\n    async fn handle(\u0026mut self, event: packets::Packet) {\r\n        match event {\r\n            Packet::Publish(p) =\u003e {\r\n                if let Ok(payload) = String::from_utf8(p.payload.to_vec()) {\r\n                    if payload.to_lowercase().contains(\"ping\") {\r\n                        self.client.publish(p.topic.clone(), p.qos, p.retain, b\"pong\").await.unwrap();\r\n                        println!(\"Received Ping, Send pong!\");\r\n                    }\r\n                }\r\n            }\r\n            Packet::ConnAck(_) =\u003e {\r\n                println!(\"Connected!\")\r\n            }\r\n            _ =\u003e (),\r\n        }\r\n    }\r\n}\r\nfn main() {\r\n    smol::block_on(async {\r\n        let (mut network, client) = NetworkBuilder::new_from_client_id(\"mqrsttSmolExample\").smol_network();\r\n        let stream = smol::net::TcpStream::connect((\"broker.emqx.io\", 1883)).await.unwrap();\r\n\r\n        let mut pingpong = PingPong { client: client.clone() };\r\n\r\n        network.connect(stream, \u0026mut pingpong).await.unwrap();\r\n\r\n        // This subscribe is only processed when we run the network\r\n        client.subscribe(\"mqrstt\").await.unwrap();\r\n\r\n        let task_handle = smol::spawn(async move {\r\n            let result = network.run(\u0026mut pingpong).await;\r\n            (result, pingpong)\r\n        });\r\n\r\n        smol::Timer::after(std::time::Duration::from_secs(30)).await;\r\n        client.disconnect().await.unwrap();\r\n\r\n        let (result, _pingpong) = task_handle.await;\r\n\r\n        assert!(result.is_ok());\r\n        assert_eq!(result.unwrap(), NetworkStatus::OutgoingDisconnect);\r\n    });\r\n}\r\n```\r\n\r\n### Tokio example:\r\n```rust\r\nuse mqrstt::{\r\n    packets::{self, Packet},\r\n    AsyncEventHandler, MqttClient, NetworkBuilder, NetworkStatus,\r\n};\r\nuse tokio::time::Duration;\r\n\r\npub struct PingPong {\r\n    pub client: MqttClient,\r\n}\r\nimpl AsyncEventHandler for PingPong {\r\n    // Handlers only get INCOMING packets.\r\n    async fn handle(\u0026mut self, event: packets::Packet) {\r\n        match event {\r\n            Packet::Publish(p) =\u003e {\r\n                if let Ok(payload) = String::from_utf8(p.payload.to_vec()) {\r\n                    if payload.to_lowercase().contains(\"ping\") {\r\n                        self.client.publish(p.topic.clone(), p.qos, p.retain, b\"pong\").await.unwrap();\r\n                        println!(\"Received Ping, Send pong!\");\r\n                    }\r\n                }\r\n            }\r\n            Packet::ConnAck(_) =\u003e {\r\n                println!(\"Connected!\")\r\n            }\r\n            _ =\u003e (),\r\n        }\r\n    }\r\n}\r\n\r\n#[tokio::main]\r\nasync fn main() {\r\n    let (mut network, client) = NetworkBuilder::new_from_client_id(\"TokioTcpPingPongExample\").tokio_network();\r\n\r\n    let stream = tokio::net::TcpStream::connect((\"broker.emqx.io\", 1883)).await.unwrap();\r\n    let stream = tokio::io::BufStream::new(stream);\r\n\r\n    let mut pingpong = PingPong { client: client.clone() };\r\n\r\n    network.connect(stream, \u0026mut pingpong).await.unwrap();\r\n\r\n    client.subscribe(\"mqrstt\").await.unwrap();\r\n\r\n    let network_handle = tokio::spawn(async move {\r\n        let result = network.run(\u0026mut pingpong).await;\r\n        (result, pingpong)\r\n    });\r\n\r\n    tokio::time::sleep(Duration::from_secs(30)).await;\r\n    client.disconnect().await.unwrap();\r\n\r\n    let (result, _pingpong) = network_handle.await.unwrap();\r\n    assert!(result.is_ok());\r\n    assert_eq!(result.unwrap(), NetworkStatus::OutgoingDisconnect);\r\n}\r\n\r\n```\r\n\r\n## FAQ\r\n  - Not much gets frequently asked, so please do! :)\r\n  - Open to feature requests\r\n\r\n## License\r\nLicensed under\r\n* Mozilla Public License, Version 2.0, [(MPL-2.0)](https://choosealicense.com/licenses/mpl-2.0/)\r\n\r\n## Contribution\r\nUnless you explicitly state otherwise, any contribution intentionally\r\nsubmitted for inclusion in the work by you, shall be licensed under MPL-2.0, without any additional terms or\r\nconditions.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgunnarmorrigan%2Fmqrstt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgunnarmorrigan%2Fmqrstt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgunnarmorrigan%2Fmqrstt/lists"}