# Racros Collection of rust macros. ## Content ### AutoDebug Works on structs and enums, similar to [`std::fmt::Debug`] but support some customization: * Specify field name or value in print message. * Ignore specified field. * Use [`std::fmt::Display`] instead of [`std::fmt:;Debug`] on specified field. * Set print style similar to printing tuple or struct. #### Basic Usage * `#[derive(AutoDebug)]` makes a struct style debug implementation. #### Struct Attributes * `#[debug_style = tuple]` makes a tuple style debug implementation. Default is struct style. * `#[debug_format = display]` uses `Display` trait on fields. Default is debug format. #### Struct Field Attributes * `#[debug_name = "foo"]` override field name with "foo", if in struct `debug_style`. * `#[debug_value = "foo"]` override field value with "foo". * `#[debug_ignore]` will ignore this field in the output. * `#[debug_debug]` will use [Debug] trait implementation for this field in output. * `#[debug_display]` will use [Display] trait implementation for this field in output. #### Example For a custom type `MyType` that print `display MyType` in `Display` and `debug MyType` in `Debug`: ``` rust struct MyType {} impl std::fmt::Debug for MyType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str("debug MyType") } } impl std::fmt::Display for MyType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str("display MyType") } } ``` Generate implementation with "{:#?}" for each field in `debug_struct` on default. ``` rust #[derive(AutoDebug)] struct Foo1 { #[debug_name = "my_foo1"] // Use "my_foo1" as strcut field name. foo1: MyType, #[debug_ignore] // Ignore this field. foo2: MyType, #[debug_display] // Use `Display` implementation for this field. foo3: MyType, #[debug_value = "foo4, MyType"] // Use "foo4, MyType" as struct field value. foo4: MyType, } #[derive(AutoDebug)] #[debug_format = "display"] // Default implementation use "{}" for each field. #[debug_style = "tuple"] // Output style set to `debug_tuple`. struct Foo2 { #[debug_debug] // Use `Debug` implementation for this field. foo1: MyType, foo2: MyType, } let foo1 = Foo1 { foo1: MyType {}, foo2: MyType {}, foo3: MyType {}, foo4: MyType {}, }; assert_eq!( std::fmt::format(format_args!("{:#?}", foo1)), r#"Foo1 { my_foo1: debug MyType, foo3: display MyType, foo4: "foo4, MyType", }"# ); let foo2 = Foo2 { foo1: MyType {}, foo2: MyType {}, }; assert_eq!( std::fmt::format(format_args!("{:#?}", foo2)), r#"Foo2( debug MyType, display MyType, )"# ); ``` ### AutoStr Implement [`TryFrom`] `String` and [`ToString`] for enums with following features: * Specify what `String` value can convert from/to. * Allow convert from multiple `String` values. * Set default convert style: * `lowercase` * `UPPERCASE` * `camelCase` * `PascalCase` * `snake_case` * `SCREAMING_CASE` For the following code: ``` rust #[derive(AutoStr)] enum MyEnum { E1, E2, E3, } ``` `AutoStr` will generate codes: ``` rust impl TryFrom<&str> for MyEnum { type Error = String; fn try_from(value: &str) -> Result { match value { "E1" => Ok(MyEnum::E1), "E2" => Ok(MyEnum::E2), "E3" => Ok(MyEnum::E3), _ => Err(Self::Error::from( format("failed to convert to {0} :invalid value","MyEnum") )) } } } impl ToString for MyEnum { fn to_string(&self) -> String { match self { MyEnum::E1 => "E1".to_string(), MyEnum::E2 => "E2".to_string(), MyEnum::E3 => "E3".to_string(), } } } ``` The string format can be set to * `lowercase` * `UPPERCASE` * `camelCase` * `PascalCase` * `snake_case` * `SCREAMING_CASE` by adding a `#[autorule = "xxxx"]` attribute to the enum: ``` rust #[derive(AutoStr)] #[autorule = "lowercase"] enum MyEnum { E1, E2, E3, } impl TryFrom<&str> for MyEnum { type Error = String; fn try_from(value: &str) -> Result { match value { "e1" => Ok(MyEnum::E1), "e2" => Ok(MyEnum::E2), "e3" => Ok(MyEnum::E3), _ => Err(Self::Error::from( format("failed to convert to {} :invalid value","MyEnum") )) } } } impl ToString for MyEnum { fn to_string(&self) -> String { match self { MyEnum::E1 => "e1".to_string(), MyEnum::E2 => "e2".to_string(), MyEnum::E3 => "e3".to_string(), } } } ``` In addition, adding the `#[str(...)]` attribute to enum field will override the default format. ``` rust #[derive(AutoStr)] #[autorule = "lowercase"] enum MyEnum { #[str("e1", "E1")] E1, E2, #[str("e3", "ee")] E3, } impl TryFrom<&str> for MyEnum { type Error = String; fn try_from(value: &str) -> Result { match value { "e1" | "E1" => Ok(MyEnum::E1), "e2" => Ok(MyEnum::E2), "e3" | "ee"=> Ok(MyEnum::E3), _ => Err(Self::Error::from( format("failed to convert to {} :invalid value","MyEnum") )) } } } impl ToString for MyEnum { fn to_string(&self) -> String { match self { MyEnum::E1 => "e1".to_string(), MyEnum::E2 => "e2".to_string(), MyEnum::E3 => "e3".to_string(), } } } ``` Support embedded enums: ``` rust enum MyEnum4 { E41(MyEnum), E42(MyEnum2), } impl TryFrom<&str> for MyEnum4 { type Error = String; fn try_from(value: &str) -> Result { match value { _ => { let mut fallback_field: Option<&str> = None; let mut fallback_result: Option = None; if let Ok(v) = MyEnum::try_from(value) { if fallback_result.is_some() { return Err( Self::Error::from({ format!( "#[str(...)] attribute not set and fallback guess is ambiguous: both {} and {} can accept this convert from \"{}\"", fallback_field.unwrap(), "MyEnum", value, ) }), ); } fallback_field = Some("MyEnum"); fallback_result = Some(MyEnum4::E41(v)); } if let Ok(v) = MyEnum2::try_from(value) { if fallback_result.is_some() { return Err( Self::Error::from({ format_args!( "#[str(...)] attribute not set and fallback guess is ambiguous: both {} and {} can accept this convert from \"{}\"", fallback_field.unwrap(), "MyEnum2", value, ) }), ); } fallback_field = Some("MyEnum2"); fallback_result = Some(MyEnum4::E42(v)); } match fallback_result { Some(v) => Ok(v), None => { Err( Self::Error::from({ format_args!( "failed to convert to {} :invalid value \"{}\"", "MyEnum4", value, ) }), ) } } } } } } impl ToString for MyEnum4 { fn to_string(&self) -> String { match self { MyEnum4::E41(v) => v.to_string(), MyEnum4::E42(v) => v.to_string(), } } } ``` ### CopyWith Add a `copy_with` function for decorated type, copy value from another `Self` if that value is not `default` value. #### Basic Usage For the following struct, generate: ``` rust struct MyStruct { foo1: i8, foo2: String, foo3: Option, } impl MyStruct { fn copy_with(&mut self, other: &Self) { if other.foo1 != i8::default() { self.foo1 = other.foo1.clone(); } if other.foo2 != String::default() { self.foo2 = other.foo2.clone(); } if other.foo3 != Option::default() { self.foo3 = other.foo3.clone(); } } } ``` #### Field Attributes Add `#[copy]` attribute to a field will try to call `.copy_with()` on that field instead of directly comparing values. #### Example ``` rust use racros::CopyWith; #[derive(Clone, Default, CopyWith)] struct MyStruct { foo1: i8, foo2: String, foo3: Option, } #[derive(CopyWith)] struct MyStruct2 { #[copy] bar1: MyStruct, } let s1 = MyStruct::default(); let mut s11 = MyStruct::default(); let s2 = MyStruct { foo1: 64, foo2: String::from("hello world"), foo3: Some(String::from("hello world")), }; let mut s21 = MyStruct { foo1: 64, foo2: String::from("hello world"), foo3: Some(String::from("hello world")), }; s11.copy_with(&s2); assert_eq!(s11.foo1, s2.foo1); // Copy s2.foo1 to s11.foo1 assert_eq!(s11.foo2, s2.foo2); // Copy s2.foo2 to s11.foo2 assert_eq!(s11.foo3, s2.foo3); // Copy s2.foo3 to s11.foo3 s21.copy_with(&s1); assert_eq!(s21.foo1, s2.foo1); // Not copy because s1.foo1 is default value. assert_eq!(s21.foo2, s2.foo2); // Not copy because s1.foo2 is default value. assert_eq!(s21.foo3, s2.foo3); // Not copy because s1.foo3 is default value. let mut s31 = MyStruct2 { bar1: MyStruct::default(), }; let s32 = MyStruct2 { bar1: MyStruct { foo1: 64, foo2: String::from("hello world"), foo3: Some(String::from("hello world")), }, }; s31.copy_with(&s32); // Here use `s32.copy_with()` because bar1 has `#[copy]` attribute. assert_eq!(s31.bar1.foo1, s2.foo1); assert_eq!(s31.bar1.foo2, s2.foo2); assert_eq!(s31.bar1.foo3, s2.foo3); ``` ### BundleText Bundle text content or command output into static str at compile time and use in runtime. #### Usage * Bundle file: #[bundle(name = "get_file", file = "file/path")] * Bundle command: #[bundle(name = "get_rustc_version", command = "rustc --version")] #### Example ```rust use std::io::Read; use std::process::Stdio; use racros::BundleText; #[derive(BundleText)] #[bundle(name = "some_file", file = "data/text")] #[bundle(name = "my_rustc_version", command = "rustc --version")] enum Bundler{} assert_eq!( Bundler::some_file(), r#"Some Text To Read"# ); let mut stdout = String::new(); std::process::Command::new("rustc") .arg("--version") .stdout(Stdio::piped()) .spawn() .unwrap() .stdout .unwrap() .read_to_string(&mut stdout) .expect("failed to run rustc"); assert_eq!(Bundler::my_rustc_version(), stdout); ```