Turned the repo into a Cargo workspace, with r701 as a member
Check, format and test / build (push) Successful in 44s

Moving the library into its own r701/ directory makes room for the
Tauri GUI client (Oxibadge) to be added as a sibling workspace member
in a following commit. edition/license/repository are now inherited
from the new workspace root instead of being duplicated per member.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-10-06 12:49:54 +02:00
co-authored by Claude Sonnet 5
parent c2471ed548
commit cc8181ebdc
10 changed files with 112 additions and 78 deletions
+5 -25
View File
@@ -1,28 +1,8 @@
[package] [workspace]
name = "r701" resolver = "3"
version = "0.2.0" members = ["r701"]
[workspace.package]
edition = "2021" edition = "2021"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
repository = "https://git.nicolabelluti.me/nicolabelluti/r701" repository = "https://git.nicolabelluti.me/nicolabelluti/r701"
[dependencies]
async-stream = "0.3.6"
chrono = { version = "0.4.38", default-features = false, features = ["clock"] }
serde = { version = "1.0.203", default-features = false, features = ["derive"], optional = true }
tokio = { version = "1.53.1", default-features = false, features = ["net", "io-util", "sync"] }
tokio-stream = { version = "0.1.19", default-features = false }
[features]
serde = ["chrono/serde", "dep:serde"]
[lints.rust]
unsafe_code = "forbid"
[lints.clippy]
unwrap_used = "deny"
enum_glob_use = { level = "deny", priority = 1 }
pedantic = { level = "deny", priority = -1 }
nursery = { level = "deny", priority = -1 }
[dev-dependencies]
tokio = { version = "1.53.1", features = ["rt", "macros", "net"] }
+11 -53
View File
@@ -1,6 +1,6 @@
<div align="center"> <div align="center">
# R701 🕰️ # R701 | Oxibadge
[![Rust](https://img.shields.io/badge/Rust-f74c00?logo=rust)](https://www.rust-lang.org) [![Rust](https://img.shields.io/badge/Rust-f74c00?logo=rust)](https://www.rust-lang.org)
[![Claude Code](https://img.shields.io/badge/Claude%20Code-grey?logo=claude&logoColor=d97757)](https://claude.com/product/claude-code) [![Claude Code](https://img.shields.io/badge/Claude%20Code-grey?logo=claude&logoColor=d97757)](https://claude.com/product/claude-code)
@@ -11,58 +11,16 @@
</div><br> </div><br>
> A reverse-engineered library to communicate with the > A reverse-engineered client for the
> [R701](https://ipsattendant.it/rilevatore-presenze-r701/) by [I.P.S. > [R701](https://ipsattendant.it/rilevatore-presenze-r701/) attendance reader by
> Informatica](https://ipsinformatica.info/), written in Rust > [I.P.S. Informatica](https://ipsinformatica.info/)
If you want to read about this reverse engineering attempt, check out This repository is a Cargo workspace with two members:
<https://nicolabelluti.me/series/attendance-reader/>.
## Library Usage - **[`r701/`](r701/README.md)** — the async Rust library that speaks the
device's TCP protocol.
- **`oxibadge/`** — Oxibadge, a minimal Tauri + Svelte desktop client built on
top of the library. *(Coming soon.)*
1. Add the library to you project: If you want to read about the reverse engineering attempt behind the library,
check out <https://nicolabelluti.me/series/attendance-reader/>.
```shell
cargo add r701 --git https://git.nicolabelluti.me/nicolabelluti/r701.git
```
2. Use the library in you project:
This library is async and built on [tokio](https://tokio.rs); add it to your
own `[dependencies]` (e.g. `tokio = { version = "1", features =
["rt-multi-thread", "macros"] }`) to run the example below.
```rust
use r701::R701;
use tokio_stream::StreamExt;
#[tokio::main]
async fn main() {
let r701 = R701::connect("127.0.0.1:5005").await.unwrap();
let mut record_stream = r701.into_record_iter().await.unwrap();
let mut id = 0;
println!("No\tMchn\tEnNo\t\tName\t\tMode\tIOMd\tDateTime\t");
while let Some(record) = record_stream.next().await {
let record = record.unwrap();
let name = match r701.get_name(record.employee_id).await.unwrap() {
Some(Ok(name)) => name,
Some(Err(error)) => String::from_utf8_lossy(error.as_bytes()).into_owned(),
None => format!("user #{}", record.employee_id),
};
println!(
"{:0>6}\t{}\t{:0>9}\t{: <10}\t{}\t{}\t{}",
id + 1,
1,
record.employee_id,
name,
35,
record.clock as u8,
record.datetime.format("%Y/%m/%d %H:%M:%S"),
);
id += 1;
}
}
```
+28
View File
@@ -0,0 +1,28 @@
[package]
name = "r701"
version = "0.2.0"
edition.workspace = true
license.workspace = true
repository.workspace = true
[dependencies]
async-stream = "0.3.6"
chrono = { version = "0.4.38", default-features = false, features = ["clock"] }
serde = { version = "1.0.203", default-features = false, features = ["derive"], optional = true }
tokio = { version = "1.53.1", default-features = false, features = ["net", "io-util", "sync"] }
tokio-stream = { version = "0.1.19", default-features = false }
[features]
serde = ["chrono/serde", "dep:serde"]
[lints.rust]
unsafe_code = "forbid"
[lints.clippy]
unwrap_used = "deny"
enum_glob_use = { level = "deny", priority = 1 }
pedantic = { level = "deny", priority = -1 }
nursery = { level = "deny", priority = -1 }
[dev-dependencies]
tokio = { version = "1.53.1", features = ["rt", "macros", "net"] }
+68
View File
@@ -0,0 +1,68 @@
<div align="center">
# R701 🕰️
[![Rust](https://img.shields.io/badge/Rust-f74c00?logo=rust)](https://www.rust-lang.org)
[![Claude Code](https://img.shields.io/badge/Claude%20Code-grey?logo=claude&logoColor=d97757)](https://claude.com/product/claude-code)
[![GNU AGPLv3.0 License](https://img.shields.io/badge/License-GNU%20AGPLv3.0-dark_green?logo=gnu)](https://choosealicense.com/licenses/agpl-3.0)
[![Buymeacoffee](https://img.shields.io/badge/Buymeacoffee-gray?logo=buymeacoffee)](https://buymeacoffee.com/nicolabelluti)
<br>
[![CI Badge](https://git.nicolabelluti.me/nicolabelluti/r701/actions/workflows/check-format-and-test.yaml/badge.svg)](https://git.nicolabelluti.me/nicolabelluti/r701/actions/?workflow=check-format-and-test.yaml)
</div><br>
> A reverse-engineered library to communicate with the
> [R701](https://ipsattendant.it/rilevatore-presenze-r701/) by [I.P.S.
> Informatica](https://ipsinformatica.info/), written in Rust
If you want to read about this reverse engineering attempt, check out
<https://nicolabelluti.me/series/attendance-reader/>.
## Library Usage
1. Add the library to you project:
```shell
cargo add r701 --git https://git.nicolabelluti.me/nicolabelluti/r701.git
```
2. Use the library in you project:
This library is async and built on [tokio](https://tokio.rs); add it to your
own `[dependencies]` (e.g. `tokio = { version = "1", features =
["rt-multi-thread", "macros"] }`) to run the example below.
```rust
use r701::R701;
use tokio_stream::StreamExt;
#[tokio::main]
async fn main() {
let r701 = R701::connect("127.0.0.1:5005").await.unwrap();
let mut record_stream = r701.into_record_iter().await.unwrap();
let mut id = 0;
println!("No\tMchn\tEnNo\t\tName\t\tMode\tIOMd\tDateTime\t");
while let Some(record) = record_stream.next().await {
let record = record.unwrap();
let name = match r701.get_name(record.employee_id).await.unwrap() {
Some(Ok(name)) => name,
Some(Err(error)) => String::from_utf8_lossy(error.as_bytes()).into_owned(),
None => format!("user #{}", record.employee_id),
};
println!(
"{:0>6}\t{}\t{:0>9}\t{: <10}\t{}\t{}\t{}",
id + 1,
1,
record.employee_id,
name,
35,
record.clock as u8,
record.datetime.format("%Y/%m/%d %H:%M:%S"),
);
id += 1;
}
}
```
View File
View File
View File