/////////////////////////////////////////////////////////////
// FactoredStructure::Executive::main.rs //
// - Executive creates and uses all lower level parts //
// Jim Fawcett, https://JimFawcett.github.io, 04 Mar 2021 //
/////////////////////////////////////////////////////////////
#![allow(dead_code)]
#![allow(unused_imports)]
use compute::*;
use input::*;
use output::*;
/*-- attempt to open file and count its lines of code --*/
fn test_ops(name: &str) -> usize {
let mut inp = Input::new();
let mut cmp = Compute::new();
let out = Output::new();
let mut lines: usize = 0;
let opt = inp.do_input(name);
if let Some(file) = opt {
cmp.do_compute(name, file);
lines = cmp.lines();
out.do_output(name, lines);
} else {
print!("\n couldn't process {:?}", name);
}
lines
}
/*-- run test_ops for each package in this project --*/
fn run_test_ops() -> usize {
let name = "../Executive/src/main.rs";
let mut lines = 0;
lines += test_ops(name);
println!();
let name = "../Input/src/lib.rs";
lines += test_ops(name);
let name = "../Input/examples/test1.rs";
lines += test_ops(name);
println!();
let name = "../Compute/src/lib.rs";
lines += test_ops(name);
let name = "../Compute/examples/test1.rs";
lines += test_ops(name);
println!();
let name = "../Output/src/lib.rs";
lines += test_ops(name);
let name = "../Output/examples/test1.rs";
lines += test_ops(name);
println!();
let name = "../Fileutils/src/lib.rs";
lines += test_ops(name);
println!();
let name = "no-exist";
lines += test_ops(name);
println!();
lines
}
/*-- Executive processing --*/
fn main() {
print!("\n -- FactoredStructure::Executive --\n");
let lines = run_test_ops();
print!("\n total lines: {}", lines);
print!("\n\n That's all Folks!\n\n");
}
> cargo run -q
-- FactoredStructure::Executive --
73 lines in file "../Executive/src/main.rs"
60 lines in file "../Input/src/lib.rs"
22 lines in file "../Input/examples/test1.rs"
61 lines in file "../Compute/src/lib.rs"
42 lines in file "../Compute/examples/test1.rs"
24 lines in file "../Output/src/lib.rs"
16 lines in file "../Output/examples/test1.rs"
91 lines in file "../Fileutils/src/lib.rs"
can't open file "no-exist"
couldn't process "no-exist"
total lines: 389
That's all Folks!
[package]
name = "executive"
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]
input = { path = "../Input" }
compute = { path = "../Compute" }
output = { path = "../Output" }
/////////////////////////////////////////////////////////////
// FactoredStructure::Input::lib.rs //
// - Input attempts to open named file and return File //
// Jim Fawcett, https://JimFawcett.github.io, 04 Mar 2021 //
/////////////////////////////////////////////////////////////
use file_utils::open_file_for_read;
use std::fs::*;
#[allow(dead_code)]
#[derive(Debug)]
pub struct Input {
// no data members
}
impl Default for Input {
fn default() -> Self {
Self::new()
}
}
impl Input {
pub fn new() -> Input {
Input {}
}
pub fn do_input(&mut self, name: &str) -> Option<File> {
let rslt = open_file_for_read(name);
if let Ok(file) = rslt {
Some(file)
} else {
print!("\n can't open file {:?}", name);
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use file_utils::{read_file_to_string, write_string_to_file};
// fn test1() has been refactored by Perplexity AI from my
// original test code. Now more consise.
#[test]
fn test1() {
// Test setup: create test file
write_string_to_file("test1", "test1.txt").expect("Failed to write test file");
// Check: file is readable
let _file = open_file_for_read("test1.txt").expect("File should be readable");
// Test Input::do_input opens file successfully
let mut inp = Input::new();
let file_option = inp.do_input("test1.txt");
let mut file = file_option.expect("Input::do_input should return Some(file)");
// The file content is as expected
let content = read_file_to_string(&mut file).expect("Should read file content");
assert_eq!(content, "test1");
}
}
[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" }
> cargo build --lib
Compiling file_utils v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\FactoredStructure\Fileutils)
Compiling input v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\FactoredStructure\Input)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.23s
C:\github\JimFawcett\NewSite\Code\DesignStructure\FactoredStructure\Input
> cargo test --lib
Compiling input v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\FactoredStructure\Input)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.24s
Running unittests src\lib.rs (target\debug\deps\input-afa3cf23b115c222.exe)
running 1 test
test tests::test1 ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
/////////////////////////////////////////////////////////////
// FactoredStructure::Compute::lib.rs //
// - Input attempts to read File to string & count lines //
// Jim Fawcett, https://JimFawcett.github.io, 04 Mar 2021 //
/////////////////////////////////////////////////////////////
use file_utils::read_file_to_string;
use std::fs::*;
#[derive(Debug)]
pub struct Compute {
lines: usize,
}
impl Default for Compute {
fn default() -> Self {
Self::new()
}
}
impl Compute {
pub fn new() -> Compute {
Compute { lines: 0 }
}
/*-- read file, count lines and save count --*/
pub fn do_compute(&mut self, name: &str, mut file: File) {
let rslt = read_file_to_string(&mut file);
if let Ok(contents) = rslt {
if contents.len() == 0 {
self.lines = 0;
} else {
self.lines = 1;
}
for ch in contents.chars() {
if ch == '\n' {
self.lines += 1;
}
}
} else {
print!("\n couldn't open {:?}", name);
}
}
/*-- return saved line count --*/
pub fn lines(&self) -> usize {
self.lines
}
}
#[cfg(test)]
mod tests {
use super::*;
use file_utils::{open_file_for_read, write_string_to_file};
#[test]
fn read_and_compute() {
write_string_to_file("test1", "test1.txt").expect("Failed to write test file");
let file = open_file_for_read("test1.txt").expect("Failed to open test file for read");
let mut comp = Compute::new();
comp.do_compute("test1.txt", file);
assert_eq!(comp.lines(), 1);
}
}
[package]
name = "compute"
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" }
> cargo build --lib
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
C:\github\JimFawcett\NewSite\Code\DesignStructure\FactoredStructure\Compute
> cargo test --lib
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.01s
Running unittests src\lib.rs (target\debug\deps\compute-af586bcaada24d0a.exe)
running 1 test
test tests::read_and_compute ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
/////////////////////////////////////////////////////////////
// FactoredStructure::Output::lib.rs //
// - Output displays line count //
// Jim Fawcett, https://JimFawcett.github.io, 04 Mar 2021 //
/////////////////////////////////////////////////////////////
#[derive(Debug)]
pub struct Output {}
impl Default for Output {
fn default() -> Self {
Self::new()
}
}
impl Output {
pub fn new() -> Output {
Output {}
}
pub fn do_output(&self, name: &str, lines: usize) {
print!("\n {:4} lines in file {:?}", lines, name);
}
}
#[cfg(test)]
mod tests {
use super::*;
/// new() + Debug should produce the struct name
#[test]
fn new_and_debug() {
let out = Output::new();
// Debug derive on `struct Output {}` prints just "Output"
assert_eq!(format!("{:?}", out), "Output");
}
/// do_output() should return () and never panic
#[test]
fn do_output_does_not_panic() {
let out = Output::new();
// We're not capturing stdout here (that requires an external crate
// or reworking the API), but at least we ensure it runs cleanly.
let unit = out.do_output("foo.rs", 7);
let () = unit; // type-check that we got the unit value back
}
}
[package]
name = "output"
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]
> cargo build --lib
Compiling output v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\FactoredStructure\Output)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.20s
C:\github\JimFawcett\NewSite\Code\DesignStructure\FactoredStructure\Output
> cargo test --lib
Compiling output v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\FactoredStructure\Output)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.24s
Running unittests src\lib.rs (target\debug\deps\output-1eee8dc0a1134569.exe)
running 2 tests
test tests::do_output_does_not_panic ... ok
test tests::new_and_debug ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
/////////////////////////////////////////////////////////////
// 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 `write_string_to_file` + `open_file_for_read` + `read_file_to_string` path.
#[test]
fn write_and_read_via_filename() {
// create a temp file path but don't write to it yet
let tmp = NamedTempFile::new().expect("create temp file");
let path = tmp.path().to_str().unwrap();
let test_string = "hello filename!";
// write via filename API
write_string_to_file(test_string, path).expect("write_string_to_file failed");
// now read it 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 `open_file_for_write` + `write_string_to_file_handle` + read path.
#[test]
fn write_and_read_via_handle() {
let tmp = NamedTempFile::new().expect("create temp file");
let path = tmp.path().to_str().unwrap();
let test_string = "hello handle!";
// open for write (creates & truncates), 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 it 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);
}
}
[package]
name = "file_utils"
version = "0.1.0"
edition = "2024"
[dependencies]
[dev-dependencies]
tempfile = "3"
> cargo build --lib
Compiling file_utils v0.1.0 (C:\github\JimFawcett\NewSite\Code\DesignStructure\FactoredStructure\FileUtils)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.29s
C:\github\JimFawcett\NewSite\Code\DesignStructure\FactoredStructure\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\FactoredStructure\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_filename ... ok
test tests::write_and_read_via_handle ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s