Compare commits

...

4 Commits

Author SHA1 Message Date
32595d41bb Swapped longest into its declarative form.
The function was ment to use the functional programming style, however,
I forgot to uncomment and swap out the imperative style.
2025-07-02 15:39:19 -04:00
d16f8e333e Made the jobs do something.
Also, stamped the advanced async file with the license.
2025-06-28 18:00:11 -04:00
4b16a23712 Added some advanced async stuff.
Really it doesn't have channels so the only advanced thing was the
dynamic trait part. Pinning the futures is only real hard part to
this module. The rest does show trait implementations on structs though
which was something missing from the library before.

Also, the project was turned into a library and the foundational
examples were put into a basic module.
2025-06-28 16:52:04 -04:00
d395741822 Committing a set of basic rust examples.
This contains code and tests for several basic rust skills.
2025-06-27 15:55:37 -04:00
8 changed files with 1044 additions and 24 deletions

348
Cargo.lock generated Normal file
View File

@ -0,0 +1,348 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "backtrace"
version = "0.3.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets",
]
[[package]]
name = "bitflags"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "bytes"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cfg-if"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "example"
version = "1.0.0"
dependencies = [
"tokio",
]
[[package]]
name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "libc"
version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]]
name = "lock_api"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "memchr"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]
[[package]]
name = "mio"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
dependencies = [
"libc",
"wasi",
"windows-sys 0.59.0",
]
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]]
name = "parking_lot"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
dependencies = [
"bitflags",
]
[[package]]
name = "rustc-demangle"
version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "signal-hook-registry"
version = "1.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
dependencies = [
"libc",
]
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "socket2"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "syn"
version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tokio"
version = "1.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
dependencies = [
"backtrace",
"bytes",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.52.0",
]
[[package]]
name = "tokio-macros"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

View File

@ -1,6 +1,6 @@
[package]
name = "example"
version = "0.0.0"
version = "1.0.0"
edition = "2021"
description = "Just a small sample of Rust code."
repository = "/myrddin/example"
@ -11,3 +11,4 @@ license = "Apache-2.0"
[dependencies]
tokio = { version = "1.45.1", features = ["full", "test-util"] }

137
README.md
View File

@ -1,6 +1,137 @@
# example
# Example
Just a small sample of Rust code.
This repository demonstrates modern, idiomatic Rust through two parts:
1. **Foundational Rust Examples** covering core concepts like lifetimes,
error handling, async, and testing.
2. **Advanced Async Job Runner** a trait-based, concurrent task execution
framework built using `tokio`.
---
## Part 1: Foundational Rust Examples
A lightweight collection of examples demonstrating essential Rust features,
without relying on external dependencies (except `tokio`).
### Concepts Covered
- Arrays and Iteration
- Lifetimes and Borrowing
- Error Handling with `Result`, `?`, and custom messages
- Async Programming using `#[tokio::main]`
- File I/O with the standard library
- Unit and async testing
### Key Functions
| Function | Description |
|------------------|--------------------------------------------------------|
| `longest` | Returns the longest string in a slice without copying. |
| `read_and_parse` | Reads a file and parses its content into an integer. |
| `run_tasks` | Demonstrates spawning and awaiting async tasks. |
### Testing
Includes both synchronous and asynchronous unit tests under a
`#[cfg(test)]` module.
---
## Part 2: Advanced Async Job Runner
An extensible, asynchronous job system using:
- Trait objects
- Pinned futures (`Pin<Box<dyn Future>>`)
- Tokios `JoinSet` for concurrent execution
- Fully dynamic job management without macros
### Why It Matters
This system demonstrates:
- Manual implementation of async trait behavior (without `async_trait`)
- Advanced type management (`Box<dyn Trait>`, lifetimes, `Pin`)
- Idiomatic use of `tokio::task::JoinSet` for parallelism
### Architecture
#### `Job` Trait
```rust
pub trait Job: Send + Sync {
fn name(&self) -> &str;
fn run<'a>(&self) -> Pin<Box<dyn Future<Output = JobResult> + Send + 'a>>
where Self: Sync + 'a;
}
```
#### `JobRunner` Struct
```rust
pub struct JobRunner {
jobs: Vec<Box<dyn Job>>
}
```
Manages and runs jobs concurrently.
#### Built-In Jobs
| Job Type | Behavior |
|-----------|---------------------------|
| `FileJob` | (Planned) Reads from disk |
| `SleepJob`| Simulates a delay |
| `MathJob` | Performs a math task |
### Usage
```rust
let mut runner = JobRunner::new();
runner.add_job(FileJob::new());
runner.add_job(SleepJob::new());
runner.add_job(MathJob::new());
for (name, result) in runner.run_all().await {
match result {
Ok(msg) => println!("{name}: {msg}"),
Err(err) => eprintln!("{name}: {err}"),
}
}
```
---
## Testing & Extending
You can add unit or integration tests using mock jobs to simulate both success and failure.
Ideas for extension:
- Make the Jobs actually do something
- Interjob communication using channels
- Create a QueueRunner that will run jobs in sequence
- Create a ParallelRunner that will run jobs concurrently (simple rename)
- Create a system that allows QueueRunners and ParallelRunners to be combined
---
## Requirements
- Rust 1.76+ (for full language support)
- [Tokio 1.37+](https://docs.rs/tokio) (for async runtime)
---
## Project Structure
```text
src/
├── adv_async.rs # Advanced async JobRunner
├── basic.rs # Foundational examples
├── info.rs # Crate information from Cargo
├── lib.rs # Entry point for the library
```
@ -8,7 +139,7 @@ Just a small sample of Rust code.
## Copyright & License
Copyright 2025 CyberMages LLC
Copyright 2025 Jason Travis Smith
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this library except in compliance with the License.

332
src/adv_async.rs Normal file
View File

@ -0,0 +1,332 @@
// SPDX-License-Identifier: Apache-2.0
// Sealed with Magistamp.
//! An example job execution framework.
//!
//! It includes:
//! - A `Job` trait for defining asynchronous tasks.
//! - A `JobRunner` struct for collecting and executing multiple jobs.
//! - Concrete implementations of jobs (`FileJob`, `SleepJob`, `MathJob`).
//!
//! Jobs are run concurrently using Tokio, and results are gathered
//! with proper error handling.
use std::io::Write;
use std::pin::Pin;
use tokio::time::{sleep, Duration};
/// Run a predefined set of jobs asynchronously.
///
/// This function creates a `JobRunner`, adds three different jobs,
/// and executes them concurrently. Results are printed to stdout,
/// and any errors are reported to stderr.
pub async fn run_jobs()
{
let mut runner: JobRunner = JobRunner::new();
runner.add_job(FileJob::new());
runner.add_job(SleepJob::new());
runner.add_job(MathJob::new());
for result in runner.run_all().await.iter()
{
match &result.1
{
Ok(msg) =>
{
println!("{}: {}", result.0, msg);
}
Err(error) =>
{
eprintln!("{}: {}", result.0, error);
}
}
}
}
/// A Job will return this after running. The traits need to be Send
/// because they maybe sent between threads.
type JobResult = Result<String, Box<dyn std::error::Error + Send>>;
/// The Future task that Jobs will return that can later be run.
/// Here we Pin a heap allocated future so that it can be referenced safely.
/// The output is then set to the desired output of the function.
/// This is the may way to do 'async dyn traits'.
type PinnedFuture<'a> =
Pin<Box<dyn core::future::Future<Output = JobResult> + Send + 'a>>;
/// An async trait that can be used as a dyn trait.
pub trait Job: Send + Sync
{
/// Retrieve the name of the Job.
fn name(&self) -> &str;
/// Run the Job.
///
/// This function needs to be async.
/// Here we Pin a heap allocated future so that it can be referenced safely.
/// The output is then set to the desired output of the function.
///
/// Inside the implementation all you need to do is:
/// ```ignore
/// Box::pin(async move
/// {
/// // Place your functions code here.
/// })
/// ```
fn run<'a>(&self) -> PinnedFuture<'a>
where Self: Sync + 'a;
}
/// A struct to hold and execute multiple jobs.
pub struct JobRunner
{
jobs: Vec<Box<dyn Job>>
}
impl JobRunner
{
/// Create a new JobRunner with an empty job list.
pub fn new() -> Self
{
JobRunner
{
jobs: Vec::new()
}
}
/// Add a new job to the runner.
pub fn add_job(&mut self, job: impl Job + 'static)
{
self.jobs.push(Box::new(job));
}
/// Run all added jobs concurrently and collect their results.
///
/// Uses Tokio to spawn concurrent tasks for each job. After
/// completion, all jobs are cleared from the runner.
pub async fn run_all(&mut self) -> Vec<(String, JobResult)>
{
let tasks: tokio::task::JoinSet<(String, JobResult)> =
self.jobs
.iter()
.map(|j| {
let name = j.name().to_string();
let fut = j.run();
async move { (name, fut.await) }
})
.collect();
let result = tasks.join_all().await;
self.jobs.clear();
result
}
}
/// A dummy job simulating a file operation.
pub struct FileJob {}
impl FileJob
{
/// Create a new FileJob instance.
fn new() -> Self
{
FileJob
{
}
}
}
/// Implementation of the Job trait for FileJob.
impl Job for FileJob
{
/// Retrieve the name of the Job.
fn name(&self) -> &str
{
"File Job"
}
/// Run the File Job.
fn run<'a>(&self) -> PinnedFuture<'a>
where Self: Sync + 'a
{
Box::pin(async move {
let mut write_target = std::env::temp_dir();
write_target.push("file_job");
write_target.set_extension("txt");
let mut file = std::fs::File::create(write_target)
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send>)?;
file.write_all(b"Jason is an awesome programmer!!")
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send>)?;
Ok(String::from("File written"))
})
}
}
/// A dummy job simulating a sleep or delay operation.
pub struct SleepJob {}
impl SleepJob
{
/// Create a new SleepJob instance.
fn new() -> Self
{
SleepJob
{
}
}
}
/// Implementation of the Job trait for SleepJob.
impl Job for SleepJob
{
/// Retrieve the name of the Job.
fn name(&self) -> &str
{
"Sleep Job"
}
/// Run the Sleep Job.
fn run<'a>(&self) -> PinnedFuture<'a>
where Self: Sync + 'a
{
Box::pin(async move
{
sleep(Duration::from_millis(500)).await;
Ok(String::from("Zzzzzzzzzzz"))
})
}
}
/// A dummy job simulating a math operation.
pub struct MathJob {}
impl MathJob
{
/// Create a new MathJob instance.
fn new() -> Self
{
MathJob
{
}
}
}
/// Implementation of the Job trait for MathJob.
impl Job for MathJob
{
/// Retrieve the name of the Job.
fn name(&self) -> &str
{
"Math Job"
}
/// Run the Math Job.
fn run<'a>(&self) -> PinnedFuture<'a>
where Self: Sync + 'a
{
Box::pin(async move {
let _ans = 10 * 4 + 2;
Ok(String::from("Math stuff"))
})
}
}
#[cfg(test)]
mod tests
{
use super::*;
use tokio::time::Instant;
#[tokio::test]
async fn test_file_job()
{
let job = FileJob::new();
assert_eq!(job.name(), "File Job");
let result = job.run().await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), "File written");
}
#[tokio::test]
async fn test_sleep_job()
{
let job = SleepJob::new();
assert_eq!(job.name(), "Sleep Job");
let start = Instant::now();
let result = job.run().await;
let elapsed = start.elapsed();
assert!(result.is_ok());
assert_eq!(result.unwrap(), "Zzzzzzzzzzz");
assert!(elapsed >= Duration::from_millis(500), "Sleep duration too short");
}
#[tokio::test]
async fn test_math_job()
{
let job = MathJob::new();
assert_eq!(job.name(), "Math Job");
let result = job.run().await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), "Math stuff");
}
#[tokio::test]
async fn test_job_runner_executes_all_jobs()
{
let mut runner = JobRunner::new();
runner.add_job(FileJob::new());
runner.add_job(SleepJob::new());
runner.add_job(MathJob::new());
let mut results = runner.run_all().await;
// Ensure jobs are cleared after run
assert!(runner.jobs.is_empty());
// Sort results by name for consistent testing
results.sort_by(|a, b| a.0.cmp(&b.0));
assert_eq!(results.len(), 3);
assert_eq!(results[0].0, "File Job");
assert_eq!(results[0].1.as_ref().unwrap(), "File written");
assert_eq!(results[1].0, "Math Job");
assert_eq!(results[1].1.as_ref().unwrap(), "Math stuff");
assert_eq!(results[2].0, "Sleep Job");
assert_eq!(results[2].1.as_ref().unwrap(), "Zzzzzzzzzzz");
}
#[tokio::test]
async fn test_job_runner_with_no_jobs()
{
let mut runner = JobRunner::new();
let results = runner.run_all().await;
assert!(results.is_empty());
}
}

207
src/basic.rs Normal file
View File

@ -0,0 +1,207 @@
// SPDX-License-Identifier: Apache-2.0
// Sealed with Magistamp.
//! This demonstrates several foundational Rust concepts.
//!
//! It includes working examples of:
//!
//! - **Arrays and iteration**
//! - **Error handling with `Result` and `?`**
//! - **File I/O using the standard library**
//! - **Asynchronous programming with `tokio`**
//! - **Basic unit and async testing**
//!
//! ## Included Functions
//!
//! - `longest`: Finds the longest string in a slice without copying.
//! - `read_and_parse`: Reads a file and parses its contents as an integer.
//! - `run_tasks`: Spawns multiple asynchronous tasks and waits for them to
//! complete.
//!
//! Unit tests are included under the `#[cfg(test)]` module to validate
//! functionality and show best practices.
use std::io::Read;
use tokio::time::{sleep, Duration};
/// Takes a list of strings and returns the longest one without
/// copying the strings.
///
/// The lifetime here is because we are returning the borrow reference of
/// the passed in String. The lifetime tells the borrow checker that the
/// returned reference needs to live as long as the owning String does.
///
/// ```
/// # use example::basic::longest;
/// let strings: [String; 5] = [String::from("Jason"),
/// String::from("is"),
/// String::from("an"),
/// String::from("awesome"),
/// String::from("programmer")];
///
/// assert_eq!(Some(strings[4].as_str()), longest(&strings));
/// ```
pub fn longest<'a>(strings: &'a [String]) -> Option<&'a str>
{
if strings.is_empty()
{
eprintln!("No strings detected");
return None;
}
// Imperative programming style.
//
// ```Rust
// This is the current longest string. (index, length)
// let mut longest: (usize, usize) = (0, 0);
// for (index, string) in strings.iter().enumerate()
// {
// if string.len() > longest.1
// {
// longest = (index, string.len());
// }
// }
//
// Some(&strings[longest.0])
// ```
//
// This can also be done with functional style:
// When coding prefer the functional, declarative style.
// It is just as fast or faster than the imperative style.
strings.iter().max_by_key(|s| s.len()).map(|s| s.as_str())
}
/// Read and parse the contents of a file as an integer.
///
/// ```
/// # fn try_main() -> Result<(), Box<dyn std::error::Error>>
/// # {
/// # use example::basic::read_and_parse;
/// use std::io::{ErrorKind, Write};
///
/// let val: i32 = 12345;
/// let mut write_target = std::env::temp_dir();
/// write_target.push("read_test");
/// write_target.set_extension("txt");
///
/// let read_target = write_target.clone();
///
/// let mut file = std::fs::File::create(write_target)?;
/// file.write_all(format!("{}", val).as_bytes())?;
///
///
/// match read_target.to_str()
/// {
/// Some(path) =>
/// {
/// assert_eq!(val, read_and_parse(path)?);
/// Ok(())
/// }
/// None =>
/// {
/// Err(Box::new(std::io::Error::new(ErrorKind::InvalidData,
/// "Unable to turn target file into \
/// a UTF-8 string.")))
/// }
/// }
/// # }
/// # fn main()
/// # {
/// # try_main().unwrap();
/// # }
/// ```
pub fn read_and_parse<P>(path: P) -> Result<i32, Box<dyn std::error::Error>>
where P: AsRef<std::path::Path>
{
let mut file = std::fs::File::open(path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
let val: i32 = contents.parse()?;
Ok(val)
}
/// Spawns 5 tasks, each sleeping for a different duration,
/// then prints when each finishes.
pub async fn run_tasks()
{
let tasks: tokio::task::JoinSet<_> =
(0..5).map(|i| {
async move {
sleep(Duration::from_millis(500 + (i * 10))).await;
println!("Finished!");
}
})
.collect();
let _result = tasks.join_all().await;
}
#[cfg(test)]
mod test
{
use std::io::Write;
use crate::basic::{longest, read_and_parse, run_tasks};
#[test]
fn longest_test() -> Result<(), String>
{
let strings: [String; 5] = [String::from("Jason"),
String::from("is"),
String::from("an"),
String::from("awesome"),
String::from("programmer")];
// Could also be Some(&*strings[4]). as_str() is easier to read though.
assert_eq!(Some(strings[4].as_str()), longest(&strings));
Ok(())
}
#[test]
fn read_parse_test() -> Result<(), Box<dyn std::error::Error>>
{
let val: i32 = 12345;
let mut write_target = std::env::temp_dir();
write_target.push("read_test");
write_target.set_extension("txt");
let read_target = write_target.clone();
let mut file = std::fs::File::create(write_target)?;
file.write_all(format!("{}", val).as_bytes())?;
match read_target.to_str()
{
Some(path) =>
{
assert_eq!(val, read_and_parse(path)?);
Ok(())
}
None =>
{
Err(Box::new(std::io::Error::new(std::io::ErrorKind::InvalidData,
"Unable to turn target file \
into a UTF-8 string.")))
}
}
}
#[tokio::test(start_paused = true)]
async fn run_tasks_test()
{
run_tasks().await;
}
}

View File

@ -1,3 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
// Sealed with Magistamp.
//! This is where the cargo build information can be retrieved from.
@ -23,8 +26,8 @@ pub fn get_name() -> &'static str
}
/// Returns the name of the program as defined by the CARGO_PKG_VERSION. This is
/// set at compile time and comes from the Cargo.toml file.
/// Returns the version of the program as defined by the CARGO_PKG_VERSION.
/// This is set at compile time and comes from the Cargo.toml file.
///
/// If a value is not found, then it will return the not defined value.
pub fn get_version() -> &'static str

16
src/lib.rs Normal file
View File

@ -0,0 +1,16 @@
// SPDX-License-Identifier: Apache-2.0
// Sealed with Magistamp.
//! This repository demonstrates modern, idiomatic Rust through two parts:
//!
//! 1. **Foundational Rust Examples** covering core concepts like lifetimes,
//! error handling, async, and testing.
//!
//! 2. **Advanced Async Job Runner** a trait-based, concurrent task
//! execution framework built using
//! `tokio`.
pub mod basic;
pub mod adv_async;

View File

@ -1,18 +0,0 @@
//! Just a small sample of Rust code.
mod info;
/// Print the version.
fn print_version()
{
println!("{} v{}", info::get_name(), info::get_version());
}
fn main()
{
print_version();
}