/////////////////////////////////////////////////////////////
// PluginWithTraitObjects::Executive::main.rs //
// - Executive creates and uses all lower level parts //
// Jim Fawcett, https://JimFawcett.github.io, 04 Mar 2021 //
/////////////////////////////////////////////////////////////
/*
Note:
Executive creates Input Comp and Output instances.
cargo run -- console
or
cargo run -- file <file name>, e.g., file count.txt
The file name must not be an existing source code file
*/
use compute::ComputeImpl;
use compute::Output as ComputeOutput;
use input::InputImpl;
use std::env;
fn main() {
// Choose plugin via CLI: e.g. `--output=file out.txt` or `--output=console`
//---------------------------------------------------------------------------
let mut args = env::args().skip(1);
let plugin = args.next().unwrap_or_else(|| "console".into());
// plugin = "console".to_string();
// Instantiate the right boxed Output
//---------------------------------------------------------------------------
let out: Box<dyn ComputeOutput> = match plugin.as_str() {
"console" => Box::new(console_output::ConsoleOutput::new()),
"file" => {
let path = args
.next()
.expect("usage: executive --output=file <out-path> <files>...");
Box::new(file_output::FileOutput::new(&path).expect("failed to create FileOutput"))
}
other => {
eprintln!("unknown output plugin "{}"", other);
std::process::exit(1);
}
};
// Wire Compute and Input
//---------------------------------------------------------------------------
let compute = Box::new(ComputeImpl::new(out));
let mut input = InputImpl::new(compute);
let mut total = 0;
for path in &[
"./src/main.rs",
"../Input/src/lib.rs",
"../Compute/src/lib.rs",
"../ConsoleOutput/src/lib.rs",
"../FileOutput/src/lib.rs",
"../Fileutils/src/lib.rs",
] {
total += input.do_input(path);
}
println!("\ntotal lines: {:?}\n", total);
}
> cargo run -q console
./src/main.rs: 67 lines
../Input/src/lib.rs: 51 lines
../Compute/src/lib.rs: 62 lines
../ConsoleOutput/src/lib.rs: 36 lines
../FileOutput/src/lib.rs: 35 lines
../Fileutils/src/lib.rs: 86 lines
total lines: 337
------------------------------------------------------
C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\Executive
> cargo run -q file count.txt
total lines: 337
------------------------------------------------------
count.txt
------------------------------------------------------
./src/main.rs -> 67 lines
../Input/src/lib.rs -> 51 lines
../Compute/src/lib.rs -> 62 lines
../ConsoleOutput/src/lib.rs -> 36 lines
../FileOutput/src/lib.rs -> 35 lines
../Fileutils/src/lib.rs -> 86 lines
[package]
name = "executive"
version = "0.1.0"
edition = "2021"
[dependencies]
input = { path = "../Input" }
compute = { path = "../Compute" }
console_output = { path = "../ConsoleOutput" }
file_output = { path = "../FileOutput" }
/////////////////////////////////////////////////////////////
// PlugInWithTraitObjects::Input::lib.rs //
// - Attempts to return line count from file //
// Jim Fawcett, https://JimFawcett.github.io, 04 Mar 2021 //
/////////////////////////////////////////////////////////////
/*
Note:
- Input owns and instantiates Compute.
- It attempts to open file and pass to Compute for
processing.
- Returns line count if successful
*/
use file_utils::open_file_for_read;
use std::fs::File;
/// The trait for anything that can count lines.
pub trait Compute {
fn do_compute(&mut self, name: &str, file: File);
fn lines(&self) -> usize;
}
/// Your InputImpl just needs a boxed Compute.
pub struct InputImpl {
compute: Box<dyn Compute>,
}
impl InputImpl {
/// `compute` is any implementor of the Compute trait.
pub fn new(compute: Box<dyn Compute>) -> Self {
InputImpl { compute }
}
/// Opens `name`, hands it to compute, returns the line count.
pub fn do_input(&mut self, name: &str) -> usize {
if let Ok(f) = open_file_for_read(name) {
self.compute.do_compute(name, f);
self.compute.lines()
} else {
eprintln!("could not open {:?}", name);
0
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::RefCell;
use std::io::Write;
use std::rc::Rc;
use tempfile::NamedTempFile;
/// Stub Compute that records the last filename it saw
/// and returns a fixed line count.
struct StubCompute {
last_name: Rc<RefCell<Option<String>>>,
return_lines: usize,
}
impl StubCompute {
fn new(record: Rc<RefCell<Option<String>>>, return_lines: usize) -> Self {
StubCompute {
last_name: record,
return_lines,
}
}
}
impl Compute for StubCompute {
fn do_compute(&mut self, name: &str, _file: File) {
*self.last_name.borrow_mut() = Some(name.to_string());
}
fn lines(&self) -> usize {
self.return_lines
}
}
#[test]
fn missing_file_returns_zero_and_skips_compute() {
let record = Rc::new(RefCell::new(None));
let stub = StubCompute::new(Rc::clone(&record), 99);
let mut inp = InputImpl::new(Box::new(stub));
let count = inp.do_input("definitely_not_a_file.txt");
assert_eq!(count, 0, "should return 0 for missing file");
assert!(record.borrow().is_none(), "do_compute should not be called");
}
#[test]
fn existing_file_calls_compute_and_returns_stub_value() {
// prepare a real file so open_file_for_read succeeds
let mut tmp = NamedTempFile::new().expect("create temp file");
write!(tmp, "ignored").expect("write to temp file");
tmp.flush().expect("flush temp file");
let path = tmp.path().to_str().unwrap().to_string();
let record = Rc::new(RefCell::new(None));
let stub = StubCompute::new(Rc::clone(&record), 7);
let mut inp = InputImpl::new(Box::new(stub));
let count = inp.do_input(&path);
assert_eq!(count, 7, "should return the stub's line count");
assert_eq!(
record.borrow().as_ref(),
Some(&path),
"do_compute should be called with the file name"
);
}
}
> cargo build --lib
Compiling file_utils v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\Fileutils)
Compiling input v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\Input)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.35s
C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\Input
> cargo test --lib
Compiling windows_x86_64_msvc v0.52.6
Compiling getrandom v0.3.3
Compiling cfg-if v1.0.1
Compiling once_cell v1.21.3
Compiling fastrand v2.3.0
Compiling windows-targets v0.52.6
Compiling windows-sys v0.59.0
Compiling tempfile v3.20.0
Compiling input v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\Input)
Finished `test` profile [unoptimized + debuginfo] target(s) in 2.36s
Running unittests src\lib.rs (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\target\debug\deps\input-3f3680d8940bbc10.exe)
running 2 tests
test tests::missing_file_returns_zero_and_skips_compute ... ok
test tests::existing_file_calls_compute_and_returns_stub_value ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
[package]
name = "input"
version = "0.1.0"
authors = ["James W. Fawcett "]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
file_utils = { path = "../Fileutils" }
[dev-dependencies]
tempfile = "3" # tempfile crate from crates.io
/////////////////////////////////////////////////////////////
// PlugInWithTraitObjects::Compute::lib.rs //
// - Attempts to read opened file to string, count lines //
// Jim Fawcett, https://JimFawcett.github.io, 04 Mar 2021 //
/////////////////////////////////////////////////////////////
/*
Note:
- creates instance of Output
- attempts to read file to string and count its lines
- sends results to Output
*/
use file_utils::read_file_to_string;
use input::Compute;
use std::fs::File;
// use output::console_output as OutputTrait; // downstream abstraction from output crate
/// The trait you expect from any Output plugin.
pub trait Output {
fn do_output(&self, name: &str, lines: usize);
}
/// Your ComputeImpl just holds a boxed Output.
pub struct ComputeImpl {
lines: usize,
out: Box<dyn Output>,
}
impl ComputeImpl {
/// `out` is any implementor of OutputTrait.
pub fn new(out: Box<dyn Output>) -> Self {
ComputeImpl { lines: 0, out }
}
}
impl Compute for ComputeImpl {
fn do_compute(&mut self, name: &str, mut file: File) {
match read_file_to_string(&mut file) {
Ok(contents) => {
// count lines
let mut count = if contents.is_empty() { 0 } else { 1 };
count += contents.chars().filter(|&c| c == '\n').count();
self.lines = count;
self.out.do_output(name, count);
}
Err(e) => eprintln!("\ncompute failed to read {:?}: {}\n", name, e),
}
}
fn lines(&self) -> usize {
self.lines
}
}
// Re-export Compute trait under its old name if you wish:
pub use input::Compute as ComputeTrait;
#[cfg(test)]
mod tests {
use super::*; // brings in ComputeImpl, Output, Compute
use std::cell::RefCell;
use std::fs::File;
use std::io::Write;
use std::rc::Rc;
use tempfile::NamedTempFile;
/// A stub Output that records the last (name, lines) it was asked to output.
struct StubOutput {
record: Rc<RefCell<Option<(String, usize)>>>,
}
impl StubOutput {
fn new(record: Rc<RefCell<Option<(String, usize)>>>) -> Self {
StubOutput { record }
}
}
impl Output for StubOutput {
fn do_output(&self, name: &str, lines: usize) {
*self.record.borrow_mut() = Some((name.to_string(), lines));
}
}
/// Write `contents` to a temp file and reopen it for reading from the start.
fn make_file(contents: &str) -> File {
let mut tmp = NamedTempFile::new().expect("create temp file");
write!(tmp, "{}", contents).expect("write temp file");
tmp.flush().expect("flush temp file");
tmp.reopen().expect("reopen temp file")
}
#[test]
fn empty_file_emits_zero_and_reports_zero() {
let record = Rc::new(RefCell::new(None));
let stub = StubOutput::new(Rc::clone(&record));
let mut comp = ComputeImpl::new(Box::new(stub));
let file = make_file("");
comp.do_compute("empty.txt", file);
assert_eq!(comp.lines(), 0);
assert_eq!(*record.borrow(), Some(("empty.txt".to_string(), 0)));
}
#[test]
fn single_line_emits_one_and_reports_one() {
let record = Rc::new(RefCell::new(None));
let stub = StubOutput::new(Rc::clone(&record));
let mut comp = ComputeImpl::new(Box::new(stub));
let file = make_file("just one line");
comp.do_compute("single.txt", file);
assert_eq!(comp.lines(), 1);
assert_eq!(*record.borrow(), Some(("single.txt".to_string(), 1)));
}
#[test]
fn multiple_lines_counted_correctly() {
let record = Rc::new(RefCell::new(None));
let stub = StubOutput::new(Rc::clone(&record));
let mut comp = ComputeImpl::new(Box::new(stub));
let file = make_file("a\nb\nc");
comp.do_compute("multi.txt", file);
assert_eq!(comp.lines(), 3);
assert_eq!(*record.borrow(), Some(("multi.txt".to_string(), 3)));
}
#[test]
fn trailing_newline_counts_empty_line() {
let record = Rc::new(RefCell::new(None));
let stub = StubOutput::new(Rc::clone(&record));
let mut comp = ComputeImpl::new(Box::new(stub));
let file = make_file("x\ny\n");
comp.do_compute("trail.txt", file);
assert_eq!(comp.lines(), 3);
assert_eq!(*record.borrow(), Some(("trail.txt".to_string(), 3)));
}
}
> cargo build --lib
Compiling file_utils v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\Fileutils)
Compiling input v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\Input)
Compiling compute v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\Compute)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.49s
C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\Compute
> cargo test --lib
Compiling windows_x86_64_msvc v0.52.6
Compiling getrandom v0.3.3
Compiling cfg-if v1.0.1
Compiling fastrand v2.3.0
Compiling once_cell v1.21.3
Compiling windows-targets v0.52.6
Compiling windows-sys v0.59.0
Compiling tempfile v3.20.0
Compiling compute v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\Compute)
Finished `test` profile [unoptimized + debuginfo] target(s) in 2.58s
Running unittests src\lib.rs (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\target\debug\deps\compute-061f996749009d21.exe)
running 4 tests
test tests::empty_file_emits_zero_and_reports_zero ... ok
test tests::trailing_newline_counts_empty_line ... ok
test tests::multiple_lines_counted_correctly ... ok
test tests::single_line_emits_one_and_reports_one ... ok
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
[package]
name = "compute"
version = "0.1.0"
edition = "2021"
[dependencies]
# console_output = { path = "../ConsoleOutput" }
file_utils = { path = "../Fileutils" }
input = { path = "../Input" }
[dev-dependencies]
tempfile = "3"
/////////////////////////////////////////////////////////////
// PlugInWithTraitObjects::ConsoleOutput::lib.rs //
// - Sends results to console //
// Jim Fawcett, https://JimFawcett.github.io, 04 Mar 2021 //
/////////////////////////////////////////////////////////////
use compute::Output as ComputeOutput;
/// A trivial console printer
pub struct ConsoleOutput;
impl ConsoleOutput {
pub fn new() -> Self {
ConsoleOutput
}
}
impl Default for ConsoleOutput {
fn default() -> ConsoleOutput {
Self::new()
}
}
impl ComputeOutput for ConsoleOutput {
fn do_output(&self, name: &str, lines: usize) {
println!("\n{}: {} lines", name, lines);
}
}
#[cfg(test)]
mod tests {
use super::*;
use compute::Output as ComputeOutput;
use std::any::{Any, TypeId};
/// new() and default() both produce the same ConsoleOutput type.
#[test]
fn new_and_default_same_type() {
let a = ConsoleOutput::new();
let b: ConsoleOutput = ConsoleOutput::default();
// `type_id()` comes from the Any trait, so import it.
assert_eq!(TypeId::of::<ConsoleOutput>(), a.type_id());
assert_eq!(TypeId::of::<ConsoleOutput>(), b.type_id());
}
/// do_output() returns the unit value and never panics.
#[test]
fn do_output_returns_unit() {
let out = ConsoleOutput::new();
let ret = out.do_output("example.rs", 7);
assert_eq!(ret, ());
}
/// It still works via a Box<dyn ComputeOutput> trait object.
#[test]
fn trait_object_dispatches() {
let obj: Box<dyn ComputeOutput> = Box::new(ConsoleOutput::new());
let ret = obj.do_output("foo.rs", 13);
assert_eq!(ret, ());
}
}
> cargo build --lib
Compiling file_utils v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\Fileutils)
Compiling input v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\Input)
Compiling compute v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\Compute)
Compiling console_output v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\ConsoleOutput)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.45s
C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\ConsoleOutput
> cargo test --lib
Compiling console_output v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\ConsoleOutput)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.23s
Running unittests src\lib.rs (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\target\debug\deps\console_output-3f714d74054ccfe7.exe)
running 3 tests
test tests::do_output_returns_unit ... ok
test tests::new_and_default_same_type ... ok
test tests::trait_object_dispatches ... ok
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
[package]
name = "console_output"
version = "0.1.0"
edition = "2021"
[dependencies]
compute = { path = "../Compute" }
/////////////////////////////////////////////////////////////
// PlugInWithTraitObjects::FileOutput::lib.rs //
// - Sends results to console //
// Jim Fawcett, https://JimFawcett.github.io, 04 Mar 2021 //
/////////////////////////////////////////////////////////////
use compute::Output as ComputeOutput;
use std::fs::File;
use std::io::Write;
/// Writes each output line into a file.
pub struct FileOutput {
file: File,
}
impl FileOutput {
pub fn new(path: &str) -> std::io::Result<Self> {
let f = File::create(path)?;
Ok(FileOutput { file: f })
}
}
impl ComputeOutput for FileOutput {
fn do_output(&self, name: &str, lines: usize) {
writeln!(&self.file, "{} -> {} lines", name, lines).expect("failed to write to output file");
}
}
#[cfg(test)]
mod tests {
use super::*;
use compute::Output as ComputeOutput;
use tempfile::NamedTempFile;
use std::fs;
use std::io::Read;
/// Helper to read the entire contents of a file into a String.
fn read_file_to_string(path: &str) -> String {
let mut s = String::new();
let mut f = fs::File::open(path).expect("failed to open output file for reading");
f.read_to_string(&mut s).expect("failed to read output file");
s
}
#[test]
fn new_creates_file_and_do_output_writes_expected_line() {
// Create a temporary file and get its path
let tmp = NamedTempFile::new().expect("failed to create temp file");
let path = tmp.path().to_str().unwrap();
// Instantiate FileOutput on that path
let fo = FileOutput::new(path).expect("FileOutput::new failed");
// Write a test line
fo.do_output("test.rs", 7);
// Read back the file contents
let contents = read_file_to_string(path);
// Expect exactly one line in the format: "name -> lines\n"
assert_eq!(contents, "test.rs -> 7 lines\n");
}
#[test]
fn multiple_do_output_appends_lines() {
let tmp = NamedTempFile::new().expect("failed to create temp file");
let path = tmp.path().to_str().unwrap();
let fo = FileOutput::new(path).expect("FileOutput::new failed");
fo.do_output("a.rs", 1);
fo.do_output("b.rs", 2);
fo.do_output("c.rs", 3);
let contents = read_file_to_string(path);
let expected = "\
a.rs -> 1 lines
b.rs -> 2 lines
c.rs -> 3 lines
";
assert_eq!(contents, expected);
}
}
> cargo build --lib
Compiling file_utils v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\Fileutils)
Compiling input v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\Input)
Compiling compute v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\Compute)
Compiling file_output v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\FileOutput)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.51s
C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\FileOutput
> cargo test --lib
Compiling windows_x86_64_msvc v0.52.6
Compiling getrandom v0.3.3
Compiling cfg-if v1.0.1
Compiling fastrand v2.3.0
Compiling once_cell v1.21.3
Compiling windows-targets v0.52.6
Compiling windows-sys v0.59.0
Compiling tempfile v3.20.0
Compiling file_output v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\FileOutput)
Finished `test` profile [unoptimized + debuginfo] target(s) in 2.29s
Running unittests src\lib.rs (C:\github\JimFawcett\NewSite\Code\DesignStructure\PlugInWithTraitObjects\target\debug\deps\file_output-c072502cc8920022.exe)
running 2 tests
test tests::new_creates_file_and_do_output_writes_expected_line ... ok
test tests::multiple_do_output_appends_lines ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
[package]
name = "file_output"
version = "0.1.0"
edition = "2021"
[dependencies]
compute = { path = "../Compute" }
[dev-dependencies]
tempfile = "3"
/////////////////////////////////////////////////////////////
// FactoredStructure::Fileutils::file_utilities.rs //
// - Input attempts to open named file and return File //
// Jim Fawcett, https://JimFawcett.github.io, 04 Mar 2021 //
/////////////////////////////////////////////////////////////
/*
This code may be useful for other programs so it is
factored into a module here.
*/
#![allow(dead_code)]
use std::fs::*;
use std::io::{Error, ErrorKind, Read, Write};
pub fn open_file_for_read(file_name: &str) -> Result<File, std::io::Error> {
let rfile = OpenOptions::new().read(true).open(file_name);
rfile
}
pub fn read_file_to_string(f: &mut File) -> Result<String, std::io::Error> {
let mut contents = String::new();
let bytes_rslt = f.read_to_string(&mut contents);
if bytes_rslt.is_ok() {
Ok(contents)
} else {
Err(Error::new(ErrorKind::Other, "read error"))
}
}
pub fn open_file_for_write(file_name: &str) -> Result<File, std::io::Error> {
let wfile = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(file_name);
wfile
}
pub fn write_string_to_file_handle(s: &str, mut f: std::fs::File) -> std::io::Result<()> {
f.write_all(s.as_bytes())?;
f.flush()?;
Ok(())
}
pub fn write_string_to_file(s: &str, file_name: &str) -> std::io::Result<()> {
std::fs::write(file_name, s)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::NamedTempFile;
/// Test the filename-based API: write_string_to_file + open_file_for_read + read_file_to_string
#[test]
fn write_and_read_via_filename() {
// Create a temporary file and get its path as a String
let tmp = NamedTempFile::new().expect("failed to create temp file");
let path = tmp.path().to_str().unwrap().to_owned();
let test_string = "hello via filename";
// Write via the file_name API
write_string_to_file(test_string, &path).expect("write_string_to_file failed");
// Read back
let mut f = open_file_for_read(&path).expect("open_file_for_read failed");
let contents = read_file_to_string(&mut f).expect("read_file_to_string failed");
assert_eq!(contents, test_string);
}
/// Test the handle-based API: open_file_for_write + write_string_to_file_handle + open/read
#[test]
fn write_and_read_via_handle() {
let tmp = NamedTempFile::new()
.expect("failed to create temp file");
let path = tmp.path().to_str().unwrap().to_owned();
let test_string = "hello via handle";
// Open for write, then write via handle
let wfile = open_file_for_write(&path)
.expect("open_file_for_write failed");
write_string_to_file_handle(test_string, wfile)
.expect("write_string_to_file_handle failed");
// Read back
let mut f2 = open_file_for_read(&path)
.expect("open_file_for_read failed");
let contents2 = read_file_to_string(&mut f2)
.expect("read_file_to_string failed");
assert_eq!(contents2, test_string);
}
}
> cargo build --lib
Compiling file_utils v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\TypeEraseDataFlowStructure\Fileutils)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.28s
C:\github\JimFawcett\NewSite\Code\DesignStructure\TypeEraseDataFlowStructure\Fileutils
> cargo test --lib
Compiling windows_x86_64_msvc v0.52.6
Compiling getrandom v0.3.3
Compiling cfg-if v1.0.1
Compiling once_cell v1.21.3
Compiling fastrand v2.3.0
Compiling windows-targets v0.52.6
Compiling windows-sys v0.59.0
Compiling tempfile v3.20.0
Compiling file_utils v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\TypeEraseDataFlowStructure\Fileutils)
Finished `test` profile [unoptimized + debuginfo] target(s) in 2.02s
Running unittests src\lib.rs (target\debug\deps\file_utils-43bed36694d2543d.exe)
running 2 tests
test tests::write_and_read_via_handle ... ok
test tests::write_and_read_via_filename ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
[package]
name = "file_utils"
version = "0.1.0"
edition = "2024"
[dependencies]
[dev-dependencies]
tempfile = "3"