diff --git a/Cargo.lock b/Cargo.lock index 6f8f0cf..7882ae0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,17 +21,137 @@ dependencies = [ "memchr", ] +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "clap" +version = "4.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "906f7fe1da4185b7a282b2bc90172a496f9def1aca4545fe7526810741591e14" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "351f9ad9688141ed83dfd8f5fb998a06225ef444b48ff4dc43de6d409b7fd10b" +dependencies = [ + "bitflags", + "clap_lex", + "is-terminal", + "once_cell", + "strsim", + "termcolor", + "terminal_size", +] + +[[package]] +name = "clap_derive" +version = "4.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81d7dc0031c3a59a04fc2ba395c8e2dd463cba1859275f065d225f6122221b45" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + [[package]] name = "livestock-rs" -version = "0.9.2" +version = "0.10.0" dependencies = [ "Inflector", + "anyhow", + "clap", "serde", ] @@ -41,6 +161,12 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + [[package]] name = "proc-macro2" version = "1.0.93" @@ -88,6 +214,20 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "rustix" +version = "0.37.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + [[package]] name = "serde" version = "1.0.217" @@ -108,6 +248,12 @@ dependencies = [ "syn", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "syn" version = "2.0.96" @@ -119,8 +265,184 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminal_size" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237" +dependencies = [ + "rustix", + "windows-sys 0.48.0", +] + [[package]] name = "unicode-ident" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[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.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[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.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[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.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[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.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[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.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[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.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml index bd731e4..99c95b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "livestock-rs" -version = "0.9.2" +version = "0.10.0" edition = "2021" authors = ["Jaken Herman "] license = "MIT" -description = "A library for managing and identifying livestock breeds." +description = "A comprehensive library and CLI tool for managing, identifying, and working with livestock breeds, growth rates, and health. Designed to support farming and ranching applications, breed registries, and livestock data processing." repository = "https://github.com/rowan-ranch/livestock-rs" keywords = ["livestock", "farming", "ranching", "agriculture", "agritech"] categories = ["data-structures"] @@ -13,6 +13,8 @@ publish = true [dependencies] Inflector = "0.11.4" +anyhow = "1.0.95" +clap = { version = "~4.1", features = ["derive", "cargo", "env", "wrap_help"] } serde = { version = "1.0.217", features=["derive"] } [badges] diff --git a/README.md b/README.md index adc2929..6aaad16 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,11 @@ ![crates.io](https://img.shields.io/crates/v/livestock-rs.svg) -A comprehensive library for managing, identifying, and working with livestock breeds. Designed to support farming and ranching applications, breed registries, and livestock data processing. +A comprehensive library and CLI tool for managing, identifying, and working with livestock breeds, growth rates, and health. Designed to support farming and ranching applications, breed registries, and livestock data processing. Features - 🌱 Support for over 1,000+ livestock breeds, categorized by species. + - 📊 Provides calculators for growth metrics like Average Daily Gain (ADG). - 🐄 Includes common cattle breeds like Angus and Brahman. - 🐐 Includes common goat breeds like Alpine and Boer. - 🐑 Includes common sheep breeds like Dorper and St. Croix. @@ -23,7 +24,7 @@ Add the crate to your `Cargo.toml`: ``` [dependencies] -livestock_rs = "0.9.2" +livestock_rs = "0.10.0" ``` or @@ -32,7 +33,13 @@ or cargo add livestock-rs ``` -## Usage Example +## ADG Usage Example +``` rust +use livestock_rs::calculators::growth::adg::calculate_adg; +let adg = calculate_adg(100.0, 150.0, 50); +``` + +## Breed Usage Example ``` rust use livestock_rs::breeds::GoatBreed; diff --git a/src/bin/stocktools/adg.rs b/src/bin/stocktools/adg.rs new file mode 100644 index 0000000..a60b613 --- /dev/null +++ b/src/bin/stocktools/adg.rs @@ -0,0 +1,59 @@ +use anyhow::{Context, Result}; +use clap::Parser; +use livestock_rs::calculators::growth::adg::calculate_adg; + +#[derive(Parser, Debug)] +#[command( + arg_required_else_help(true), + about = "Calculate Average Daily Gain (ADG) for livestock.", + long_about = " + Calculate Average Daily Gain (ADG) for livestock. + + The Average Daily Gain (ADG) is a measure of the average weight gain of an animal per day. + It is calculated by subtracting the initial weight from the final weight and dividing by the + number of days in the measurement period. + + The formula is: + + ADG = (final_weight - initial_weight) / days + + where: + + - `ADG` is the average daily gain in the same unit as the weight. + - `final_weight` is the ending weight of the animal (in kg or lbs). + - `initial_weight` is the starting weight of the animal (in kg or lbs). + - `days` is the number of days in the measurement period. + + # Example + + Calculate the ADG for an animal that starts at 100 kg and ends at 150 kg over 50 days: + + ``` + livestock adg -i 100 -f 150 -d 50 + ``` + + The result will be `1.0`, which means the animal gained 1 kg per day. + " +)] +pub struct AdgSubcommand { + #[arg(help = "Initial weight of livestock (in kg or lbs)", long, short = 'i')] + initial_weight: f64, + #[arg(help = "Final weight of livestock (in kg or lbs)", long, short = 'f')] + final_weight: f64, + #[arg( + long, + help = "The number of days in the measurement period.", + short = 'd' + )] + days: usize, +} + +impl AdgSubcommand { + pub fn run(&self) -> Result<()> { + let adg = calculate_adg(self.initial_weight, self.final_weight, self.days) + .context("Failed to calculate ADG.")?; + + println!("Average Daily Gain (ADG): {:.2}", adg); + Ok(()) + } +} diff --git a/src/bin/stocktools/main.rs b/src/bin/stocktools/main.rs new file mode 100644 index 0000000..ad945ea --- /dev/null +++ b/src/bin/stocktools/main.rs @@ -0,0 +1,32 @@ +use anyhow::Result; +use clap::{Parser, Subcommand}; + +mod adg; +use adg::AdgSubcommand; + +#[derive(Subcommand, Debug)] +enum Commands { + Adg(AdgSubcommand), +} + +#[derive(Parser)] +#[command( + author, + version, + propagate_version = true, + about = "Tools for managing and identifying livestock breeds, growth, and health." +)] +struct Cli { + #[command(subcommand)] + command: Commands, + #[arg(long, short, help = "Print extra detail in output logs")] + verbose: bool, +} + +fn main() -> Result<()> { + let cli = Cli::parse(); + + match cli.command { + Commands::Adg(subcommand) => subcommand.run(), + } +} diff --git a/src/breeds/camel.rs b/src/breeds/camel.rs index cedf0f0..1f0da67 100644 --- a/src/breeds/camel.rs +++ b/src/breeds/camel.rs @@ -19,7 +19,7 @@ pub enum CamelBreed { AlxaBactrian, ArvanaDromedary, KalmykBactrian, - SomaliDromedary + SomaliDromedary, } impl ToString for CamelBreed { @@ -57,7 +57,10 @@ impl FromStr for CamelBreed { "arvana dromedary" | "arvana" => Ok(CamelBreed::ArvanaDromedary), "kalmyk bactrian" | "kalmyk" => Ok(CamelBreed::KalmykBactrian), "somali dromedary" | "somali" => Ok(CamelBreed::SomaliDromedary), - _ => Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid camel breed")), + _ => Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Invalid camel breed", + )), } } } diff --git a/src/breeds/cattle.rs b/src/breeds/cattle.rs index 036ceb1..92be6fe 100644 --- a/src/breeds/cattle.rs +++ b/src/breeds/cattle.rs @@ -1,6 +1,6 @@ +use inflector::Inflector; use serde::{Deserialize, Serialize}; use std::str::FromStr; -use inflector::Inflector; /// An enum representing the different breeds of cattle. /// @@ -387,7 +387,14 @@ impl FromStr for CattleBreed { "bhagnari" => Ok(CattleBreed::Bhagnari), "black hereford" => Ok(CattleBreed::BlackHereford), "blacksided trondheim and norland" => Ok(CattleBreed::BlacksidedTrondheimAndNorland), - "blanca cacerena" | "blanca cacereña" | "white cáceres" | "white caceres" | "blanca cacerena/white caceres" | "blanca cacereña/white cáceres" | "blanca cacerena/white cáceres" | "blanca cacereña/white caceres" => Ok(CattleBreed::BlancaCacerena), + "blanca cacerena" + | "blanca cacereña" + | "white cáceres" + | "white caceres" + | "blanca cacerena/white caceres" + | "blanca cacereña/white cáceres" + | "blanca cacerena/white cáceres" + | "blanca cacereña/white caceres" => Ok(CattleBreed::BlancaCacerena), "blanco orejinegro" => Ok(CattleBreed::BlancoOrejinegro), "blonde d'aquitaine" => Ok(CattleBreed::BlondeDAquitaine), "bonsmara" => Ok(CattleBreed::Bonsmara), @@ -432,7 +439,11 @@ impl FromStr for CattleBreed { "dolafe" | "dølafe" => Ok(CattleBreed::Dolafe), "droughtmaster" => Ok(CattleBreed::Droughtmaster), "dulong" => Ok(CattleBreed::Dulong), - "dutch belted" | "lakenvelder" | "dutch belted (lakenvelder)" | "dutch belted/lakenvelder" | "lakenvelder/dutch belted" => Ok(CattleBreed::DutchBelted), + "dutch belted" + | "lakenvelder" + | "dutch belted (lakenvelder)" + | "dutch belted/lakenvelder" + | "lakenvelder/dutch belted" => Ok(CattleBreed::DutchBelted), "dutch friesian" => Ok(CattleBreed::DutchFriesian), "east anatolian red" => Ok(CattleBreed::EastAnatolianRed), "enderby island" => Ok(CattleBreed::EnderbyIsland), @@ -499,7 +510,9 @@ impl FromStr for CattleBreed { "krishna valley" => Ok(CattleBreed::KrishnaValley), "kurdi black" => Ok(CattleBreed::KurdiBlack), "kuri" => Ok(CattleBreed::Kuri), - "latvian brown" | "buraya latviiskaya" | "latvian brown (buraya latviiskaya)" => Ok(CattleBreed::LatvianBrown), + "latvian brown" | "buraya latviiskaya" | "latvian brown (buraya latviiskaya)" => { + Ok(CattleBreed::LatvianBrown) + } "limousin" => Ok(CattleBreed::Limousin), "limpurger" => Ok(CattleBreed::Limpurger), "lincoln red" => Ok(CattleBreed::LincolnRed), @@ -528,7 +541,9 @@ impl FromStr for CattleBreed { "morucha" => Ok(CattleBreed::Morucha), "murboden" => Ok(CattleBreed::Murboden), "murray grey" => Ok(CattleBreed::MurrayGrey), - "muturu" | "west african dwarf shorthorn" | "muturu west african dwarf shorthorn" => Ok(CattleBreed::Muturu), + "muturu" | "west african dwarf shorthorn" | "muturu west african dwarf shorthorn" => { + Ok(CattleBreed::Muturu) + } "ndama" | "n'dama" => Ok(CattleBreed::Ndama), "nagori" => Ok(CattleBreed::Nagori), "nanyang" => Ok(CattleBreed::Nanyang), @@ -609,7 +624,10 @@ impl FromStr for CattleBreed { "welsh black" => Ok(CattleBreed::WelshBlack), "white park" => Ok(CattleBreed::WhitePark), "yanbian" => Ok(CattleBreed::Yanbian), - _ => Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid cattle breed")), + _ => Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Invalid cattle breed", + )), } } } @@ -630,7 +648,10 @@ mod tests { (CattleBreed::DutchBelted, "Dutch Belted (Lakenvelder)"), (CattleBreed::HolandoArgentino, "Holando-Argentino"), (CattleBreed::IndoBrazilian, "Indo-Brazilian"), - (CattleBreed::LatvianBrown, "Latvian Brown (Buraya Latviiskaya)"), + ( + CattleBreed::LatvianBrown, + "Latvian Brown (Buraya Latviiskaya)", + ), (CattleBreed::MaineAnjou, "Maine-Anjou"), (CattleBreed::MeuseRhineYssel, "Meuse-Rhine-Yssel"), (CattleBreed::Montbeliard, "Montbéliarde"), @@ -658,7 +679,10 @@ mod tests { ("Dutch Belted (Lakenvelder)", CattleBreed::DutchBelted), ("Holando-Argentino", CattleBreed::HolandoArgentino), ("Indo-Brazilian", CattleBreed::IndoBrazilian), - ("Latvian Brown (Buraya Latviiskaya)", CattleBreed::LatvianBrown), + ( + "Latvian Brown (Buraya Latviiskaya)", + CattleBreed::LatvianBrown, + ), ("Maine-Anjou", CattleBreed::MaineAnjou), ("Meuse-Rhine-Yssel", CattleBreed::MeuseRhineYssel), ("Montbéliard", CattleBreed::Montbeliard), diff --git a/src/breeds/chicken.rs b/src/breeds/chicken.rs index fefcf02..d21bd90 100644 --- a/src/breeds/chicken.rs +++ b/src/breeds/chicken.rs @@ -85,7 +85,7 @@ pub enum ChickenBreed { Welsummer, WhiteFacedBlackSpanish, Wyandotte, - Yokohama + Yokohama, } impl ToString for ChickenBreed { @@ -169,7 +169,9 @@ impl FromStr for ChickenBreed { "nankin" => Ok(ChickenBreed::Nankin), "new hampshire red" => Ok(ChickenBreed::NewHampshireRed), "old english game" => Ok(ChickenBreed::OldEnglishGame), - "orpington" | "buff orpington" | "lavender orpington" | "buff" => Ok(ChickenBreed::Orpington), + "orpington" | "buff orpington" | "lavender orpington" | "buff" => { + Ok(ChickenBreed::Orpington) + } "plymouth rock" => Ok(ChickenBreed::PlymouthRock), "polish" => Ok(ChickenBreed::Polish), "red cap" => Ok(ChickenBreed::RedCap), @@ -189,7 +191,10 @@ impl FromStr for ChickenBreed { "white faced black spanish" => Ok(ChickenBreed::WhiteFacedBlackSpanish), "wyandotte" => Ok(ChickenBreed::Wyandotte), "yokohama" => Ok(ChickenBreed::Yokohama), - _ => Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Unknown breed")), + _ => Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Unknown breed", + )), } } } @@ -207,11 +212,14 @@ mod tests { (ChickenBreed::Ancona, "Ancona"), (ChickenBreed::Andalusian, "Andalusian"), (ChickenBreed::AppenzellBeardedHen, "Appenzell Bearded Hen"), - (ChickenBreed::AppenzellPointedHoodHen, "Appenzell Pointed Hood Hen"), + ( + ChickenBreed::AppenzellPointedHoodHen, + "Appenzell Pointed Hood Hen", + ), (ChickenBreed::Araucana, "Araucana"), (ChickenBreed::Aseel, "Aseel"), (ChickenBreed::Australorp, "Australorp"), - (ChickenBreed::Baheij, "Baheij") + (ChickenBreed::Baheij, "Baheij"), ]; for (breed, expected) in breeds.iter() { @@ -231,10 +239,16 @@ mod tests { ("new hampshire red", ChickenBreed::NewHampshireRed), ("dutch bantam", ChickenBreed::DutchBantam), ("appenzell bearded hen", ChickenBreed::AppenzellBeardedHen), - ("appenzell pointed hood hen", ChickenBreed::AppenzellPointedHoodHen), - ("white faced black spanish", ChickenBreed::WhiteFacedBlackSpanish), + ( + "appenzell pointed hood hen", + ChickenBreed::AppenzellPointedHoodHen, + ), + ( + "white faced black spanish", + ChickenBreed::WhiteFacedBlackSpanish, + ), ("silver montazah", ChickenBreed::SilverMontazah), - ("golden montazah", ChickenBreed::GoldenMontazah) + ("golden montazah", ChickenBreed::GoldenMontazah), ]; for (breed, expected) in breeds.iter() { diff --git a/src/breeds/donkey.rs b/src/breeds/donkey.rs index dec8d11..80a24f3 100644 --- a/src/breeds/donkey.rs +++ b/src/breeds/donkey.rs @@ -22,7 +22,7 @@ pub enum DonkeyBreed { Mary, Miniature, Poitou, - Standard + Standard, } impl ToString for DonkeyBreed { @@ -63,7 +63,10 @@ impl FromStr for DonkeyBreed { "miniature" | "mini" => Ok(DonkeyBreed::Miniature), "poitou" => Ok(DonkeyBreed::Poitou), "standard" => Ok(DonkeyBreed::Standard), - _ => Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid reindeer breed")), + _ => Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Invalid reindeer breed", + )), } } } @@ -101,7 +104,7 @@ mod tests { ("miniature", DonkeyBreed::Miniature), ("mini", DonkeyBreed::Miniature), ("poitou", DonkeyBreed::Poitou), - ("standard", DonkeyBreed::Standard) + ("standard", DonkeyBreed::Standard), ]; for (breed, expected) in breeds.iter() { diff --git a/src/breeds/horse.rs b/src/breeds/horse.rs index 1f53a96..c08d985 100644 --- a/src/breeds/horse.rs +++ b/src/breeds/horse.rs @@ -422,7 +422,9 @@ impl FromStr for HorseBreed { "nooitgedacht" => Ok(HorseBreed::Nooitgedacht), "noric" => Ok(HorseBreed::Noric), "nordland" => Ok(HorseBreed::Nordland), - "northeastern crioulo" | "northeastern crioulo" => Ok(HorseBreed::NortheasternCrioulo), + "northeastern crioulo" | "northeastern crioulo" => { + Ok(HorseBreed::NortheasternCrioulo) + } "north swedish" => Ok(HorseBreed::NorthSwedish), "norwegian fjord" => Ok(HorseBreed::NorwegianFjord), "ob" => Ok(HorseBreed::Ob), @@ -497,7 +499,10 @@ impl FromStr for HorseBreed { "yonaguni" => Ok(HorseBreed::Yonaguni), "zaniskari" => Ok(HorseBreed::Zaniskari), "zhemaichu" => Ok(HorseBreed::Zhemaichu), - _ => Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid horse breed")), + _ => Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Invalid horse breed", + )), } } } @@ -546,7 +551,7 @@ mod tests { ("single-footing", HorseBreed::SingleFooting), ("spanish-norman", HorseBreed::SpanishNorman), ("sudan country-bred", HorseBreed::SudanCountryBred), - ("welsh pony & cob", HorseBreed::WelshPonyAndCob) + ("welsh pony & cob", HorseBreed::WelshPonyAndCob), ]; for (breed, expected) in breeds.iter() { diff --git a/src/breeds/mod.rs b/src/breeds/mod.rs index c3cbf11..e07fd03 100644 --- a/src/breeds/mod.rs +++ b/src/breeds/mod.rs @@ -6,4 +6,4 @@ pub mod goat; pub mod horse; pub mod reindeer; pub mod sheep; -pub mod swine; \ No newline at end of file +pub mod swine; diff --git a/src/breeds/reindeer.rs b/src/breeds/reindeer.rs index 2e381f7..a1ea6f5 100644 --- a/src/breeds/reindeer.rs +++ b/src/breeds/reindeer.rs @@ -17,7 +17,7 @@ pub enum ReindeerBreed { Chukotka, Even, Evenk, - Nentsi + Nentsi, } impl ToString for ReindeerBreed { @@ -59,7 +59,10 @@ impl FromStr for ReindeerBreed { "even" => Ok(ReindeerBreed::Even), "evenk" => Ok(ReindeerBreed::Evenk), "nentsi" => Ok(ReindeerBreed::Nentsi), - _ => Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid reindeer breed")), + _ => Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Invalid reindeer breed", + )), } } } diff --git a/src/breeds/sheep.rs b/src/breeds/sheep.rs index 748e0fd..8c46869 100644 --- a/src/breeds/sheep.rs +++ b/src/breeds/sheep.rs @@ -261,7 +261,7 @@ pub enum SheepBreed { Zemmour, ZetaYellow, Zlatusha, - Zoulay + Zoulay, } impl ToString for SheepBreed { @@ -332,7 +332,7 @@ impl FromStr for SheepBreed { "bavarian forest" => Ok(SheepBreed::BavarianForest), "bentheimer landschaf" => Ok(SheepBreed::BentheimerLandschaf), "bergamasca" => Ok(SheepBreed::Bergamasca), - "beulah speckled face" | "beulah speckle face"=> Ok(SheepBreed::BeulahSpeckledFace), + "beulah speckled face" | "beulah speckle face" => Ok(SheepBreed::BeulahSpeckledFace), "bibrik" => Ok(SheepBreed::Bibrik), "biellese" => Ok(SheepBreed::Biellese), "blackwelshmountain" => Ok(SheepBreed::BlackWelshMountain), @@ -380,7 +380,9 @@ impl FromStr for SheepBreed { "derbyshire gritstone" => Ok(SheepBreed::DerbyshireGritstone), "dorper" => Ok(SheepBreed::Dorper), "devon closewool" => Ok(SheepBreed::DevonClosewool), - "deutsches blaukoepfiges fleischschaf" => Ok(SheepBreed::DeutschesBlaukoepfigesFleischschaf), + "deutsches blaukoepfiges fleischschaf" => { + Ok(SheepBreed::DeutschesBlaukoepfigesFleischschaf) + } "dorset down" => Ok(SheepBreed::DorsetDown), "dorset" => Ok(SheepBreed::Dorset), "drysdale" => Ok(SheepBreed::Drysdale), @@ -417,7 +419,7 @@ impl FromStr for SheepBreed { "ile de france" => Ok(SheepBreed::IleDeFrance), "istrian pramenka" => Ok(SheepBreed::IstrianPramenka), "jacob" => Ok(SheepBreed::Jacob), - "jezersko solcava" | "jezersko solčava" => Ok(SheepBreed::JezerskoSolcava), + "jezersko solcava" | "jezersko solčava" => Ok(SheepBreed::JezerskoSolcava), "kachhi" => Ok(SheepBreed::Kachhi), "kajli" => Ok(SheepBreed::Kajli), "karakul" => Ok(SheepBreed::Karakul), @@ -503,7 +505,11 @@ impl FromStr for SheepBreed { "south wales mountain" => Ok(SheepBreed::SouthWalesMountain), "spaeslau" => Ok(SheepBreed::Spaeslau), "spiegel" => Ok(SheepBreed::Spiegel), - "st croix" | "virgin island white" | "st. croix (virgin island white)" | "st. croix" | "saint croix" => Ok(SheepBreed::StCroix), + "st croix" + | "virgin island white" + | "st. croix (virgin island white)" + | "st. croix" + | "saint croix" => Ok(SheepBreed::StCroix), "steigar" => Ok(SheepBreed::Steigar), "steinschaf" => Ok(SheepBreed::Steinschaf), "strong wool merino" => Ok(SheepBreed::StrongWoolMerino), @@ -557,7 +563,10 @@ impl FromStr for SheepBreed { "zeta yellow" => Ok(SheepBreed::ZetaYellow), "zlatusha" => Ok(SheepBreed::Zlatusha), "zoulay" => Ok(SheepBreed::Zoulay), - _ => Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Unknown sheep breed")), + _ => Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Unknown sheep breed", + )), } } } @@ -613,4 +622,4 @@ mod tests { assert_eq!(SheepBreed::from_str(breed).unwrap(), *expected); } } -} \ No newline at end of file +} diff --git a/src/breeds/swine.rs b/src/breeds/swine.rs index 90b15ca..c6fb599 100644 --- a/src/breeds/swine.rs +++ b/src/breeds/swine.rs @@ -82,10 +82,9 @@ pub enum SwineBreed { VietnamesePotbelly, Welsh, Wuzhishan, - Yorkshire + Yorkshire, } - impl ToString for SwineBreed { /// Converts the SwineBreed enum to a human readable string. /// @@ -156,7 +155,7 @@ impl FromStr for SwineBreed { "kunekune" => Ok(SwineBreed::Kunekune), "lacombe" => Ok(SwineBreed::Lacombe), "large black" => Ok(SwineBreed::LargeBlack), - "large black-white" | "large black white"=> Ok(SwineBreed::LargeBlackWhite), + "large black-white" | "large black white" => Ok(SwineBreed::LargeBlackWhite), "large white" => Ok(SwineBreed::LargeWhite), "lithuanian" => Ok(SwineBreed::Lithuanian), "mangalitza" => Ok(SwineBreed::Mangalitza), @@ -171,7 +170,9 @@ impl FromStr for SwineBreed { "ningxiang" => Ok(SwineBreed::Ningxiang), "norwegian landrace" => Ok(SwineBreed::NorwegianLandrace), "ossabaw island" => Ok(SwineBreed::OssabawIsland), - "oxford sandy and black" | "oxford sandy & black"=> Ok(SwineBreed::OxfordSandyAndBlack), + "oxford sandy and black" | "oxford sandy & black" => { + Ok(SwineBreed::OxfordSandyAndBlack) + } "philippine native" => Ok(SwineBreed::PhilippineNative), "pietrain" => Ok(SwineBreed::Pietrain), "poland china" => Ok(SwineBreed::PolandChina), @@ -183,11 +184,16 @@ impl FromStr for SwineBreed { "thuoc nhieu" => Ok(SwineBreed::ThuocNhieu), "tibetan" => Ok(SwineBreed::Tibetan), "thuropolje" => Ok(SwineBreed::Thuropolje), - "vietnamese potbelly" | "potbelly" | "potbellied" | "vietnamese potbellied" => Ok(SwineBreed::VietnamesePotbelly), + "vietnamese potbelly" | "potbelly" | "potbellied" | "vietnamese potbellied" => { + Ok(SwineBreed::VietnamesePotbelly) + } "welsh" => Ok(SwineBreed::Welsh), "wuzhishan" => Ok(SwineBreed::Wuzhishan), "yorkshire" => Ok(SwineBreed::Yorkshire), - _ => Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid reindeer breed")), + _ => Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Invalid reindeer breed", + )), } } } @@ -225,10 +231,13 @@ mod tests { (SwineBreed::FinnishLandrace, "Finnish Landrace"), (SwineBreed::FrenchLandrace, "French Landrace"), (SwineBreed::GermanLandrace, "German Landrace"), - (SwineBreed::GloucestershireOldSpot, "Gloucestershire Old Spot"), + ( + SwineBreed::GloucestershireOldSpot, + "Gloucestershire Old Spot", + ), (SwineBreed::GuineaHog, "Guinea Hog"), (SwineBreed::Hampshire, "Hampshire"), - (SwineBreed::Hereford, "Hereford") + (SwineBreed::Hereford, "Hereford"), ]; for (breed, expected) in breeds.iter() { @@ -238,8 +247,8 @@ mod tests { #[test] fn test_from_str() { - // We don't have many special cases, just test a few. - let breeds = [ + // We don't have many special cases, just test a few. + let breeds = [ ("american landrace", SwineBreed::AmericanLandrace), ("american yorkshire", SwineBreed::AmericanYorkshire), ("angeln saddleback", SwineBreed::AngelnSaddleback), @@ -252,11 +261,11 @@ mod tests { ("oxford sandy and black", SwineBreed::OxfordSandyAndBlack), ("oxford sandy & black", SwineBreed::OxfordSandyAndBlack), ("large black-white", SwineBreed::LargeBlackWhite), - ("large black white", SwineBreed::LargeBlackWhite) - ]; + ("large black white", SwineBreed::LargeBlackWhite), + ]; - for (breed, expected) in breeds.iter() { - assert_eq!(SwineBreed::from_str(*breed).unwrap(), *expected); - } + for (breed, expected) in breeds.iter() { + assert_eq!(SwineBreed::from_str(*breed).unwrap(), *expected); + } } } diff --git a/src/calculators/growth/adg.rs b/src/calculators/growth/adg.rs new file mode 100644 index 0000000..f765a83 --- /dev/null +++ b/src/calculators/growth/adg.rs @@ -0,0 +1,76 @@ +use anyhow::{ensure, Result}; + +/// Calculate Average Daily Gain (ADG) for livestock. +/// +/// # Arguments +/// - `initial_weight`: Starting weight of the animal (kg or lbs). +/// - `final_weight`: Ending weight of the animal (kg or lbs). +/// - `days`: The number of days in the measurement period. +/// +/// # Returns +/// The average daily gain (ADG) in the same unit as the weight. +/// +/// # Example +/// ``` +/// use livestock_rs::calculators::growth::adg::calculate_adg; +/// let adg = calculate_adg(100.0, 150.0, 50).unwrap(); +/// assert_eq!(adg, 1.0); // 1 kg or lb per day +/// ``` +pub fn calculate_adg(initial_weight: f64, final_weight: f64, days: usize) -> Result { + ensure!(days > 0, "Number of days cannot be zero."); + ensure!( + final_weight > initial_weight, + "Final weight must be greater than initial weight." + ); + ensure!( + final_weight > 0.0 && initial_weight > 0.0, + "Weights must be greater than zero." + ); + + Ok((final_weight - initial_weight) / days as f64) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_calculate_adg() { + let adg_test_cases = [ + (100.0, 150.0, 50, 1.0), + (100.0, 200.0, 100, 1.0), + (100.0, 200.0, 50, 2.0), + (100.0, 150.0, 100, 0.5), + ]; + + for (initial_weight, final_weight, days, expected) in adg_test_cases.iter() { + let result = calculate_adg(*initial_weight, *final_weight, *days); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), *expected); + } + } + + #[test] + fn test_calculate_adg_zero_days() { + let result = calculate_adg(100.0, 150.0, 0); + assert!(result.is_err()); + } + + #[test] + fn test_calculate_adg_negative_weight() { + let result = calculate_adg(-100.0, 150.0, 50); + assert!(result.is_err()); + } + + #[test] + fn test_calculate_adg_negative_final_weight() { + let result = calculate_adg(100.0, -150.0, 50); + assert!(result.is_err()); + } + + #[test] + fn test_calculate_adg_negative_initial_weight() { + let result = calculate_adg(-100.0, 150.0, 50); + assert!(result.is_err()); + } +} \ No newline at end of file diff --git a/src/calculators/growth/mod.rs b/src/calculators/growth/mod.rs new file mode 100644 index 0000000..bd96aba --- /dev/null +++ b/src/calculators/growth/mod.rs @@ -0,0 +1 @@ +pub mod adg; diff --git a/src/calculators/mod.rs b/src/calculators/mod.rs new file mode 100644 index 0000000..51637b1 --- /dev/null +++ b/src/calculators/mod.rs @@ -0,0 +1 @@ +pub mod growth; diff --git a/src/lib.rs b/src/lib.rs index da0eaa6..f05e826 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,2 @@ -pub mod breeds; \ No newline at end of file +pub mod breeds; +pub mod calculators;