├── .gitignore ├── LICENSE ├── README.md ├── ch1 ├── Cargo.lock ├── Cargo.toml ├── ch1-first-program │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs └── ch1-generic-functions │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ └── src │ └── main.rs ├── ch10 ├── ch10-both-path-and-exclude-exercise │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── config-macro-usage │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── src │ │ │ └── main.rs │ │ └── tests │ │ │ └── configuration │ │ │ └── config.yaml │ ├── config-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── input.rs │ │ │ ├── lib.rs │ │ │ ├── output.rs │ │ │ └── struct_output.rs │ └── configuration │ │ └── config.yaml ├── ch10-documentation │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── config-macro-usage │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── src │ │ │ └── main.rs │ │ └── tests │ │ │ ├── compilation_tests.rs │ │ │ ├── configuration │ │ │ ├── config.yaml │ │ │ └── invalid.yaml │ │ │ └── fails │ │ │ ├── missing_equals.rs │ │ │ ├── missing_equals.stderr │ │ │ ├── missing_value_after_equals.rs │ │ │ ├── missing_value_after_equals.stderr │ │ │ ├── unknown_config_file.rs │ │ │ ├── unknown_config_file.stderr │ │ │ ├── unknown_config_keyword.rs │ │ │ └── unknown_config_keyword.stderr │ ├── config-macro │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ │ ├── input.rs │ │ │ ├── lib.rs │ │ │ ├── output.rs │ │ │ └── struct_output.rs │ └── configuration │ │ └── config.yaml ├── ch10-features-exclude-exercise │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── config-macro-usage │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── main.rs │ ├── config-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── input.rs │ │ │ ├── lib.rs │ │ │ ├── output.rs │ │ │ └── struct_output.rs │ └── configuration │ │ └── config.yaml ├── ch10-features │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── config-macro-usage │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── src │ │ │ └── main.rs │ │ └── tests │ │ │ ├── compilation_tests.rs │ │ │ ├── configuration │ │ │ ├── config.yaml │ │ │ └── invalid.yaml │ │ │ └── fails │ │ │ ├── missing_equals.rs │ │ │ ├── missing_equals.stderr │ │ │ ├── missing_value_after_equals.rs │ │ │ ├── missing_value_after_equals.stderr │ │ │ ├── unknown_config_file.rs │ │ │ ├── unknown_config_file.stderr │ │ │ ├── unknown_config_keyword.rs │ │ │ └── unknown_config_keyword.stderr │ ├── config-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── input.rs │ │ │ ├── lib.rs │ │ │ ├── output.rs │ │ │ └── struct_output.rs │ └── configuration │ │ └── config.yaml ├── ch10-second-macro │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── config-macro-usage │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── src │ │ │ └── main.rs │ │ └── tests │ │ │ ├── compilation_tests.rs │ │ │ ├── configuration │ │ │ ├── config.yaml │ │ │ └── invalid.yaml │ │ │ └── fails │ │ │ ├── missing_equals.rs │ │ │ ├── missing_equals.stderr │ │ │ ├── missing_value_after_equals.rs │ │ │ ├── missing_value_after_equals.stderr │ │ │ ├── unknown_config_file.rs │ │ │ ├── unknown_config_file.stderr │ │ │ ├── unknown_config_keyword.rs │ │ │ └── unknown_config_keyword.stderr │ ├── config-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── input.rs │ │ │ ├── lib.rs │ │ │ └── output.rs │ └── configuration │ │ └── config.yaml └── ch10-start │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── config-macro-usage │ ├── Cargo.lock │ ├── Cargo.toml │ ├── src │ │ └── main.rs │ └── tests │ │ ├── compilation_tests.rs │ │ ├── configuration │ │ ├── config.yaml │ │ └── invalid.yaml │ │ └── fails │ │ ├── missing_equals.rs │ │ ├── missing_equals.stderr │ │ ├── missing_value_after_equals.rs │ │ ├── missing_value_after_equals.stderr │ │ ├── unknown_config_file.rs │ │ ├── unknown_config_file.stderr │ │ ├── unknown_config_keyword.rs │ │ └── unknown_config_keyword.stderr │ ├── config-macro │ ├── Cargo.toml │ └── src │ │ ├── input.rs │ │ ├── lib.rs │ │ └── output.rs │ └── configuration │ └── config.yaml ├── ch2 ├── Cargo.lock ├── Cargo.toml ├── ch2-compose │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs ├── ch2-dsl-exercise-solution │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs ├── ch2-dsl │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs ├── ch2-greeting │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── greeting.rs │ │ └── main.rs ├── ch2-my-vec-trailing-comma-exercise-solution │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs ├── ch2-my-vec │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs ├── ch2-newtypes-exercise-solutions │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs ├── ch2-newtypes │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs └── ch2-trace-macros │ ├── Cargo.toml │ ├── README.md │ └── src │ ├── greeting.rs │ └── main.rs ├── ch3 ├── Cargo.lock ├── Cargo.toml ├── ch3-fill-in-blanks-exercise-solution │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── ch3-implemented-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── src │ │ └── main.rs ├── ch3-implemented-no-syn-or-quote │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── ch3-implemented-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── src │ │ └── main.rs ├── ch3-implemented │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── ch3-implemented-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── src │ │ └── main.rs ├── ch3-initial-code │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── ch3-initial-code-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── src │ │ └── main.rs ├── ch3-name-to-output-exercise-solution │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── ch3-implemented-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── src │ │ └── main.rs ├── ch3-testing-function-exercise-solution │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── ch3-implemented-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── src │ │ └── main.rs └── ch3-venial │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── ch3-venial-macro │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── lib.rs │ └── src │ └── main.rs ├── ch4 ├── Cargo.lock ├── Cargo.toml ├── ch4-enum-and-unnamed-struct-exercise │ ├── Cargo.toml │ ├── README.md │ ├── make-public-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── src │ │ └── main.rs ├── ch4-public-macro-with-structfield-and-parse-impl │ ├── Cargo.toml │ ├── README.md │ ├── make-public-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── src │ │ └── main.rs ├── ch4-public-macro-with-structfield │ ├── Cargo.toml │ ├── README.md │ ├── make-public-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── src │ │ └── main.rs ├── ch4-public-macro │ ├── Cargo.toml │ ├── README.md │ ├── make-public-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── src │ │ └── main.rs └── ch4-structfield-without-punctuated-exercise │ ├── Cargo.toml │ ├── README.md │ ├── make-public-macro │ ├── Cargo.toml │ └── src │ │ └── lib.rs │ └── src │ └── main.rs ├── ch5 ├── Cargo.lock ├── Cargo.toml ├── ch5-compose-with-different-token-exercise │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── ch5-function-like-compose-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── src │ │ └── main.rs ├── ch5-compose │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── ch5-function-like-compose-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── src │ │ └── main.rs ├── ch5-hello-world-only-name-exercise │ ├── Cargo.toml │ ├── README.md │ ├── hello-world-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── src │ │ └── main.rs ├── ch5-information-hiding │ ├── Cargo.toml │ ├── README.md │ ├── private-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── src │ │ └── main.rs └── ch5-private-fields-exercise │ ├── Cargo.toml │ ├── README.md │ ├── private-macro │ ├── Cargo.toml │ └── src │ │ └── lib.rs │ └── src │ ├── main.rs │ └── other_file.rs ├── ch6 ├── ch6-first-setup │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── builder-code │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── builder-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── builder-usage │ │ ├── Cargo.toml │ │ └── src │ │ └── main.rs ├── ch6-implemented │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── builder-code │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── builder-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── builder-usage │ │ ├── Cargo.toml │ │ └── src │ │ └── main.rs ├── ch6-refactored │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── builder-code │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── fields.rs │ │ │ └── lib.rs │ ├── builder-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── builder-usage │ │ ├── Cargo.toml │ │ └── src │ │ └── main.rs └── ch6-unhappy-path │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── builder-code │ ├── Cargo.toml │ └── src │ │ ├── fields.rs │ │ └── lib.rs │ ├── builder-macro │ ├── Cargo.toml │ └── src │ │ └── lib.rs │ └── builder-usage │ ├── Cargo.toml │ ├── src │ └── main.rs │ └── tests │ ├── compilation_tests.rs │ └── fails │ ├── build_enum.rs │ └── build_enum.stderr ├── ch7 ├── ch7-better-errors │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── panic-to-result-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── panic-to-result-usage │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── src │ │ └── main.rs │ │ └── tests │ │ ├── compilation_tests.rs │ │ └── fails │ │ ├── create_person_with_empty_panic.rs │ │ ├── create_person_with_empty_panic.stderr │ │ ├── create_person_with_result.rs │ │ ├── create_person_with_result.stderr │ │ ├── create_person_with_result_and_empty_panic.rs │ │ └── create_person_with_result_and_empty_panic.stderr ├── ch7-expand-panic-exercise │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── panic-to-result-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── panic-to-result-usage │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ └── main.rs ├── ch7-proc-macro-errors-exercise │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── private-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── src │ │ ├── main.rs │ │ └── other_file.rs ├── ch7-proc-macro-errors │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── panic-to-result-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── panic-to-result-usage │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── src │ │ └── main.rs │ │ └── tests │ │ ├── compilation_tests.rs │ │ └── fails │ │ ├── create_person_two_issues.rs │ │ ├── create_person_two_issues.stderr │ │ ├── create_person_with_empty_panic.rs │ │ ├── create_person_with_empty_panic.stderr │ │ ├── create_person_with_result.rs │ │ └── create_person_with_result.stderr ├── ch7-public-fields-exercise │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── make-public-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── src │ │ └── main.rs ├── ch7-result-instead-of-panic │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── panic-to-result-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── panic-to-result-usage │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ └── main.rs ├── ch7-setup │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── panic-to-result-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── panic-to-result-usage │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ └── main.rs ├── ch7-signature-changed │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── panic-to-result-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── panic-to-result-usage │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ └── main.rs └── ch7-syn-errors-exercise │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── private-macro │ ├── Cargo.toml │ └── src │ │ └── lib.rs │ └── src │ ├── main.rs │ └── other_file.rs ├── ch8 ├── ch8-build-back-better │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── builder-code │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── fields.rs │ │ │ ├── lib.rs │ │ │ └── util.rs │ ├── builder-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── builder-usage │ │ ├── Cargo.toml │ │ ├── src │ │ └── main.rs │ │ └── tests │ │ ├── compilation_tests.rs │ │ └── fails │ │ ├── missing_prop.rs │ │ └── missing_prop.stderr ├── ch8-builder-rename-exercise │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── builder-code │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── fields.rs │ │ │ └── lib.rs │ ├── builder-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── builder-usage │ │ ├── Cargo.toml │ │ └── src │ │ └── main.rs ├── ch8-default-assertion-capital-letter-exercise │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── builder-code │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── fields.rs │ │ │ └── lib.rs │ ├── builder-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── builder-usage │ │ ├── Cargo.toml │ │ ├── src │ │ └── main.rs │ │ └── tests │ │ ├── compilation_tests.rs │ │ └── fails │ │ ├── no_default.rs │ │ └── no_default.stderr ├── ch8-defaults │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── builder-code │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── fields.rs │ │ │ └── lib.rs │ ├── builder-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── builder-usage │ │ ├── Cargo.toml │ │ └── src │ │ └── main.rs ├── ch8-error-for-defaults │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── builder-code │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── fields.rs │ │ │ └── lib.rs │ ├── builder-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── builder-usage │ │ ├── Cargo.toml │ │ ├── src │ │ └── main.rs │ │ └── tests │ │ ├── compilation_tests.rs │ │ └── fails │ │ ├── no_default.rs │ │ └── no_default.stderr ├── ch8-other-attributes │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── other-attributes-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── src │ │ └── main.rs ├── ch8-public-macro-with-attributes │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── make-public-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── src │ │ ├── example.rs │ │ └── main.rs ├── ch8-rename-attribute-alternative │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── builder-code │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── fields.rs │ │ │ └── lib.rs │ ├── builder-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── builder-usage │ │ ├── Cargo.toml │ │ └── src │ │ └── main.rs ├── ch8-rename-attribute │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── builder-code │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── fields.rs │ │ │ └── lib.rs │ ├── builder-macro │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── builder-usage │ │ ├── Cargo.toml │ │ └── src │ │ └── main.rs └── ch8-uppercasing-exercise │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── builder-code │ ├── Cargo.toml │ └── src │ │ ├── fields.rs │ │ └── lib.rs │ ├── builder-macro │ ├── Cargo.toml │ └── src │ │ └── lib.rs │ └── builder-usage │ ├── Cargo.toml │ ├── src │ └── main.rs │ └── tests │ ├── compilation_tests.rs │ └── fails │ ├── wrong_type.rs │ └── wrong_type.stderr ├── ch9 ├── ch9-better-input-modelling-exercise │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── example │ │ └── handler.js │ ├── iac-macro-usage │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── main.rs │ └── iac-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ ├── errors.rs │ │ ├── input.rs │ │ ├── lambda.rs │ │ ├── lib.rs │ │ └── s3.rs ├── ch9-checking-resources-exercise │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── example │ │ └── handler.js │ ├── iac-macro-usage │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── main.rs │ └── iac-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ ├── errors.rs │ │ ├── input.rs │ │ ├── lambda.rs │ │ ├── lib.rs │ │ └── s3.rs ├── ch9-start-alternative │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── iac-macro-usage │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── src │ │ │ └── main.rs │ │ └── tests │ │ │ ├── compilation_tests.rs │ │ │ └── fails │ │ │ ├── bucket_and_no_lambda_parentheses.rs │ │ │ ├── bucket_and_no_lambda_parentheses.stderr │ │ │ ├── bucket_typo.rs │ │ │ ├── bucket_typo.stderr │ │ │ ├── lambda_colon_instead_of_equals.rs │ │ │ ├── lambda_colon_instead_of_equals.stderr │ │ │ ├── no_bucket_name.rs │ │ │ ├── no_bucket_name.stderr │ │ │ ├── no_lambda_for_event.rs │ │ │ ├── no_lambda_for_event.stderr │ │ │ ├── no_lambda_name.rs │ │ │ └── no_lambda_name.stderr │ └── iac-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ └── lib.rs ├── ch9-start-another-alternative │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── iac-macro-usage │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── src │ │ │ └── main.rs │ │ └── tests │ │ │ ├── compilation_tests.rs │ │ │ └── fails │ │ │ ├── bucket_and_no_lambda_parentheses.rs │ │ │ ├── bucket_and_no_lambda_parentheses.stderr │ │ │ ├── bucket_typo.rs │ │ │ ├── bucket_typo.stderr │ │ │ ├── lambda_colon_instead_of_equals.rs │ │ │ ├── lambda_colon_instead_of_equals.stderr │ │ │ ├── no_bucket_name.rs │ │ │ ├── no_bucket_name.stderr │ │ │ ├── no_lambda_for_event.rs │ │ │ ├── no_lambda_for_event.stderr │ │ │ ├── no_lambda_name.rs │ │ │ └── no_lambda_name.stderr │ └── iac-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ └── lib.rs ├── ch9-start │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── iac-macro-usage │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── src │ │ │ └── main.rs │ │ └── tests │ │ │ ├── compilation_tests.rs │ │ │ └── fails │ │ │ ├── bucket_and_no_lambda_name.rs │ │ │ ├── bucket_and_no_lambda_name.stderr │ │ │ ├── bucket_typo.rs │ │ │ ├── bucket_typo.stderr │ │ │ ├── lambda_fake_property.rs │ │ │ ├── lambda_fake_property.stderr │ │ │ ├── lambda_negative_mem.rs │ │ │ ├── lambda_negative_mem.stderr │ │ │ ├── lambda_negative_time.rs │ │ │ ├── lambda_negative_time.stderr │ │ │ ├── lambda_time_not_a_number.rs │ │ │ ├── lambda_time_not_a_number.stderr │ │ │ ├── lambda_typo.rs │ │ │ ├── lambda_typo.stderr │ │ │ ├── no_bucket_name.rs │ │ │ ├── no_bucket_name.stderr │ │ │ ├── no_lambda_for_event.rs │ │ │ ├── no_lambda_for_event.stderr │ │ │ ├── no_lambda_name.rs │ │ │ └── no_lambda_name.stderr │ └── iac-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ └── lib.rs ├── ch9-suggestions-exercise │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── iac-macro-usage │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── src │ │ │ └── main.rs │ │ └── tests │ │ │ ├── compilation_tests.rs │ │ │ └── fails │ │ │ ├── another_bucket_typo.rs │ │ │ ├── another_bucket_typo.stderr │ │ │ ├── bucket_and_no_lambda_name.rs │ │ │ ├── bucket_and_no_lambda_name.stderr │ │ │ ├── bucket_typo.rs │ │ │ ├── bucket_typo.stderr │ │ │ ├── lambda_another_typo.rs │ │ │ ├── lambda_another_typo.stderr │ │ │ ├── lambda_fake_property.rs │ │ │ ├── lambda_fake_property.stderr │ │ │ ├── lambda_negative_mem.rs │ │ │ ├── lambda_negative_mem.stderr │ │ │ ├── lambda_negative_time.rs │ │ │ ├── lambda_negative_time.stderr │ │ │ ├── lambda_time_not_a_number.rs │ │ │ ├── lambda_time_not_a_number.stderr │ │ │ ├── lambda_typo.rs │ │ │ ├── lambda_typo.stderr │ │ │ ├── no_bucket_name.rs │ │ │ ├── no_bucket_name.stderr │ │ │ ├── no_lambda_for_event.rs │ │ │ ├── no_lambda_for_event.stderr │ │ │ ├── no_lambda_name.rs │ │ │ ├── no_lambda_name.stderr │ │ │ ├── really_unknown_property.rs │ │ │ └── really_unknown_property.stderr │ └── iac-macro │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ └── lib.rs └── ch9-with-sdk-calls │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── example │ └── handler.js │ ├── iac-macro-usage │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── main.rs │ └── iac-macro │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ ├── errors.rs │ ├── input.rs │ ├── lambda.rs │ ├── lib.rs │ └── s3.rs ├── rust-toolchain.toml └── util └── create_setup.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target 3 | temp 4 | *.zip -------------------------------------------------------------------------------- /ch1/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "ch1-first-program", 6 | "ch1-generic-functions" 7 | ] 8 | -------------------------------------------------------------------------------- /ch1/ch1-first-program/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch1-first-program" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | serde = { version = "1.0.192", features = ["derive"] } 8 | serde_json = "1.0.108" 9 | -------------------------------------------------------------------------------- /ch1/ch1-first-program/README.md: -------------------------------------------------------------------------------- 1 | # First Program 2 | 3 | ## Overview 4 | 5 | This is the example program from the start of the book (chapter 1, the introduction). 6 | 7 | It tries to show just how ubiquitous macros are in the Rust world: even a simple program with less than 40 lines of code might use multiple declarative and procedural macros for accomplishing relatively simple things! 8 | -------------------------------------------------------------------------------- /ch1/ch1-first-program/src/main.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | 3 | #[derive(Deserialize)] 4 | struct Request { 5 | given_name: String, 6 | last_name: String, 7 | } 8 | 9 | fn full_name(given: &str, last: &str) -> String { 10 | format!("{} {}", given, last) 11 | } 12 | 13 | fn main() { 14 | let r = Request { 15 | given_name: "Sam".to_string(), 16 | last_name: "Hall".to_string() 17 | }; 18 | dbg!(full_name(&r.given_name, &r.last_name)); 19 | } 20 | 21 | #[cfg(test)] 22 | mod tests { 23 | use super::*; 24 | 25 | #[test] 26 | fn test_deserialize() { 27 | let actual: Request = serde_json::from_str("{ \"given_name\": \"Test\", \"last_name\": \"McTest\" }") 28 | .expect("deserialize to work"); 29 | 30 | assert_eq!(actual.given_name, "Test".to_string()); 31 | assert_eq!(actual.last_name, "McTest".to_string()); 32 | } 33 | } -------------------------------------------------------------------------------- /ch1/ch1-generic-functions/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ch1-first-program" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /ch1/ch1-generic-functions/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch1-generic-functions" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /ch1/ch1-generic-functions/README.md: -------------------------------------------------------------------------------- 1 | # Generic functions 2 | 3 | ## Overview 4 | 5 | We briefly discuss generics and blanket implementations in chapter 1. 6 | This is the code that we show in that context. 7 | -------------------------------------------------------------------------------- /ch1/ch1-generic-functions/src/main.rs: -------------------------------------------------------------------------------- 1 | trait Hello { 2 | fn hello(&self); 3 | } 4 | 5 | impl Hello for T { 6 | fn hello(&self) { 7 | println!("Hello world"); 8 | } 9 | } 10 | 11 | fn main() { 12 | 2.hello(); 13 | true.hello(); 14 | 'c'.hello(); 15 | } 16 | -------------------------------------------------------------------------------- /ch10/ch10-both-path-and-exclude-exercise/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "config-macro", 6 | "config-macro-usage" 7 | ] -------------------------------------------------------------------------------- /ch10/ch10-both-path-and-exclude-exercise/README.md: -------------------------------------------------------------------------------- 1 | # Both path and exclude 2 | 3 | ## Overview 4 | 5 | This is the solution to the exercise from chapter 10 where we ask you to allow both path and exclude. 6 | -------------------------------------------------------------------------------- /ch10/ch10-both-path-and-exclude-exercise/config-macro-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "config-macro-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | config-macro = { path = "../config-macro", features = ["struct"] } 8 | -------------------------------------------------------------------------------- /ch10/ch10-both-path-and-exclude-exercise/config-macro-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config_struct; 2 | 3 | #[config_struct(path = "./config-macro-usage/tests/configuration/config.yaml" exclude = "from")] 4 | #[derive(Debug)] 5 | struct ConfigStruct {} 6 | 7 | fn main() { 8 | let config = ConfigStruct::new(); 9 | println!("{config:?}"); 10 | } 11 | 12 | #[cfg(test)] 13 | mod tests { 14 | use std::collections::HashMap; 15 | use config_macro::{config, config_struct}; 16 | 17 | #[test] 18 | fn should_generate_config_struct_with_expected_values() { 19 | config!(); 20 | 21 | let cfg = Config::new(); 22 | let user = cfg.0.get("user").unwrap(); 23 | 24 | assert_eq!(user, "admin"); 25 | } 26 | 27 | #[test] 28 | fn should_generate_config_for_existing_struct_with_from_method() { 29 | #[config_struct(path = "./config-macro-usage/tests/configuration/config.yaml")] 30 | struct MyConfigStruct {} 31 | 32 | let cfg = MyConfigStruct::new(); 33 | let as_map: HashMap = cfg.into(); 34 | 35 | assert_eq!(as_map.get("user").unwrap(), "test"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ch10/ch10-both-path-and-exclude-exercise/config-macro-usage/tests/configuration/config.yaml: -------------------------------------------------------------------------------- 1 | user: "test" 2 | -------------------------------------------------------------------------------- /ch10/ch10-both-path-and-exclude-exercise/config-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "config-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | serde = "1.0.192" 10 | serde_yaml = "0.9.27" 11 | proc-macro2 = "1.0.69" 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [features] 17 | struct = [] 18 | -------------------------------------------------------------------------------- /ch10/ch10-both-path-and-exclude-exercise/config-macro/src/output.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use proc_macro2::{TokenStream}; 4 | use quote::quote; 5 | 6 | fn generate_inserts(yaml_values: HashMap) -> Vec { 7 | yaml_values.iter().map(|v| { 8 | let key = v.0; 9 | let value = v.1; 10 | quote!(map.insert(#key.to_string(), #value.to_string());) 11 | }).collect() 12 | } 13 | 14 | pub fn generate_config_struct(yaml_values: HashMap) -> TokenStream { 15 | let inserts = generate_inserts(yaml_values); 16 | 17 | quote! { 18 | pub struct Config(pub std::collections::HashMap); 19 | 20 | impl Config { 21 | pub fn new() -> Self { 22 | let mut map = std::collections::HashMap::new(); 23 | #(#inserts)* 24 | Config(map) 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ch10/ch10-both-path-and-exclude-exercise/configuration/config.yaml: -------------------------------------------------------------------------------- 1 | user: "admin" 2 | password: "admin" 3 | -------------------------------------------------------------------------------- /ch10/ch10-documentation/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "config-macro", 6 | "config-macro-usage" 7 | ] -------------------------------------------------------------------------------- /ch10/ch10-documentation/README.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | ## Overview 4 | 5 | This is the fourth implementation of the 'config' macro from chapter 10. 6 | 7 | In this phase, we add documentation and some publish info 8 | -------------------------------------------------------------------------------- /ch10/ch10-documentation/config-macro-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "config-macro-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | config-macro = { path = "../config-macro", features = ["struct"] } 8 | 9 | [dev-dependencies] 10 | trybuild = "1.0.85" 11 | -------------------------------------------------------------------------------- /ch10/ch10-documentation/config-macro-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config_struct; 2 | 3 | #[config_struct(exclude = "from")] 4 | #[derive(Debug)] 5 | struct ConfigStruct {} 6 | 7 | fn main() { 8 | let config = ConfigStruct::new(); 9 | println!("{config:?}"); 10 | } 11 | 12 | #[cfg(test)] 13 | mod tests { 14 | use std::collections::HashMap; 15 | use config_macro::{config, config_struct}; 16 | 17 | #[test] 18 | fn should_generate_config_struct_with_expected_values() { 19 | config!(); 20 | 21 | let cfg = Config::new(); 22 | let user = cfg.0.get("user").unwrap(); 23 | 24 | assert_eq!(user, "admin"); 25 | } 26 | 27 | #[test] 28 | fn should_generate_config_for_existing_struct_with_from_method() { 29 | #[config_struct(path = "./config-macro-usage/tests/configuration/config.yaml")] 30 | struct MyConfigStruct {} 31 | 32 | let cfg = MyConfigStruct::new(); 33 | let as_map: HashMap = cfg.into(); 34 | 35 | assert_eq!(as_map.get("user").unwrap(), "test"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ch10/ch10-documentation/config-macro-usage/tests/compilation_tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn should_not_compile() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/fails/*.rs"); 5 | } -------------------------------------------------------------------------------- /ch10/ch10-documentation/config-macro-usage/tests/configuration/config.yaml: -------------------------------------------------------------------------------- 1 | user: "test" 2 | -------------------------------------------------------------------------------- /ch10/ch10-documentation/config-macro-usage/tests/configuration/invalid.yaml: -------------------------------------------------------------------------------- 1 | nested: 2 | user: "test" 3 | -------------------------------------------------------------------------------- /ch10/ch10-documentation/config-macro-usage/tests/fails/missing_equals.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config; 2 | 3 | config!(path "stuff"); 4 | 5 | fn main() {} -------------------------------------------------------------------------------- /ch10/ch10-documentation/config-macro-usage/tests/fails/missing_equals.stderr: -------------------------------------------------------------------------------- 1 | error: expected equals sign after path 2 | --> tests/fails/missing_equals.rs:3:14 3 | | 4 | 3 | config!(path "stuff"); 5 | | ^^^^^^^ 6 | -------------------------------------------------------------------------------- /ch10/ch10-documentation/config-macro-usage/tests/fails/missing_value_after_equals.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config; 2 | 3 | config!(path =); 4 | 5 | fn main() {} -------------------------------------------------------------------------------- /ch10/ch10-documentation/config-macro-usage/tests/fails/missing_value_after_equals.stderr: -------------------------------------------------------------------------------- 1 | error: expected value after the equals sign 2 | --> tests/fails/missing_value_after_equals.rs:3:1 3 | | 4 | 3 | config!(path =); 5 | | ^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the macro `config` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /ch10/ch10-documentation/config-macro-usage/tests/fails/unknown_config_file.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config; 2 | 3 | config!(path = "./fake.yaml"); 4 | 5 | fn main() {} -------------------------------------------------------------------------------- /ch10/ch10-documentation/config-macro-usage/tests/fails/unknown_config_file.stderr: -------------------------------------------------------------------------------- 1 | error: could not find config with path ./fake.yaml 2 | --> tests/fails/unknown_config_file.rs:3:1 3 | | 4 | 3 | config!(path = "./fake.yaml"); 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the macro `config` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /ch10/ch10-documentation/config-macro-usage/tests/fails/unknown_config_keyword.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config; 2 | 3 | config!(unknown = stuff); 4 | 5 | fn main() {} -------------------------------------------------------------------------------- /ch10/ch10-documentation/config-macro-usage/tests/fails/unknown_config_keyword.stderr: -------------------------------------------------------------------------------- 1 | error: config macro only allows for 'path' input 2 | --> tests/fails/unknown_config_keyword.rs:3:9 3 | | 4 | 3 | config!(unknown = stuff); 5 | | ^^^^^^^ 6 | -------------------------------------------------------------------------------- /ch10/ch10-documentation/config-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "config-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | description = "Macros for using config as a struct within your application" 7 | license = "MIT" 8 | homepage = "https://github.com/some-page" 9 | repository = "https://github.com/some-page" 10 | readme = "README.md" 11 | keywords = ["configuration", "yaml", "macro"] 12 | 13 | [dependencies] 14 | quote = "1.0.33" 15 | syn = { version = "2.0.39", features = ["extra-traits"]} 16 | serde = "1.0.192" 17 | serde_yaml = "0.9.27" 18 | proc-macro2 = "1.0.69" 19 | 20 | [lib] 21 | proc-macro = true 22 | 23 | [features] 24 | struct = [] 25 | -------------------------------------------------------------------------------- /ch10/ch10-documentation/config-macro/README.md: -------------------------------------------------------------------------------- 1 | # Config Macro 2 | 3 | ## Overview 4 | 5 | This crate contains macros that allow you to transform yaml config into a struct that you can use in your application. 6 | 7 | ## Usage 8 | 9 | Call either the function-like `config!` macro or annotate a (preferably empty) struct with the attribute macro `#[config_struct]`. 10 | By default, the macro looks for configuration under `configuration/config.yaml`. 11 | This can be overwritten by using the 'path' attribute: `config!(path = "a/path/to.yaml")` or `#[config_struct(path = "a/path/to.yaml")]`. 12 | 13 | ## Usage example 14 | 15 | ```rust 16 | use config_macro::config; 17 | 18 | config!(path = "./configuration/config.yaml"); 19 | 20 | // we can now call new and access the hashmap of values 21 | let c = Config::new(); 22 | ``` 23 | 24 | ## Features 25 | 26 | The annotation macro is hidden behind the 'struct' feature. 27 | 28 | ## Caveats 29 | 30 | Currently only works with YAML files, and does not support nesting. 31 | So this works: 32 | 33 | ```yaml 34 | user: "admin" 35 | ``` 36 | 37 | But this won't: 38 | 39 | ```yaml 40 | database: 41 | user: "admin" 42 | ``` 43 | 44 | Furthermore, we will read all properties in the config as `String`s. 45 | -------------------------------------------------------------------------------- /ch10/ch10-documentation/config-macro/src/output.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use proc_macro2::{TokenStream}; 4 | use quote::quote; 5 | 6 | fn generate_inserts(yaml_values: HashMap) -> Vec { 7 | yaml_values.iter().map(|v| { 8 | let key = v.0; 9 | let value = v.1; 10 | quote!(map.insert(#key.to_string(), #value.to_string());) 11 | }).collect() 12 | } 13 | 14 | pub fn generate_config_struct(yaml_values: HashMap) -> TokenStream { 15 | let inserts = generate_inserts(yaml_values); 16 | 17 | quote! { 18 | pub struct Config(pub std::collections::HashMap); 19 | 20 | impl Config { 21 | pub fn new() -> Self { 22 | let mut map = std::collections::HashMap::new(); 23 | #(#inserts)* 24 | Config(map) 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ch10/ch10-documentation/configuration/config.yaml: -------------------------------------------------------------------------------- 1 | user: "admin" 2 | password: "admin" 3 | -------------------------------------------------------------------------------- /ch10/ch10-features-exclude-exercise/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "config-macro", 6 | "config-macro-usage" 7 | ] -------------------------------------------------------------------------------- /ch10/ch10-features-exclude-exercise/README.md: -------------------------------------------------------------------------------- 1 | # Exclude feature 2 | 3 | ## Overview 4 | 5 | This is the solution to the exercise from chapter 10 where we ask you to use features to exclude the `From` generation. 6 | -------------------------------------------------------------------------------- /ch10/ch10-features-exclude-exercise/config-macro-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "config-macro-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | config-macro = { path = "../config-macro", features = ["struct", "from"] } 8 | -------------------------------------------------------------------------------- /ch10/ch10-features-exclude-exercise/config-macro-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use config_macro::config_struct; 4 | 5 | #[config_struct] 6 | #[derive(Debug)] 7 | struct ConfigStruct {} 8 | 9 | fn main() { 10 | let config = ConfigStruct::new(); 11 | let map: HashMap = config.into(); 12 | println!("{map:?}"); 13 | } 14 | -------------------------------------------------------------------------------- /ch10/ch10-features-exclude-exercise/config-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "config-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | serde = "1.0.192" 10 | serde_yaml = "0.9.27" 11 | proc-macro2 = "1.0.69" 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [features] 17 | struct = [] 18 | from = [] 19 | -------------------------------------------------------------------------------- /ch10/ch10-features-exclude-exercise/config-macro/src/output.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use proc_macro2::{TokenStream}; 4 | use quote::quote; 5 | 6 | fn generate_inserts(yaml_values: HashMap) -> Vec { 7 | yaml_values.iter().map(|v| { 8 | let key = v.0; 9 | let value = v.1; 10 | quote!(map.insert(#key.to_string(), #value.to_string());) 11 | }).collect() 12 | } 13 | 14 | pub fn generate_config_struct(yaml_values: HashMap) -> TokenStream { 15 | let inserts = generate_inserts(yaml_values); 16 | 17 | quote! { 18 | pub struct Config(pub std::collections::HashMap); 19 | 20 | impl Config { 21 | pub fn new() -> Self { 22 | let mut map = std::collections::HashMap::new(); 23 | #(#inserts)* 24 | Config(map) 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ch10/ch10-features-exclude-exercise/configuration/config.yaml: -------------------------------------------------------------------------------- 1 | user: "admin" 2 | password: "admin" 3 | -------------------------------------------------------------------------------- /ch10/ch10-features/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "config-macro", 6 | "config-macro-usage" 7 | ] -------------------------------------------------------------------------------- /ch10/ch10-features/README.md: -------------------------------------------------------------------------------- 1 | # Features 2 | 3 | ## Overview 4 | 5 | This is the third implementation of the 'config' macro from chapter 10. 6 | 7 | In this phase, we add a feature and show how to exclude generation of token streams. 8 | -------------------------------------------------------------------------------- /ch10/ch10-features/config-macro-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "config-macro-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | config-macro = { path = "../config-macro", features = ["struct"] } 8 | 9 | [dev-dependencies] 10 | trybuild = "1.0.85" 11 | -------------------------------------------------------------------------------- /ch10/ch10-features/config-macro-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config_struct; 2 | 3 | #[config_struct(exclude = "from")] 4 | #[derive(Debug)] 5 | struct ConfigStruct {} 6 | 7 | fn main() { 8 | let config = ConfigStruct::new(); 9 | println!("{config:?}"); 10 | } 11 | 12 | #[cfg(test)] 13 | mod tests { 14 | use std::collections::HashMap; 15 | use config_macro::{config, config_struct}; 16 | 17 | #[test] 18 | fn should_generate_config_struct_with_expected_values() { 19 | config!(); 20 | 21 | let cfg = Config::new(); 22 | let user = cfg.0.get("user").unwrap(); 23 | 24 | assert_eq!(user, "admin"); 25 | } 26 | 27 | #[test] 28 | fn should_generate_config_for_existing_struct_with_from_method() { 29 | #[config_struct(path = "./config-macro-usage/tests/configuration/config.yaml")] 30 | struct MyConfigStruct {} 31 | 32 | let cfg = MyConfigStruct::new(); 33 | let as_map: HashMap = cfg.into(); 34 | 35 | assert_eq!(as_map.get("user").unwrap(), "test"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ch10/ch10-features/config-macro-usage/tests/compilation_tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn should_not_compile() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/fails/*.rs"); 5 | } -------------------------------------------------------------------------------- /ch10/ch10-features/config-macro-usage/tests/configuration/config.yaml: -------------------------------------------------------------------------------- 1 | user: "test" 2 | -------------------------------------------------------------------------------- /ch10/ch10-features/config-macro-usage/tests/configuration/invalid.yaml: -------------------------------------------------------------------------------- 1 | nested: 2 | user: "test" 3 | -------------------------------------------------------------------------------- /ch10/ch10-features/config-macro-usage/tests/fails/missing_equals.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config; 2 | 3 | config!(path "stuff"); 4 | 5 | fn main() {} -------------------------------------------------------------------------------- /ch10/ch10-features/config-macro-usage/tests/fails/missing_equals.stderr: -------------------------------------------------------------------------------- 1 | error: expected equals sign after path 2 | --> tests/fails/missing_equals.rs:3:14 3 | | 4 | 3 | config!(path "stuff"); 5 | | ^^^^^^^ 6 | -------------------------------------------------------------------------------- /ch10/ch10-features/config-macro-usage/tests/fails/missing_value_after_equals.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config; 2 | 3 | config!(path =); 4 | 5 | fn main() {} -------------------------------------------------------------------------------- /ch10/ch10-features/config-macro-usage/tests/fails/missing_value_after_equals.stderr: -------------------------------------------------------------------------------- 1 | error: expected value after the equals sign 2 | --> tests/fails/missing_value_after_equals.rs:3:1 3 | | 4 | 3 | config!(path =); 5 | | ^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the macro `config` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /ch10/ch10-features/config-macro-usage/tests/fails/unknown_config_file.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config; 2 | 3 | config!(path = "./fake.yaml"); 4 | 5 | fn main() {} -------------------------------------------------------------------------------- /ch10/ch10-features/config-macro-usage/tests/fails/unknown_config_file.stderr: -------------------------------------------------------------------------------- 1 | error: could not find config with path ./fake.yaml 2 | --> tests/fails/unknown_config_file.rs:3:1 3 | | 4 | 3 | config!(path = "./fake.yaml"); 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the macro `config` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /ch10/ch10-features/config-macro-usage/tests/fails/unknown_config_keyword.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config; 2 | 3 | config!(unknown = stuff); 4 | 5 | fn main() {} -------------------------------------------------------------------------------- /ch10/ch10-features/config-macro-usage/tests/fails/unknown_config_keyword.stderr: -------------------------------------------------------------------------------- 1 | error: config macro only allows for 'path' input 2 | --> tests/fails/unknown_config_keyword.rs:3:9 3 | | 4 | 3 | config!(unknown = stuff); 5 | | ^^^^^^^ 6 | -------------------------------------------------------------------------------- /ch10/ch10-features/config-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "config-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | serde = "1.0.192" 10 | serde_yaml = "0.9.27" 11 | proc-macro2 = "1.0.69" 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [features] 17 | struct = [] 18 | -------------------------------------------------------------------------------- /ch10/ch10-features/config-macro/src/output.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use proc_macro2::{TokenStream}; 4 | use quote::quote; 5 | 6 | fn generate_inserts(yaml_values: HashMap) -> Vec { 7 | yaml_values.iter().map(|v| { 8 | let key = v.0; 9 | let value = v.1; 10 | quote!(map.insert(#key.to_string(), #value.to_string());) 11 | }).collect() 12 | } 13 | 14 | pub fn generate_config_struct(yaml_values: HashMap) -> TokenStream { 15 | let inserts = generate_inserts(yaml_values); 16 | 17 | quote! { 18 | pub struct Config(pub std::collections::HashMap); 19 | 20 | impl Config { 21 | pub fn new() -> Self { 22 | let mut map = std::collections::HashMap::new(); 23 | #(#inserts)* 24 | Config(map) 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ch10/ch10-features/configuration/config.yaml: -------------------------------------------------------------------------------- 1 | user: "admin" 2 | password: "admin" 3 | -------------------------------------------------------------------------------- /ch10/ch10-second-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "config-macro", 6 | "config-macro-usage" 7 | ] -------------------------------------------------------------------------------- /ch10/ch10-second-macro/README.md: -------------------------------------------------------------------------------- 1 | # Second Macro 2 | 3 | ## Overview 4 | 5 | This is the second implementation of the 'config' macro from chapter 10. 6 | 7 | In this phase, we add a second macro to the project. 8 | -------------------------------------------------------------------------------- /ch10/ch10-second-macro/config-macro-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "config-macro-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | config-macro = { path = "../config-macro" } 8 | 9 | [dev-dependencies] 10 | trybuild = "1.0.85" 11 | -------------------------------------------------------------------------------- /ch10/ch10-second-macro/config-macro-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config_struct; 2 | 3 | #[config_struct] 4 | #[derive(Debug)] 5 | struct ConfigStruct {} 6 | 7 | fn main() { 8 | let config = ConfigStruct::new(); 9 | println!("{config:?}") 10 | } 11 | 12 | #[cfg(test)] 13 | mod tests { 14 | use config_macro::{config,config_struct}; 15 | 16 | #[test] 17 | fn should_generate_config_struct_with_expected_values() { 18 | config!(); 19 | 20 | let cfg = Config::new(); 21 | let user = cfg.0.get("user").unwrap(); 22 | 23 | assert_eq!(user, "admin"); 24 | } 25 | 26 | #[test] 27 | fn should_generate_config_for_existing_struct_with_expected_values() { 28 | #[config_struct(path = "./config-macro-usage/tests/configuration/config.yaml")] 29 | struct MyConfigStruct {} 30 | 31 | let cfg = MyConfigStruct::new(); 32 | 33 | assert_eq!(cfg.user, "test"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ch10/ch10-second-macro/config-macro-usage/tests/compilation_tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn should_not_compile() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/fails/*.rs"); 5 | } -------------------------------------------------------------------------------- /ch10/ch10-second-macro/config-macro-usage/tests/configuration/config.yaml: -------------------------------------------------------------------------------- 1 | user: "test" 2 | -------------------------------------------------------------------------------- /ch10/ch10-second-macro/config-macro-usage/tests/configuration/invalid.yaml: -------------------------------------------------------------------------------- 1 | nested: 2 | user: "test" 3 | -------------------------------------------------------------------------------- /ch10/ch10-second-macro/config-macro-usage/tests/fails/missing_equals.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config; 2 | 3 | config!(path "stuff"); 4 | 5 | fn main() {} -------------------------------------------------------------------------------- /ch10/ch10-second-macro/config-macro-usage/tests/fails/missing_equals.stderr: -------------------------------------------------------------------------------- 1 | error: expected equals sign after path 2 | --> tests/fails/missing_equals.rs:3:14 3 | | 4 | 3 | config!(path "stuff"); 5 | | ^^^^^^^ 6 | -------------------------------------------------------------------------------- /ch10/ch10-second-macro/config-macro-usage/tests/fails/missing_value_after_equals.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config; 2 | 3 | config!(path =); 4 | 5 | fn main() {} -------------------------------------------------------------------------------- /ch10/ch10-second-macro/config-macro-usage/tests/fails/missing_value_after_equals.stderr: -------------------------------------------------------------------------------- 1 | error: expected value after the equals sign 2 | --> tests/fails/missing_value_after_equals.rs:3:1 3 | | 4 | 3 | config!(path =); 5 | | ^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the macro `config` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /ch10/ch10-second-macro/config-macro-usage/tests/fails/unknown_config_file.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config; 2 | 3 | config!(path = "./fake.yaml"); 4 | 5 | fn main() {} -------------------------------------------------------------------------------- /ch10/ch10-second-macro/config-macro-usage/tests/fails/unknown_config_file.stderr: -------------------------------------------------------------------------------- 1 | error: could not read config with path ./fake.yaml: No such file or directory (os error 2) 2 | --> tests/fails/unknown_config_file.rs:3:1 3 | | 4 | 3 | config!(path = "./fake.yaml"); 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the macro `config` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /ch10/ch10-second-macro/config-macro-usage/tests/fails/unknown_config_keyword.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config; 2 | 3 | config!(unknown = stuff); 4 | 5 | fn main() {} -------------------------------------------------------------------------------- /ch10/ch10-second-macro/config-macro-usage/tests/fails/unknown_config_keyword.stderr: -------------------------------------------------------------------------------- 1 | error: config macro only allows for 'path' input 2 | --> tests/fails/unknown_config_keyword.rs:3:9 3 | | 4 | 3 | config!(unknown = stuff); 5 | | ^^^^^^^ 6 | -------------------------------------------------------------------------------- /ch10/ch10-second-macro/config-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "config-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | serde = "1.0.192" 10 | serde_yaml = "0.9.27" 11 | proc-macro2 = "1.0.69" 12 | 13 | [lib] 14 | proc-macro = true 15 | -------------------------------------------------------------------------------- /ch10/ch10-second-macro/configuration/config.yaml: -------------------------------------------------------------------------------- 1 | user: "admin" 2 | password: "admin" 3 | -------------------------------------------------------------------------------- /ch10/ch10-start/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "config-macro", 6 | "config-macro-usage" 7 | ] -------------------------------------------------------------------------------- /ch10/ch10-start/README.md: -------------------------------------------------------------------------------- 1 | # Start 2 | 3 | ## Overview 4 | 5 | This is the first implementation of the 'config' macro from chapter 10. 6 | 7 | In this phase, we have a function-like macro with proper testing and error-handling, but nothing else. 8 | -------------------------------------------------------------------------------- /ch10/ch10-start/config-macro-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "config-macro-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | config-macro = { path = "../config-macro" } 8 | 9 | [dev-dependencies] 10 | trybuild = "1.0.85" 11 | -------------------------------------------------------------------------------- /ch10/ch10-start/config-macro-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config; 2 | 3 | fn main() { 4 | config!(); 5 | 6 | let cfg = Config::new(); 7 | let user = cfg.0.get("user").unwrap(); 8 | println!("{user}"); 9 | } 10 | 11 | #[cfg(test)] 12 | mod tests { 13 | use config_macro::config; 14 | 15 | #[test] 16 | fn should_generate_config_struct_with_expected_values() { 17 | config!(); 18 | 19 | let cfg = Config::new(); 20 | let user = cfg.0.get("user").unwrap(); 21 | 22 | assert_eq!(user, "admin"); 23 | } 24 | 25 | #[test] 26 | fn should_generate_config_struct_with_expected_values_for_path_override() { 27 | config!(path = "./config-macro-usage/tests/configuration/config.yaml"); 28 | 29 | let cfg = Config::new(); 30 | let user = cfg.0.get("user").unwrap(); 31 | 32 | assert_eq!(user, "test"); 33 | } 34 | } -------------------------------------------------------------------------------- /ch10/ch10-start/config-macro-usage/tests/compilation_tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn should_not_compile() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/fails/*.rs"); 5 | } -------------------------------------------------------------------------------- /ch10/ch10-start/config-macro-usage/tests/configuration/config.yaml: -------------------------------------------------------------------------------- 1 | user: "test" 2 | -------------------------------------------------------------------------------- /ch10/ch10-start/config-macro-usage/tests/configuration/invalid.yaml: -------------------------------------------------------------------------------- 1 | nested: 2 | user: "test" 3 | -------------------------------------------------------------------------------- /ch10/ch10-start/config-macro-usage/tests/fails/missing_equals.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config; 2 | 3 | config!(path "stuff"); 4 | 5 | fn main() {} -------------------------------------------------------------------------------- /ch10/ch10-start/config-macro-usage/tests/fails/missing_equals.stderr: -------------------------------------------------------------------------------- 1 | error: expected equals sign after path 2 | --> tests/fails/missing_equals.rs:3:14 3 | | 4 | 3 | config!(path "stuff"); 5 | | ^^^^^^^ 6 | -------------------------------------------------------------------------------- /ch10/ch10-start/config-macro-usage/tests/fails/missing_value_after_equals.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config; 2 | 3 | config!(path =); 4 | 5 | fn main() {} -------------------------------------------------------------------------------- /ch10/ch10-start/config-macro-usage/tests/fails/missing_value_after_equals.stderr: -------------------------------------------------------------------------------- 1 | error: expected value after the equals sign 2 | --> tests/fails/missing_value_after_equals.rs:3:1 3 | | 4 | 3 | config!(path =); 5 | | ^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the macro `config` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /ch10/ch10-start/config-macro-usage/tests/fails/unknown_config_file.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config; 2 | 3 | config!(path = "./fake.yaml"); 4 | 5 | fn main() {} -------------------------------------------------------------------------------- /ch10/ch10-start/config-macro-usage/tests/fails/unknown_config_file.stderr: -------------------------------------------------------------------------------- 1 | error: could not read config with path ./fake.yaml: No such file or directory (os error 2) 2 | --> tests/fails/unknown_config_file.rs:3:1 3 | | 4 | 3 | config!(path = "./fake.yaml"); 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the macro `config` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /ch10/ch10-start/config-macro-usage/tests/fails/unknown_config_keyword.rs: -------------------------------------------------------------------------------- 1 | use config_macro::config; 2 | 3 | config!(unknown = stuff); 4 | 5 | fn main() {} -------------------------------------------------------------------------------- /ch10/ch10-start/config-macro-usage/tests/fails/unknown_config_keyword.stderr: -------------------------------------------------------------------------------- 1 | error: config macro only allows for 'path' input 2 | --> tests/fails/unknown_config_keyword.rs:3:9 3 | | 4 | 3 | config!(unknown = stuff); 5 | | ^^^^^^^ 6 | -------------------------------------------------------------------------------- /ch10/ch10-start/config-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "config-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | serde = "1.0.192" 10 | serde_yaml = "0.9.27" 11 | proc-macro2 = "1.0.69" 12 | 13 | [lib] 14 | proc-macro = true 15 | -------------------------------------------------------------------------------- /ch10/ch10-start/config-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use std::collections::HashMap; 3 | use std::fs; 4 | use proc_macro2::Span; 5 | 6 | use syn::{parse_macro_input}; 7 | 8 | use crate::input::ConfigInput; 9 | use crate::output::generate_config_struct; 10 | 11 | mod output; 12 | mod input; 13 | 14 | fn find_yaml_values(input: ConfigInput) -> Result, syn::Error> { 15 | let file_name = input.path 16 | .unwrap_or_else(|| { 17 | "./configuration/config.yaml".to_string() 18 | }); 19 | 20 | let file = fs::File::open(&file_name) 21 | .map_err(|err| { 22 | syn::Error::new( 23 | Span::call_site(), 24 | format!("could not read config with path {}: {}", &file_name, err) 25 | ) 26 | })?; 27 | serde_yaml::from_reader(file) 28 | .map_err(|e| { 29 | syn::Error::new(Span::call_site(), e.to_string()) 30 | }) 31 | } 32 | 33 | #[proc_macro] 34 | pub fn config(item: TokenStream) -> TokenStream { 35 | let input: ConfigInput = parse_macro_input!(item); 36 | 37 | match find_yaml_values(input) { 38 | Ok(values) => generate_config_struct(values).into(), 39 | Err(e) => e.into_compile_error().into() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ch10/ch10-start/config-macro/src/output.rs: -------------------------------------------------------------------------------- 1 | use quote::quote; 2 | use std::collections::HashMap; 3 | use proc_macro2::{TokenStream}; 4 | 5 | fn generate_inserts(yaml_values: HashMap) -> Vec { 6 | yaml_values.iter().map(|v| { 7 | let key = v.0; 8 | let value = v.1; 9 | quote!(map.insert(#key.to_string(), #value.to_string());) 10 | }).collect() 11 | } 12 | 13 | pub fn generate_config_struct(yaml_values: HashMap) -> TokenStream { 14 | let inserts = generate_inserts(yaml_values); 15 | 16 | quote! { 17 | pub struct Config(pub std::collections::HashMap); 18 | 19 | impl Config { 20 | pub fn new() -> Self { 21 | let mut map = std::collections::HashMap::new(); 22 | #(#inserts)* 23 | Config(map) 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ch10/ch10-start/configuration/config.yaml: -------------------------------------------------------------------------------- 1 | user: "admin" 2 | password: "admin" 3 | -------------------------------------------------------------------------------- /ch2/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ch2-compose" 7 | version = "0.1.0" 8 | 9 | [[package]] 10 | name = "ch2-dsl" 11 | version = "0.1.0" 12 | 13 | [[package]] 14 | name = "ch2-dsl-exercise-solution" 15 | version = "0.1.0" 16 | 17 | [[package]] 18 | name = "ch2-greeting" 19 | version = "0.1.0" 20 | 21 | [[package]] 22 | name = "ch2-my-vec" 23 | version = "0.1.0" 24 | 25 | [[package]] 26 | name = "ch2-my-vec-trailing-comma-exercise-solution" 27 | version = "0.1.0" 28 | 29 | [[package]] 30 | name = "ch2-newtypes" 31 | version = "0.1.0" 32 | 33 | [[package]] 34 | name = "ch2-newtypes-from-exercise-solutions" 35 | version = "0.1.0" 36 | 37 | [[package]] 38 | name = "ch2-trace-macros" 39 | version = "0.1.0" 40 | -------------------------------------------------------------------------------- /ch2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "ch2-my-vec", 6 | "ch2-my-vec-trailing-comma-exercise-solution", 7 | "ch2-greeting", 8 | "ch2-trace-macros", 9 | "ch2-newtypes", 10 | "ch2-newtypes-exercise-solutions", 11 | "ch2-dsl", 12 | "ch2-dsl-exercise-solution", 13 | "ch2-compose" 14 | ] 15 | 16 | -------------------------------------------------------------------------------- /ch2/ch2-compose/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch2-compose" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /ch2/ch2-compose/README.md: -------------------------------------------------------------------------------- 1 | # Compose 2 | 3 | ## Overview 4 | 5 | This example, from the 'use cases' section of the declarative macros chapter, shows how we can use macros to do things that are difficult in 'normal' Rust, like composing functions. 6 | -------------------------------------------------------------------------------- /ch2/ch2-dsl-exercise-solution/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch2-dsl-exercise-solution" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [lints.rust] 9 | dead_code = "allow" -------------------------------------------------------------------------------- /ch2/ch2-dsl-exercise-solution/README.md: -------------------------------------------------------------------------------- 1 | # DSL Exercise 2 | 3 | ## Overview 4 | 5 | This is the solution to the DSL exercise, where you expand the DSL with the notion of currency (dollars and euros). 6 | -------------------------------------------------------------------------------- /ch2/ch2-dsl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch2-dsl" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /ch2/ch2-dsl/README.md: -------------------------------------------------------------------------------- 1 | # DSL 2 | 3 | ## Overview 4 | 5 | This example, from the 'use cases' section of the declarative macros chapter, shows how we can use macros to create a *simple* DSL for transferring money in a readable way. 6 | -------------------------------------------------------------------------------- /ch2/ch2-greeting/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch2-greeting" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /ch2/ch2-greeting/README.md: -------------------------------------------------------------------------------- 1 | # Greeting 2 | 3 | ## Overview 4 | 5 | This example, from the 'use cases' section of the declarative macros chapter, shows how we can use macros to 'simulate' varargs and default arguments. 6 | -------------------------------------------------------------------------------- /ch2/ch2-greeting/src/greeting.rs: -------------------------------------------------------------------------------- 1 | pub fn base_greeting_fn(name: &str, greeting: &str) -> String { 2 | format!("{}, {}!", greeting, name) 3 | } 4 | 5 | macro_rules! greeting { 6 | ($name:literal) => { 7 | base_greeting_fn($name,"Hello") 8 | }; 9 | ($name:literal,$greeting:literal) => { 10 | base_greeting_fn($name,$greeting) 11 | } 12 | } 13 | 14 | // pub(crate) use greeting; 15 | -------------------------------------------------------------------------------- /ch2/ch2-greeting/src/main.rs: -------------------------------------------------------------------------------- 1 | use crate::greeting::base_greeting_fn; 2 | // use crate::greeting::greeting; 3 | #[macro_use] 4 | mod greeting; 5 | 6 | fn main() { 7 | let greet = greeting!("Sam", "Heya"); 8 | println!("{}", greet); 9 | let greet_with_default = greeting!("Sam"); 10 | println!("{}", greet_with_default); 11 | } 12 | -------------------------------------------------------------------------------- /ch2/ch2-my-vec-trailing-comma-exercise-solution/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ch2-my-vec" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /ch2/ch2-my-vec-trailing-comma-exercise-solution/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch2-my-vec-trailing-comma-exercise-solution" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /ch2/ch2-my-vec-trailing-comma-exercise-solution/README.md: -------------------------------------------------------------------------------- 1 | # My Vec Trailing Comma 2 | 3 | ## Overview 4 | 5 | This is the solution to the trailing comma exercise, where you allow the use of trailing commas in the `my_vec` macro. 6 | -------------------------------------------------------------------------------- /ch2/ch2-my-vec-trailing-comma-exercise-solution/src/main.rs: -------------------------------------------------------------------------------- 1 | macro_rules! my_vec { 2 | () => { 3 | Vec::new() 4 | }; 5 | ($($x:expr),+ $(,)?) => ( 6 | { 7 | let mut v = Vec::new(); 8 | $( 9 | v.push($x); 10 | )+ 11 | v 12 | } 13 | ); 14 | } 15 | 16 | fn main() {} 17 | 18 | #[cfg(test)] 19 | mod tests { 20 | 21 | #[test] 22 | fn should_create_vec_with_given_elements() { 23 | let actual = my_vec!(1, 2, 3); 24 | 25 | assert_eq!(actual.len(), 3); 26 | assert_eq!(actual[0], 1); 27 | assert_eq!(actual[1], 2); 28 | assert_eq!(actual[2], 3); 29 | } 30 | 31 | #[test] 32 | fn should_create_vec_with_trailing_comma() { 33 | let actual = my_vec!(1, 2, 3,); 34 | 35 | assert_eq!(actual.len(), 3); 36 | assert_eq!(actual[0], 1); 37 | assert_eq!(actual[1], 2); 38 | assert_eq!(actual[2], 3); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ch2/ch2-my-vec/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ch2-my-vec" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /ch2/ch2-my-vec/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch2-my-vec" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /ch2/ch2-my-vec/README.md: -------------------------------------------------------------------------------- 1 | # My Vec 2 | 3 | ## Overview 4 | 5 | This is the first example program from the declarative macros chapter. It creates a macro that works very much like the standard library `vec!` macro. 6 | -------------------------------------------------------------------------------- /ch2/ch2-newtypes-exercise-solutions/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch2-newtypes-from-exercise-solutions" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [lints.rust] 9 | dead_code = "allow" 10 | unused_macros = "allow" 11 | unused_variables = "allow" 12 | -------------------------------------------------------------------------------- /ch2/ch2-newtypes-exercise-solutions/README.md: -------------------------------------------------------------------------------- 1 | # Newtypes Exercise 2 | 3 | ## Overview 4 | 5 | This is the solution to the newtypes exercise, where you are asked to automatically generate a `From` or `TryFrom` implementation for a given newtype. 6 | It also shows the `generate_newtypes_methods` convenience method. 7 | -------------------------------------------------------------------------------- /ch2/ch2-newtypes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch2-newtypes" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [lints.rust] 9 | dead_code = "allow" 10 | -------------------------------------------------------------------------------- /ch2/ch2-newtypes/README.md: -------------------------------------------------------------------------------- 1 | # Newtypes 2 | 3 | ## Overview 4 | 5 | This example, from the 'use cases' section of the declarative macros chapter, shows how we can use macros to avoid newtype boilerplate. 6 | -------------------------------------------------------------------------------- /ch2/ch2-trace-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch2-trace-macros" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /ch2/ch2-trace-macros/README.md: -------------------------------------------------------------------------------- 1 | # Trace Macros 2 | 3 | ## Overview 4 | 5 | This example shows how you can use trace macros to get insight into how your macros are expanded. 6 | -------------------------------------------------------------------------------- /ch2/ch2-trace-macros/src/greeting.rs: -------------------------------------------------------------------------------- 1 | pub fn base_greeting_fn(name: &str, greeting: &str) -> String { 2 | format!("{}, {}!", greeting, name) 3 | } 4 | 5 | macro_rules! greeting { 6 | ($name:literal) => { 7 | base_greeting_fn($name,"Hello") 8 | }; 9 | ($name:literal,$greeting:literal) => { 10 | base_greeting_fn($name,$greeting) 11 | }; 12 | (test $name:literal) => {{ 13 | log_syntax!("The name passed to test is ", $name); 14 | println!("Returning default greeting"); 15 | base_greeting_fn($name,"Hello") 16 | }} 17 | } 18 | -------------------------------------------------------------------------------- /ch2/ch2-trace-macros/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(trace_macros)] 2 | #![feature(log_syntax)] 3 | 4 | use crate::greeting::base_greeting_fn; 5 | #[macro_use] 6 | mod greeting; 7 | 8 | fn main() { 9 | trace_macros!(true); 10 | let _greet = greeting!("Sam", "Heya"); 11 | let _greet_with_default = greeting!("Sam"); 12 | let _greet_with_default_test = greeting!(test "Sam"); 13 | trace_macros!(false); 14 | } 15 | -------------------------------------------------------------------------------- /ch3/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "ch3-implemented", 6 | "ch3-implemented-no-syn-or-quote", 7 | "ch3-initial-code", 8 | "ch3-name-to-output-exercise-solution", 9 | "ch3-testing-function-exercise-solution", 10 | "ch3-venial", 11 | "ch3-fill-in-blanks-exercise-solution" 12 | ] 13 | -------------------------------------------------------------------------------- /ch3/ch3-fill-in-blanks-exercise-solution/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world-fill-in-blanks" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | hello-world-fill-in-blanks-macro = { path = "./ch3-implemented-macro" } 8 | -------------------------------------------------------------------------------- /ch3/ch3-fill-in-blanks-exercise-solution/README.md: -------------------------------------------------------------------------------- 1 | # Fill in the blanks 2 | 3 | ## Overview 4 | 5 | This is the solution to the 'fill in the blanks' exercise from chapter 3. -------------------------------------------------------------------------------- /ch3/ch3-fill-in-blanks-exercise-solution/ch3-implemented-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world-fill-in-blanks-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = "2.0.39" 9 | 10 | [lib] 11 | proc-macro = true 12 | -------------------------------------------------------------------------------- /ch3/ch3-fill-in-blanks-exercise-solution/ch3-implemented-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use quote::quote; 2 | use proc_macro::TokenStream; 3 | use syn::{parse_macro_input, DeriveInput}; 4 | 5 | #[proc_macro_derive(UpperCaseName)] 6 | pub fn uppercase(item: TokenStream) -> TokenStream { 7 | let ast = parse_macro_input!(item as DeriveInput); 8 | let name = ast.ident; 9 | let uppercase_name = name.to_string().to_uppercase(); 10 | 11 | let add_uppercase = quote! { 12 | impl #name { 13 | fn uppercase(&self) { 14 | println!("{}", #uppercase_name); 15 | } 16 | } 17 | }; 18 | 19 | add_uppercase.into() 20 | } 21 | -------------------------------------------------------------------------------- /ch3/ch3-fill-in-blanks-exercise-solution/src/main.rs: -------------------------------------------------------------------------------- 1 | use hello_world_fill_in_blanks_macro::UpperCaseName; 2 | 3 | #[derive(UpperCaseName)] 4 | struct Example; 5 | 6 | fn main() { 7 | let e = Example {}; 8 | e.uppercase(); 9 | } -------------------------------------------------------------------------------- /ch3/ch3-implemented-no-syn-or-quote/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "hello-world" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "hello-world-macro", 10 | ] 11 | 12 | [[package]] 13 | name = "hello-world-macro" 14 | version = "0.1.0" 15 | -------------------------------------------------------------------------------- /ch3/ch3-implemented-no-syn-or-quote/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world-implemented-no-syn-or-quote" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | hello-world-implemented-no-syn-or-quote-macro = { path = "./ch3-implemented-macro" } 8 | -------------------------------------------------------------------------------- /ch3/ch3-implemented-no-syn-or-quote/README.md: -------------------------------------------------------------------------------- 1 | # Venial 2 | 3 | ## Overview 4 | 5 | This example shows how you could have written the 'Hello world' derive macro (from chapter 3) without `syn` and `quote`. 6 | -------------------------------------------------------------------------------- /ch3/ch3-implemented-no-syn-or-quote/ch3-implemented-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world-implemented-no-syn-or-quote-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [lib] 9 | proc-macro = true 10 | -------------------------------------------------------------------------------- /ch3/ch3-implemented-no-syn-or-quote/ch3-implemented-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use proc_macro::TokenTree; 3 | 4 | #[proc_macro_derive(Hello)] 5 | pub fn hello_alt(item: TokenStream) -> TokenStream { 6 | fn ident_name(item: TokenTree) -> String { 7 | match item { 8 | TokenTree::Ident(i) => i.to_string(), 9 | _ => panic!("no ident") 10 | } 11 | } 12 | let name = ident_name(item.into_iter().nth(1).unwrap()); 13 | 14 | format!("impl {} {{ fn hello_world(&self) \ 15 | {{ println!(\"Hello world\") }} }} ", name 16 | ).parse() 17 | .unwrap() 18 | } -------------------------------------------------------------------------------- /ch3/ch3-implemented-no-syn-or-quote/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate hello_world_implemented_no_syn_or_quote_macro; 3 | 4 | #[derive(Hello)] 5 | struct Example; 6 | 7 | fn main() { 8 | let e = Example {}; 9 | e.hello_world(); 10 | } 11 | -------------------------------------------------------------------------------- /ch3/ch3-implemented/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world-implemented" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | hello-world-implemented-macro = { path = "./ch3-implemented-macro" } 8 | -------------------------------------------------------------------------------- /ch3/ch3-implemented/README.md: -------------------------------------------------------------------------------- 1 | # Implemented 2 | 3 | ## Overview 4 | 5 | This is the implemented 'Hello world' derive macro example for chapter 3. 6 | -------------------------------------------------------------------------------- /ch3/ch3-implemented/ch3-implemented-macro/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "hello-world-macro" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "quote", 10 | "syn", 11 | ] 12 | 13 | [[package]] 14 | name = "proc-macro2" 15 | version = "1.0.56" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" 18 | dependencies = [ 19 | "unicode-ident", 20 | ] 21 | 22 | [[package]] 23 | name = "quote" 24 | version = "1.0.26" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" 27 | dependencies = [ 28 | "proc-macro2", 29 | ] 30 | 31 | [[package]] 32 | name = "syn" 33 | version = "1.0.109" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 36 | dependencies = [ 37 | "proc-macro2", 38 | "quote", 39 | "unicode-ident", 40 | ] 41 | 42 | [[package]] 43 | name = "unicode-ident" 44 | version = "1.0.8" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 47 | -------------------------------------------------------------------------------- /ch3/ch3-implemented/ch3-implemented-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world-implemented-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = "2.0.39" 9 | 10 | [lib] 11 | proc-macro = true 12 | -------------------------------------------------------------------------------- /ch3/ch3-implemented/ch3-implemented-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use quote::quote; 2 | use proc_macro::TokenStream; 3 | use syn::{parse_macro_input, DeriveInput}; 4 | 5 | #[proc_macro_derive(Hello)] 6 | pub fn hello(item: TokenStream) -> TokenStream { 7 | let ast = parse_macro_input!(item as DeriveInput); 8 | let name = ast.ident; 9 | 10 | let add_hello_world = quote! { 11 | impl #name { 12 | fn hello_world(&self) { 13 | println!("Hello world") 14 | } 15 | } 16 | }; 17 | 18 | add_hello_world.into() 19 | } 20 | -------------------------------------------------------------------------------- /ch3/ch3-implemented/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate hello_world_implemented_macro; 3 | 4 | #[derive(Hello)] 5 | struct Example; 6 | 7 | impl Example { 8 | fn another_function(&self) { 9 | println!("Something else"); 10 | } 11 | } 12 | 13 | #[derive(Hello)] 14 | enum Pet { 15 | Cat, 16 | } 17 | 18 | fn main() { 19 | let e = Example {}; 20 | e.hello_world(); 21 | e.another_function(); 22 | let p = Pet::Cat; 23 | p.hello_world(); 24 | } -------------------------------------------------------------------------------- /ch3/ch3-initial-code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | hello-world-macro = { path = "./ch3-initial-code-macro" } 8 | -------------------------------------------------------------------------------- /ch3/ch3-initial-code/README.md: -------------------------------------------------------------------------------- 1 | # Initial Code 2 | 3 | ## Overview 4 | 5 | This is the initial code for chapter 3, a working 'Hello world' derive macro that doesn't do anything. 6 | -------------------------------------------------------------------------------- /ch3/ch3-initial-code/ch3-initial-code-macro/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "hello-world-macro" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "quote", 10 | "syn", 11 | ] 12 | 13 | [[package]] 14 | name = "proc-macro2" 15 | version = "1.0.56" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" 18 | dependencies = [ 19 | "unicode-ident", 20 | ] 21 | 22 | [[package]] 23 | name = "quote" 24 | version = "1.0.26" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" 27 | dependencies = [ 28 | "proc-macro2", 29 | ] 30 | 31 | [[package]] 32 | name = "syn" 33 | version = "1.0.109" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 36 | dependencies = [ 37 | "proc-macro2", 38 | "quote", 39 | "unicode-ident", 40 | ] 41 | 42 | [[package]] 43 | name = "unicode-ident" 44 | version = "1.0.8" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 47 | -------------------------------------------------------------------------------- /ch3/ch3-initial-code/ch3-initial-code-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = "2.0.39" 9 | 10 | [lib] 11 | proc-macro = true 12 | -------------------------------------------------------------------------------- /ch3/ch3-initial-code/ch3-initial-code-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use quote::quote; 2 | use proc_macro::TokenStream; 3 | 4 | #[proc_macro_derive(Hello)] 5 | pub fn hello(_item: TokenStream) -> TokenStream { 6 | let add_hello_world = quote! {}; 7 | add_hello_world.into() 8 | } 9 | -------------------------------------------------------------------------------- /ch3/ch3-initial-code/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate hello_world_macro; 3 | 4 | #[allow(dead_code)] 5 | #[derive(Hello)] 6 | struct Example; 7 | 8 | fn main() {} -------------------------------------------------------------------------------- /ch3/ch3-name-to-output-exercise-solution/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world-name-to-output" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | hello-world-name-to-output-macro = { path = "./ch3-implemented-macro" } 8 | -------------------------------------------------------------------------------- /ch3/ch3-name-to-output-exercise-solution/README.md: -------------------------------------------------------------------------------- 1 | # Name to output 2 | 3 | ## Overview 4 | 5 | This is the solution to the 'name to output' exercise from chapter 3, where you have to output the name of the struct in the generated `hello_world` method. 6 | -------------------------------------------------------------------------------- /ch3/ch3-name-to-output-exercise-solution/ch3-implemented-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world-name-to-output-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = "2.0.39" 9 | 10 | [lib] 11 | proc-macro = true 12 | -------------------------------------------------------------------------------- /ch3/ch3-name-to-output-exercise-solution/ch3-implemented-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use quote::quote; 2 | use proc_macro::TokenStream; 3 | use syn::{parse_macro_input, DeriveInput}; 4 | 5 | #[proc_macro_derive(Hello)] 6 | pub fn hello(item: TokenStream) -> TokenStream { 7 | let ast = parse_macro_input!(item as DeriveInput); 8 | let name = ast.ident; 9 | 10 | let add_hello_world = quote! { 11 | impl #name { 12 | fn hello_world(&self) { 13 | println!("Hello {}", stringify!(#name)) 14 | } 15 | } 16 | }; 17 | 18 | add_hello_world.into() 19 | } 20 | -------------------------------------------------------------------------------- /ch3/ch3-name-to-output-exercise-solution/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate hello_world_name_to_output_macro; 3 | 4 | #[derive(Hello)] 5 | struct Example; 6 | 7 | fn main() { 8 | let e = Example {}; 9 | e.hello_world(); 10 | } -------------------------------------------------------------------------------- /ch3/ch3-testing-function-exercise-solution/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world-testing-fucntion" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | hello-world-testing-function-macro = { path = "./ch3-implemented-macro" } 8 | -------------------------------------------------------------------------------- /ch3/ch3-testing-function-exercise-solution/README.md: -------------------------------------------------------------------------------- 1 | # Testing function 2 | 3 | ## Overview 4 | 5 | This is the solution to the 'testing function' exercise from chapter 3, where you have to add an associated function called `testing_testing` to the derive output. 6 | -------------------------------------------------------------------------------- /ch3/ch3-testing-function-exercise-solution/ch3-implemented-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world-testing-function-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = "2.0.39" 9 | 10 | [lib] 11 | proc-macro = true 12 | -------------------------------------------------------------------------------- /ch3/ch3-testing-function-exercise-solution/ch3-implemented-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use quote::quote; 2 | use proc_macro::TokenStream; 3 | use syn::{parse_macro_input, DeriveInput}; 4 | 5 | #[proc_macro_derive(Hello)] 6 | pub fn hello(item: TokenStream) -> TokenStream { 7 | let ast = parse_macro_input!(item as DeriveInput); 8 | let name = ast.ident; 9 | 10 | let add_hello_world = quote! { 11 | impl #name { 12 | fn hello_world(&self) { 13 | println!("Hello world") 14 | } 15 | 16 | fn testing_testing() { 17 | println!("One two three") 18 | } 19 | } 20 | }; 21 | 22 | add_hello_world.into() 23 | } 24 | -------------------------------------------------------------------------------- /ch3/ch3-testing-function-exercise-solution/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate hello_world_testing_function_macro; 3 | 4 | #[derive(Hello)] 5 | struct Example; 6 | 7 | fn main() { 8 | Example::testing_testing(); 9 | } -------------------------------------------------------------------------------- /ch3/ch3-venial/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch3-venial" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | venial-macro = { path = "./ch3-venial-macro"} -------------------------------------------------------------------------------- /ch3/ch3-venial/README.md: -------------------------------------------------------------------------------- 1 | # Implemented without syn or quote 2 | 3 | ## Overview 4 | 5 | This example shows how you could have written the 'Hello world' derive macro (from chapter 3) using `venial`, a lightweight alternative to `syn` 6 | -------------------------------------------------------------------------------- /ch3/ch3-venial/ch3-venial-macro/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "proc-macro2" 7 | version = "1.0.56" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" 10 | dependencies = [ 11 | "unicode-ident", 12 | ] 13 | 14 | [[package]] 15 | name = "quote" 16 | version = "1.0.26" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" 19 | dependencies = [ 20 | "proc-macro2", 21 | ] 22 | 23 | [[package]] 24 | name = "unicode-ident" 25 | version = "1.0.8" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 28 | 29 | [[package]] 30 | name = "venial" 31 | version = "0.5.0" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "61584a325b16f97b5b25fcc852eb9550843a251057a5e3e5992d2376f3df4bb2" 34 | dependencies = [ 35 | "proc-macro2", 36 | "quote", 37 | ] 38 | 39 | [[package]] 40 | name = "venial-macro" 41 | version = "0.1.0" 42 | dependencies = [ 43 | "quote", 44 | "venial", 45 | ] 46 | -------------------------------------------------------------------------------- /ch3/ch3-venial/ch3-venial-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "venial-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | venial = "0.5.0" 8 | quote = "1.0.33" 9 | 10 | [lib] 11 | proc-macro = true -------------------------------------------------------------------------------- /ch3/ch3-venial/ch3-venial-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use quote::quote; 2 | use proc_macro::TokenStream; 3 | use venial::{parse_declaration, Declaration, Struct, Enum}; 4 | 5 | #[proc_macro_derive(Hello)] 6 | pub fn hello(item: TokenStream) -> TokenStream { 7 | let declaration = parse_declaration(item.into()).unwrap(); 8 | 9 | let name = match declaration { 10 | Declaration::Struct(Struct { name, .. }) => name, 11 | Declaration::Enum(Enum { name, .. }) => name, 12 | _ => panic!("only implemented for struct and enum") 13 | }; 14 | 15 | let add_hello_world = quote! { 16 | impl #name { 17 | fn hello_world(&self) { 18 | println!("Hello world") 19 | } 20 | } 21 | }; 22 | 23 | add_hello_world.into() 24 | } -------------------------------------------------------------------------------- /ch3/ch3-venial/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate venial_macro; 3 | 4 | #[derive(Hello)] 5 | struct Example; 6 | 7 | fn main() { 8 | let e = Example {}; 9 | e.hello_world(); 10 | } -------------------------------------------------------------------------------- /ch4/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "ch4-public-macro", 6 | "ch4-public-macro-with-structfield", 7 | "ch4-public-macro-with-structfield-and-parse-impl", 8 | "ch4-structfield-without-punctuated-exercise", 9 | "ch4-enum-and-unnamed-struct-exercise" 10 | ] 11 | -------------------------------------------------------------------------------- /ch4/ch4-enum-and-unnamed-struct-exercise/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "make-public-enum-and-unnamed-struct" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | make-public-macro-enum-and-unnamed-struct = { path = "./make-public-macro" } 8 | -------------------------------------------------------------------------------- /ch4/ch4-enum-and-unnamed-struct-exercise/README.md: -------------------------------------------------------------------------------- 1 | # Enum and unnamed struct 2 | 3 | ## Overview 4 | 5 | This is the combined solution to several exercises from chapter 4, where we ask you to: 6 | 7 | - Handle unnamed fields 8 | - Handle enums 9 | - Keep existing attributes (instead of losing them) 10 | -------------------------------------------------------------------------------- /ch4/ch4-enum-and-unnamed-struct-exercise/make-public-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "make-public-macro-enum-and-unnamed-struct" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = "2.0.39" 9 | 10 | [lib] 11 | proc-macro = true -------------------------------------------------------------------------------- /ch4/ch4-enum-and-unnamed-struct-exercise/src/main.rs: -------------------------------------------------------------------------------- 1 | use make_public_macro_enum_and_unnamed_struct::public; 2 | 3 | #[derive(Debug)] 4 | #[public] 5 | struct Example { 6 | first: String, 7 | pub second: u32, 8 | } 9 | 10 | #[public] 11 | #[derive(Debug)] 12 | struct UnnamedExample(String, f64); 13 | 14 | #[public] 15 | enum AnEnumExample { 16 | First, 17 | Second, 18 | } 19 | 20 | #[public] 21 | struct EmptyStruct {} 22 | 23 | fn main() { 24 | let _e = Example { 25 | first: "first".to_string(), 26 | second: 5, 27 | }; 28 | let u = UnnamedExample("first".to_string(), 5.2); 29 | println!("{:?}", u); 30 | let _a = AnEnumExample::First; 31 | let _empty = EmptyStruct {}; 32 | } 33 | -------------------------------------------------------------------------------- /ch4/ch4-public-macro-with-structfield-and-parse-impl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "make-public-structfield-parse" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | make-public-structfield-parse-macro = { path = "./make-public-macro" } 8 | -------------------------------------------------------------------------------- /ch4/ch4-public-macro-with-structfield-and-parse-impl/README.md: -------------------------------------------------------------------------------- 1 | # StructField with Parse (third implementation) 2 | 3 | ## Overview 4 | 5 | This is the third implementation of the 'public' attribute macro from chapter 4, where we implement `Parse` in addition to `ToTokens` for our custom struct (`StructField`). 6 | -------------------------------------------------------------------------------- /ch4/ch4-public-macro-with-structfield-and-parse-impl/make-public-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "make-public-structfield-parse-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = "2.0.39" 9 | proc-macro2 = "1.0.69" 10 | 11 | [lib] 12 | proc-macro = true -------------------------------------------------------------------------------- /ch4/ch4-public-macro-with-structfield-and-parse-impl/src/main.rs: -------------------------------------------------------------------------------- 1 | use make_public_structfield_parse_macro::public; 2 | 3 | #[public] 4 | struct Example { 5 | first: String, 6 | pub second: u32, 7 | } 8 | 9 | fn main() { 10 | let _e = Example { 11 | first: "first".to_string(), 12 | second: 5, 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /ch4/ch4-public-macro-with-structfield/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "make-public-structfield" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | make-public-structfield-macro = { path = "./make-public-macro" } 8 | -------------------------------------------------------------------------------- /ch4/ch4-public-macro-with-structfield/README.md: -------------------------------------------------------------------------------- 1 | # StructField (second implementation) 2 | 3 | ## Overview 4 | 5 | This is the second implementation of the 'public' attribute macro from chapter 4, where we use a custom struct (`StructField`) and implement the `ToTokens` trait. 6 | -------------------------------------------------------------------------------- /ch4/ch4-public-macro-with-structfield/make-public-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "make-public-structfield-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = "2.0.39" 9 | proc-macro2 = "1.0.69" 10 | 11 | [lib] 12 | proc-macro = true -------------------------------------------------------------------------------- /ch4/ch4-public-macro-with-structfield/src/main.rs: -------------------------------------------------------------------------------- 1 | use make_public_structfield_macro::public; 2 | 3 | #[public] 4 | struct Example { 5 | first: String, 6 | pub second: u32, 7 | } 8 | 9 | fn main() { 10 | let _e = Example { 11 | first: "first".to_string(), 12 | second: 5, 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /ch4/ch4-public-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "make-public" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | make-public-macro = { path = "./make-public-macro" } 8 | -------------------------------------------------------------------------------- /ch4/ch4-public-macro/README.md: -------------------------------------------------------------------------------- 1 | # Public macro (first implementation) 2 | 3 | ## Overview 4 | 5 | This is the first implementation of the 'public' attribute macro from chapter 4. 6 | -------------------------------------------------------------------------------- /ch4/ch4-public-macro/make-public-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "make-public-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = "2.0.39" 9 | 10 | [lib] 11 | proc-macro = true -------------------------------------------------------------------------------- /ch4/ch4-public-macro/make-public-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use quote::quote; 2 | use proc_macro::TokenStream; 3 | use syn::{parse_macro_input, DeriveInput, DataStruct, FieldsNamed}; 4 | use syn::Data::Struct; 5 | use syn::Fields::Named; 6 | 7 | #[proc_macro_attribute] 8 | pub fn public(_attr: TokenStream, item: TokenStream) -> TokenStream { 9 | let ast = parse_macro_input!(item as DeriveInput); 10 | let name = ast.ident; 11 | 12 | let fields = match ast.data { 13 | Struct( 14 | DataStruct { 15 | fields: Named( 16 | FieldsNamed { 17 | ref named, .. 18 | }), .. 19 | } 20 | ) => named, 21 | _ => unimplemented!( 22 | "only works for structs with named fields" 23 | ), 24 | }; 25 | 26 | let builder_fields = fields.iter().map(|f| { 27 | let name = &f.ident; 28 | let ty = &f.ty; 29 | quote! { pub #name: #ty } 30 | }); 31 | 32 | let public_version = quote! { 33 | pub struct #name { 34 | #(#builder_fields,)* 35 | } 36 | }; 37 | 38 | public_version.into() 39 | } 40 | -------------------------------------------------------------------------------- /ch4/ch4-public-macro/src/main.rs: -------------------------------------------------------------------------------- 1 | use make_public_macro::public; 2 | 3 | #[public] 4 | struct Example { 5 | first: String, 6 | pub second: u32, 7 | } 8 | 9 | fn main() { 10 | let _e = Example { 11 | first: "first".to_string(), 12 | second: 5, 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /ch4/ch4-structfield-without-punctuated-exercise/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "make-public-structfield-without-punctuated-exercise" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | make-public-structfield-without-punctuated-exercise-macro = { path = "./make-public-macro" } 8 | -------------------------------------------------------------------------------- /ch4/ch4-structfield-without-punctuated-exercise/README.md: -------------------------------------------------------------------------------- 1 | # StructField without Punctuated 2 | 3 | ## Overview 4 | 5 | This is the solution to the exercise from chapter 4 where we ask you to parse the fields without using `Punctuated`. 6 | -------------------------------------------------------------------------------- /ch4/ch4-structfield-without-punctuated-exercise/make-public-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "make-public-structfield-without-punctuated-exercise-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = "2.0.39" 9 | proc-macro2 = "1.0.69" 10 | 11 | [lib] 12 | proc-macro = true -------------------------------------------------------------------------------- /ch4/ch4-structfield-without-punctuated-exercise/src/main.rs: -------------------------------------------------------------------------------- 1 | use make_public_structfield_without_punctuated_exercise_macro::public; 2 | 3 | #[public] 4 | struct Example { 5 | first: String, 6 | pub second: u32, 7 | } 8 | 9 | fn main() { 10 | let _e = Example { 11 | first: "first".to_string(), 12 | second: 5, 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /ch5/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "ch5-information-hiding", 6 | "ch5-private-fields-exercise", 7 | "ch5-hello-world-only-name-exercise", 8 | "ch5-compose", 9 | "ch5-compose-with-different-token-exercise" 10 | ] -------------------------------------------------------------------------------- /ch5/ch5-compose-with-different-token-exercise/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "function-like-compose-with-different-token-exercise" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | function-like-compose-with-different-token-exercise-macro = { path = "ch5-function-like-compose-macro" } 8 | -------------------------------------------------------------------------------- /ch5/ch5-compose-with-different-token-exercise/README.md: -------------------------------------------------------------------------------- 1 | # Compose with different token 2 | 3 | ## Overview 4 | 5 | This is the solution to the exercise from chapter 5 where we ask you to generate a 'hello world' function for a struct, by only passing in the name of your struct. 6 | -------------------------------------------------------------------------------- /ch5/ch5-compose-with-different-token-exercise/ch5-function-like-compose-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "function-like-compose-with-different-token-exercise-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | proc-macro2 = "1.0.69" 10 | 11 | [lib] 12 | proc-macro = true -------------------------------------------------------------------------------- /ch5/ch5-compose-with-different-token-exercise/src/main.rs: -------------------------------------------------------------------------------- 1 | use function_like_compose_with_different_token_exercise_macro::compose; 2 | 3 | fn add_one(n: i32) -> i32 { 4 | n + 1 5 | } 6 | 7 | fn stringify(n: i32) -> String { 8 | n.to_string() 9 | } 10 | 11 | fn main() { 12 | let compose = compose!(add_one ! add_one ! stringify); 13 | println!("{:?}", compose(5)); 14 | } 15 | 16 | #[cfg(test)] 17 | mod tests { 18 | use super::*; 19 | 20 | #[test] 21 | fn basic_test() { 22 | let compose = compose!(add_one ! add_one ! stringify); 23 | 24 | let actual = compose(5); 25 | 26 | assert_eq!(actual, "7"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ch5/ch5-compose/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "function-like-compose" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | function-like-compose-macro = { path = "ch5-function-like-compose-macro" } 8 | -------------------------------------------------------------------------------- /ch5/ch5-compose/README.md: -------------------------------------------------------------------------------- 1 | # Compose 2 | 3 | ## Overview 4 | 5 | This is the implementation of the 'compose' macro from chapter 5. 6 | -------------------------------------------------------------------------------- /ch5/ch5-compose/ch5-function-like-compose-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "function-like-compose-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | proc-macro2 = "1.0.69" 10 | 11 | [lib] 12 | proc-macro = true -------------------------------------------------------------------------------- /ch5/ch5-compose/src/main.rs: -------------------------------------------------------------------------------- 1 | use function_like_compose_macro::compose; 2 | 3 | fn add_one(n: i32) -> i32 { 4 | n + 1 5 | } 6 | 7 | fn stringify(n: i32) -> String { 8 | n.to_string() 9 | } 10 | 11 | fn main() { 12 | let compose = compose!(add_one . add_one . stringify); 13 | println!("{:?}", compose(5)); 14 | } 15 | 16 | #[cfg(test)] 17 | mod tests { 18 | use super::*; 19 | use function_like_compose_macro::compose; 20 | 21 | #[test] 22 | fn basic_test() { 23 | let compose = compose!(add_one . add_one . stringify); 24 | 25 | let actual = compose(5); 26 | 27 | assert_eq!(actual, "7"); 28 | } 29 | 30 | #[test] 31 | fn two_elements_test() { 32 | let compose = compose!(add_one . stringify); 33 | 34 | let actual = compose(5); 35 | 36 | assert_eq!(actual, "6"); 37 | } 38 | 39 | #[test] 40 | fn four_elements_test() { 41 | let compose = compose!(add_one . add_one . add_one . add_one); 42 | 43 | let actual = compose(12); 44 | 45 | assert_eq!(actual, 16); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ch5/ch5-hello-world-only-name-exercise/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch5-hello-world-only-name-exercise" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | hello-world-only-name-exercise-macro = { path = "./hello-world-macro" } 8 | -------------------------------------------------------------------------------- /ch5/ch5-hello-world-only-name-exercise/README.md: -------------------------------------------------------------------------------- 1 | # Hello world only name 2 | 3 | ## Overview 4 | 5 | This is the solution to the exercise from chapter 5 where we ask you to use a different token for the 'compose' macro. 6 | In this case, we've gone with an exclamation mark (`!`). 7 | -------------------------------------------------------------------------------- /ch5/ch5-hello-world-only-name-exercise/hello-world-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world-only-name-exercise-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = "2.0.39" 9 | 10 | [lib] 11 | proc-macro = true 12 | -------------------------------------------------------------------------------- /ch5/ch5-hello-world-only-name-exercise/hello-world-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use quote::quote; 2 | use proc_macro::TokenStream; 3 | use syn::{parse_macro_input, Ident}; 4 | 5 | #[proc_macro] 6 | pub fn hello(item: TokenStream) -> TokenStream { 7 | let ast = parse_macro_input!(item as Ident); 8 | 9 | quote!( 10 | impl #ast { 11 | fn hello_world(&self) { 12 | println!("Hello world") 13 | } 14 | } 15 | ).into() 16 | } 17 | -------------------------------------------------------------------------------- /ch5/ch5-hello-world-only-name-exercise/src/main.rs: -------------------------------------------------------------------------------- 1 | use hello_world_only_name_exercise_macro::hello; 2 | 3 | struct Example { 4 | another_value: String 5 | } 6 | 7 | hello!(Example); 8 | 9 | fn main() { 10 | let e = Example { 11 | another_value: "does not disappear".to_string(), 12 | }; 13 | e.hello_world(); 14 | } 15 | 16 | #[cfg(test)] 17 | mod tests { 18 | use crate::Example; 19 | 20 | #[test] 21 | fn value_does_not_disappear() { 22 | let e = Example { 23 | another_value: "does not disappear".to_string(), 24 | }; 25 | 26 | assert_eq!(e.another_value, "does not disappear".to_string()); 27 | } 28 | 29 | #[test] 30 | fn hello_world_method_available() { 31 | let e = Example { 32 | another_value: "does not disappear".to_string(), 33 | }; 34 | 35 | e.hello_world(); 36 | } 37 | } -------------------------------------------------------------------------------- /ch5/ch5-information-hiding/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch5-private" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | private-macro = { path = "./private-macro" } 8 | -------------------------------------------------------------------------------- /ch5/ch5-information-hiding/README.md: -------------------------------------------------------------------------------- 1 | # Information hiding 2 | 3 | ## Overview 4 | 5 | This is the implementation of the 'private' macro from chapter 5. 6 | -------------------------------------------------------------------------------- /ch5/ch5-information-hiding/private-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "private-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = "2.0.39" 9 | proc-macro2 = "1.0.69" 10 | 11 | [lib] 12 | proc-macro = true 13 | -------------------------------------------------------------------------------- /ch5/ch5-information-hiding/src/main.rs: -------------------------------------------------------------------------------- 1 | use private_macro::private; 2 | 3 | private!( 4 | struct Example { 5 | string_value: String, 6 | number_value: i32, 7 | } 8 | ); 9 | 10 | fn main() { 11 | let e = Example { 12 | string_value: "value".to_string(), 13 | number_value: 2, 14 | }; 15 | 16 | e.get_string_value(); 17 | e.get_number_value(); 18 | } 19 | 20 | #[cfg(test)] 21 | mod tests { 22 | use crate::Example; 23 | 24 | #[test] 25 | fn generates_necessary_methods() { 26 | let e = Example { 27 | string_value: "value".to_string(), 28 | number_value: 2, 29 | }; 30 | 31 | assert_eq!(e.get_string_value(), &"value"); 32 | assert_eq!(e.get_number_value(), &2); 33 | } 34 | } -------------------------------------------------------------------------------- /ch5/ch5-private-fields-exercise/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch5-private-with-private-fields" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | private-macro-with-private-fields = { path = "./private-macro" } 8 | -------------------------------------------------------------------------------- /ch5/ch5-private-fields-exercise/README.md: -------------------------------------------------------------------------------- 1 | # Private fields 2 | 3 | ## Overview 4 | 5 | This is the solution to the exercise from chapter 5 where we ask you to set the fields of a struct to private. In addition, you should generate public methods to access those fields. 6 | -------------------------------------------------------------------------------- /ch5/ch5-private-fields-exercise/private-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "private-macro-with-private-fields" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = "2.0.39" 9 | 10 | [lib] 11 | proc-macro = true 12 | -------------------------------------------------------------------------------- /ch5/ch5-private-fields-exercise/src/main.rs: -------------------------------------------------------------------------------- 1 | use crate::other_file::Example; 2 | 3 | mod other_file; 4 | 5 | fn main() { 6 | let e = Example::new(); 7 | 8 | e.get_string_value(); 9 | e.get_number_value(); 10 | } 11 | 12 | #[cfg(test)] 13 | mod tests { 14 | use crate::Example; 15 | 16 | #[test] 17 | fn generates_necessary_methods() { 18 | let e = Example::new(); 19 | 20 | assert_eq!(e.get_string_value(), &"value"); 21 | assert_eq!(e.get_number_value(), &2); 22 | } 23 | } -------------------------------------------------------------------------------- /ch5/ch5-private-fields-exercise/src/other_file.rs: -------------------------------------------------------------------------------- 1 | use private_macro_with_private_fields::private; 2 | 3 | private!( 4 | struct Example { 5 | pub string_value: String, 6 | pub number_value: i32, 7 | } 8 | ); 9 | -------------------------------------------------------------------------------- /ch6/ch6-first-setup/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "builder-macro", 6 | "builder-code", 7 | "builder-usage" 8 | ] 9 | -------------------------------------------------------------------------------- /ch6/ch6-first-setup/README.md: -------------------------------------------------------------------------------- 1 | # First setup 2 | 3 | ## Overview 4 | 5 | This is the first implementation of the 'builder' macro from chapter 6. 6 | 7 | It demonstrates the macro setup with three directories, and white-box unit testing. 8 | -------------------------------------------------------------------------------- /ch6/ch6-first-setup/builder-code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-code" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | proc-macro2 = "1.0.69" 10 | -------------------------------------------------------------------------------- /ch6/ch6-first-setup/builder-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-code = { path = "../builder-code" } 8 | 9 | [lib] 10 | proc-macro = true 11 | -------------------------------------------------------------------------------- /ch6/ch6-first-setup/builder-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use builder_code::create_builder; 3 | 4 | #[proc_macro_derive(Builder)] 5 | pub fn builder(item: TokenStream) -> TokenStream { 6 | create_builder(item.into()).into() 7 | } 8 | -------------------------------------------------------------------------------- /ch6/ch6-first-setup/builder-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-macro = { path = "../builder-macro" } 8 | -------------------------------------------------------------------------------- /ch6/ch6-first-setup/builder-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | use builder_macro::Builder; 2 | 3 | #[allow(dead_code)] 4 | #[derive(Builder)] 5 | struct Gleipnir {} 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /ch6/ch6-implemented/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "builder-macro", 6 | "builder-code", 7 | "builder-usage" 8 | ] 9 | -------------------------------------------------------------------------------- /ch6/ch6-implemented/README.md: -------------------------------------------------------------------------------- 1 | # Implemented 2 | 3 | ## Overview 4 | 5 | This is the second implementation of the 'builder' macro from chapter 6. 6 | 7 | It demonstrates the macro setup with three directories, a real implementation of the builder, white-box and black-box unit testing. 8 | -------------------------------------------------------------------------------- /ch6/ch6-implemented/builder-code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-code" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | proc-macro2 = "1.0.69" 10 | -------------------------------------------------------------------------------- /ch6/ch6-implemented/builder-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-code = { path = "../builder-code" } 8 | 9 | [lib] 10 | proc-macro = true 11 | -------------------------------------------------------------------------------- /ch6/ch6-implemented/builder-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use builder_code::create_builder; 3 | 4 | #[proc_macro_derive(Builder)] 5 | pub fn builder(item: TokenStream) -> TokenStream { 6 | create_builder(item.into()).into() 7 | } 8 | -------------------------------------------------------------------------------- /ch6/ch6-implemented/builder-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-macro = { path = "../builder-macro" } 8 | -------------------------------------------------------------------------------- /ch6/ch6-implemented/builder-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use builder_macro::Builder; 6 | 7 | #[test] 8 | fn should_generate_builder_for_struct_with_no_properties() { 9 | #[derive(Builder)] 10 | struct ExampleStructNoFields {} 11 | 12 | let _: ExampleStructNoFields = ExampleStructNoFields::builder() 13 | .build(); 14 | } 15 | 16 | #[test] 17 | fn should_generate_builder_for_struct_with_one_property() { 18 | #[derive(Builder)] 19 | struct Gleipnir { 20 | roots_of: String, 21 | } 22 | 23 | let gleipnir = Gleipnir::builder() 24 | .roots_of("mountains".to_string()) 25 | .build(); 26 | 27 | assert_eq!(gleipnir.roots_of, "mountains".to_string()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ch6/ch6-refactored/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "builder-macro", 6 | "builder-code", 7 | "builder-usage" 8 | ] 9 | -------------------------------------------------------------------------------- /ch6/ch6-refactored/README.md: -------------------------------------------------------------------------------- 1 | # Refactored 2 | 3 | ## Overview 4 | 5 | This is the third implementation of the 'builder' macro from chapter 6. 6 | 7 | It demonstrates the macro setup with three directories, a real implementation of the builder (this time with moving of the builder to avoid cloning of properties), white-box and black-box unit testing. 8 | -------------------------------------------------------------------------------- /ch6/ch6-refactored/builder-code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-code" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | proc-macro2 = "1.0.69" 10 | -------------------------------------------------------------------------------- /ch6/ch6-refactored/builder-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-code = { path = "../builder-code" } 8 | 9 | [lib] 10 | proc-macro = true 11 | -------------------------------------------------------------------------------- /ch6/ch6-refactored/builder-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use builder_code::create_builder; 3 | 4 | #[proc_macro_derive(Builder)] 5 | pub fn builder(item: TokenStream) -> TokenStream { 6 | create_builder(item.into()).into() 7 | } 8 | -------------------------------------------------------------------------------- /ch6/ch6-refactored/builder-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-macro = { path = "../builder-macro" } 8 | -------------------------------------------------------------------------------- /ch6/ch6-unhappy-path/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "builder-macro", 6 | "builder-code", 7 | "builder-usage" 8 | ] 9 | -------------------------------------------------------------------------------- /ch6/ch6-unhappy-path/README.md: -------------------------------------------------------------------------------- 1 | # Unhappy path 2 | 3 | ## Overview 4 | 5 | This is the final implementation of the 'builder' macro from chapter 6. 6 | 7 | The main difference with the refactored version, is that this one also has failure/compilation tests in the `builder-usage/tests` folder. 8 | -------------------------------------------------------------------------------- /ch6/ch6-unhappy-path/builder-code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-code" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | proc-macro2 = "1.0.69" 10 | -------------------------------------------------------------------------------- /ch6/ch6-unhappy-path/builder-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-code = { path = "../builder-code" } 8 | 9 | [lib] 10 | proc-macro = true 11 | -------------------------------------------------------------------------------- /ch6/ch6-unhappy-path/builder-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use builder_code::create_builder; 3 | 4 | #[proc_macro_derive(Builder)] 5 | pub fn builder(item: TokenStream) -> TokenStream { 6 | create_builder(item.into()).into() 7 | } 8 | -------------------------------------------------------------------------------- /ch6/ch6-unhappy-path/builder-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-macro = { path = "../builder-macro" } 8 | 9 | [dev-dependencies] 10 | trybuild = "1.0.85" 11 | -------------------------------------------------------------------------------- /ch6/ch6-unhappy-path/builder-usage/tests/compilation_tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn should_not_compile() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/fails/*.rs"); 5 | } -------------------------------------------------------------------------------- /ch6/ch6-unhappy-path/builder-usage/tests/fails/build_enum.rs: -------------------------------------------------------------------------------- 1 | use builder_macro::Builder; 2 | 3 | #[derive(Builder)] 4 | pub enum ExampleEnum {} 5 | 6 | fn main() {} -------------------------------------------------------------------------------- /ch6/ch6-unhappy-path/builder-usage/tests/fails/build_enum.stderr: -------------------------------------------------------------------------------- 1 | error: proc-macro derive panicked 2 | --> tests/fails/build_enum.rs:3:10 3 | | 4 | 3 | #[derive(Builder)] 5 | | ^^^^^^^ 6 | | 7 | = help: message: not implemented: only implemented for structs 8 | -------------------------------------------------------------------------------- /ch7/ch7-better-errors/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "panic-to-result-macro", 6 | "panic-to-result-usage" 7 | ] 8 | -------------------------------------------------------------------------------- /ch7/ch7-better-errors/README.md: -------------------------------------------------------------------------------- 1 | # Better errors 2 | 3 | ## Overview 4 | 5 | This is the fourth implementation of the 'panic-to-result' macro from chapter 7. 6 | 7 | We are now using `syn::Error` for better error handling and compile time warnings. 8 | -------------------------------------------------------------------------------- /ch7/ch7-better-errors/panic-to-result-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "panic-to-result-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | proc-macro2 = "1.0.69" 8 | quote = "1.0.33" 9 | syn = { version = "2.0.39", features = ["full", "extra-traits"]} 10 | 11 | [lib] 12 | proc-macro = true 13 | -------------------------------------------------------------------------------- /ch7/ch7-better-errors/panic-to-result-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "panic-to-result-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | panic-to-result-macro = { path = "../panic-to-result-macro" } 8 | 9 | [dev-dependencies] 10 | trybuild = "1.0.85" 11 | -------------------------------------------------------------------------------- /ch7/ch7-better-errors/panic-to-result-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | use panic_to_result_macro::panic_to_result; 2 | 3 | #[allow(dead_code)] 4 | #[derive(Debug)] 5 | pub struct Person { 6 | name: String, 7 | age: u32, 8 | } 9 | 10 | #[panic_to_result] 11 | fn create_person(name: String, age: u32) -> Person { 12 | if age > 30 { 13 | panic!("I hope I die before I get old"); 14 | } 15 | Person { 16 | name, 17 | age, 18 | } 19 | } 20 | 21 | fn main() {} 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use super::*; 26 | 27 | #[test] 28 | fn happy_path() { 29 | let actual = create_person("Sam".to_string(), 22).unwrap(); 30 | 31 | assert_eq!(actual.name, "Sam".to_string()); 32 | assert_eq!(actual.age, 22); 33 | } 34 | 35 | #[test] 36 | fn should_err_on_invalid_age() { 37 | let actual = create_person("S".to_string(), 32); 38 | 39 | assert_eq!(actual.expect_err("This should be an err"), "I hope I die before I get old".to_string()); 40 | } 41 | } -------------------------------------------------------------------------------- /ch7/ch7-better-errors/panic-to-result-usage/tests/compilation_tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn should_not_compile() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/fails/*.rs"); 5 | } 6 | -------------------------------------------------------------------------------- /ch7/ch7-better-errors/panic-to-result-usage/tests/fails/create_person_with_empty_panic.rs: -------------------------------------------------------------------------------- 1 | use panic_to_result_macro::panic_to_result; 2 | 3 | #[derive(Debug)] 4 | pub struct Person { 5 | name: String, 6 | age: u32, 7 | } 8 | 9 | #[panic_to_result] 10 | fn create_person_with_empty_panic(name: String, age: u32) -> Person { 11 | if age > 30 { 12 | panic!(); 13 | } 14 | Person { 15 | name, 16 | age, 17 | } 18 | } 19 | 20 | fn main() {} 21 | -------------------------------------------------------------------------------- /ch7/ch7-better-errors/panic-to-result-usage/tests/fails/create_person_with_empty_panic.stderr: -------------------------------------------------------------------------------- 1 | error: please make sure every panic in your function has a message 2 | --> tests/fails/create_person_with_empty_panic.rs:12:9 3 | | 4 | 12 | panic!(); 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /ch7/ch7-better-errors/panic-to-result-usage/tests/fails/create_person_with_result.rs: -------------------------------------------------------------------------------- 1 | use panic_to_result_macro::panic_to_result; 2 | 3 | #[derive(Debug)] 4 | pub struct Person { 5 | name: String, 6 | age: u32, 7 | } 8 | 9 | #[panic_to_result] 10 | fn create_person_with_result(name: String, age: u32) -> Result { 11 | if age > 30 { 12 | panic!("I hope I die before I get old"); 13 | } 14 | Ok(Person { 15 | name, 16 | age, 17 | }) 18 | } 19 | 20 | fn main() {} 21 | -------------------------------------------------------------------------------- /ch7/ch7-better-errors/panic-to-result-usage/tests/fails/create_person_with_result.stderr: -------------------------------------------------------------------------------- 1 | error: this macro can only be applied to a function that does not return a Result. Signature: Result < Person, String > 2 | --> tests/fails/create_person_with_result.rs:10:1 3 | | 4 | 10 | fn create_person_with_result(name: String, age: u32) -> Result { 5 | | ^^ 6 | -------------------------------------------------------------------------------- /ch7/ch7-better-errors/panic-to-result-usage/tests/fails/create_person_with_result_and_empty_panic.rs: -------------------------------------------------------------------------------- 1 | use panic_to_result_macro::panic_to_result; 2 | 3 | #[derive(Debug)] 4 | pub struct Person { 5 | name: String, 6 | age: u32, 7 | } 8 | 9 | #[panic_to_result] 10 | fn create_person_with_result(name: String, age: u32) -> Result { 11 | if age > 30 { 12 | panic!(); 13 | } 14 | Ok(Person { 15 | name, 16 | age, 17 | }) 18 | } 19 | 20 | fn main() {} 21 | -------------------------------------------------------------------------------- /ch7/ch7-better-errors/panic-to-result-usage/tests/fails/create_person_with_result_and_empty_panic.stderr: -------------------------------------------------------------------------------- 1 | error: please make sure every panic in your function has a message 2 | --> tests/fails/create_person_with_result_and_empty_panic.rs:12:9 3 | | 4 | 12 | panic!(); 5 | | ^^^^^ 6 | 7 | error: this macro can only be applied to a function that does not return a Result. Signature: Result < Person, String > 8 | --> tests/fails/create_person_with_result_and_empty_panic.rs:10:1 9 | | 10 | 10 | fn create_person_with_result(name: String, age: u32) -> Result { 11 | | ^^ 12 | -------------------------------------------------------------------------------- /ch7/ch7-expand-panic-exercise/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "panic-to-result-macro", 6 | "panic-to-result-usage" 7 | ] 8 | -------------------------------------------------------------------------------- /ch7/ch7-expand-panic-exercise/README.md: -------------------------------------------------------------------------------- 1 | # Expand Panic 2 | 3 | ## Overview 4 | 5 | This is the solution to the exercise from chapter 7 where we ask you to also detect panics in `while` expressions. 6 | -------------------------------------------------------------------------------- /ch7/ch7-expand-panic-exercise/panic-to-result-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "panic-to-result-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | proc-macro-error = "1.0.4" 8 | proc-macro2 = "1.0.69" 9 | quote = "1.0.33" 10 | syn = { version = "2.0.39", features = ["full", "extra-traits"]} 11 | 12 | [lib] 13 | proc-macro = true 14 | -------------------------------------------------------------------------------- /ch7/ch7-expand-panic-exercise/panic-to-result-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "expand-panic-exercise" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | panic-to-result-macro = { path = "../panic-to-result-macro" } 8 | 9 | [dev-dependencies] 10 | trybuild = "1.0.85" 11 | -------------------------------------------------------------------------------- /ch7/ch7-proc-macro-errors-exercise/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch7-proc-macro-errors-exercise" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | proc-macro-errors-exercise = { path = "./private-macro" } 8 | -------------------------------------------------------------------------------- /ch7/ch7-proc-macro-errors-exercise/README.md: -------------------------------------------------------------------------------- 1 | # Proc Macro Error 2 | 3 | ## Overview 4 | 5 | This is the solution to the exercise from chapter 7 where we ask you to change the `unimplemented` panic to something befitting `proc-macro-errors`. 6 | -------------------------------------------------------------------------------- /ch7/ch7-proc-macro-errors-exercise/private-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "proc-macro-errors-exercise" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | proc-macro-error = "1.0.4" 8 | quote = "1.0.33" 9 | syn = "2.0.39" 10 | 11 | [lib] 12 | proc-macro = true 13 | -------------------------------------------------------------------------------- /ch7/ch7-proc-macro-errors-exercise/src/main.rs: -------------------------------------------------------------------------------- 1 | use crate::other_file::Example; 2 | 3 | mod other_file; 4 | 5 | fn main() { 6 | let e = Example::new(); 7 | 8 | e.get_string_value(); 9 | e.get_number_value(); 10 | } 11 | -------------------------------------------------------------------------------- /ch7/ch7-proc-macro-errors-exercise/src/other_file.rs: -------------------------------------------------------------------------------- 1 | use proc_macro_errors_exercise::private; 2 | 3 | private!( 4 | struct Example { 5 | pub string_value: String, 6 | pub number_value: i32, 7 | } 8 | ); 9 | 10 | // will fail 11 | // private!( 12 | // enum Example { 13 | // First 14 | // } 15 | // ); 16 | -------------------------------------------------------------------------------- /ch7/ch7-proc-macro-errors/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "panic-to-result-macro", 6 | "panic-to-result-usage" 7 | ] 8 | -------------------------------------------------------------------------------- /ch7/ch7-proc-macro-errors/README.md: -------------------------------------------------------------------------------- 1 | # Proc Macro Errors 2 | 3 | ## Overview 4 | 5 | This is the fifth implementation of the 'panic-to-result' macro from chapter 7. 6 | 7 | We are now `proc-macro-error` for better error handling and compile time warnings, as an alternative to `syn::Error`. 8 | -------------------------------------------------------------------------------- /ch7/ch7-proc-macro-errors/panic-to-result-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "panic-to-result-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | proc-macro-error = "1.0.4" 8 | proc-macro2 = "1.0.69" 9 | quote = "1.0.33" 10 | syn = { version = "2.0.39", features = ["full", "extra-traits"]} 11 | 12 | [lib] 13 | proc-macro = true 14 | -------------------------------------------------------------------------------- /ch7/ch7-proc-macro-errors/panic-to-result-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "panic-to-result-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | panic-to-result-macro = { path = "../panic-to-result-macro" } 8 | 9 | [dev-dependencies] 10 | trybuild = "1.0.85" 11 | -------------------------------------------------------------------------------- /ch7/ch7-proc-macro-errors/panic-to-result-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | use panic_to_result_macro::panic_to_result; 2 | 3 | #[allow(dead_code)] 4 | #[derive(Debug)] 5 | pub struct Person { 6 | name: String, 7 | age: u32, 8 | } 9 | 10 | #[panic_to_result] 11 | fn create_person(name: String, age: u32) -> Person { 12 | if age > 30 { 13 | panic!("I hope I die before I get old"); 14 | } 15 | Person { 16 | name, 17 | age, 18 | } 19 | } 20 | 21 | fn main() {} 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use super::*; 26 | 27 | #[test] 28 | fn happy_path() { 29 | let actual = create_person("Sam".to_string(), 22).unwrap(); 30 | 31 | assert_eq!(actual.name, "Sam".to_string()); 32 | assert_eq!(actual.age, 22); 33 | } 34 | 35 | #[test] 36 | fn should_err_on_invalid_age() { 37 | let actual = create_person("S".to_string(), 32); 38 | 39 | assert_eq!(actual.expect_err("This should be an err"), "I hope I die before I get old".to_string()); 40 | } 41 | } -------------------------------------------------------------------------------- /ch7/ch7-proc-macro-errors/panic-to-result-usage/tests/compilation_tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn should_not_compile() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/fails/*.rs"); 5 | } 6 | -------------------------------------------------------------------------------- /ch7/ch7-proc-macro-errors/panic-to-result-usage/tests/fails/create_person_two_issues.rs: -------------------------------------------------------------------------------- 1 | use panic_to_result_macro::panic_to_result; 2 | 3 | #[derive(Debug)] 4 | pub struct Person { 5 | name: String, 6 | age: u32, 7 | } 8 | 9 | #[panic_to_result] 10 | fn create_person_two_issues(name: String, age: u32) -> Result { 11 | if age > 30 { 12 | panic!(); 13 | } 14 | Ok(Person { 15 | name, 16 | age, 17 | }) 18 | } 19 | 20 | fn main() {} 21 | -------------------------------------------------------------------------------- /ch7/ch7-proc-macro-errors/panic-to-result-usage/tests/fails/create_person_two_issues.stderr: -------------------------------------------------------------------------------- 1 | error: panic needs a message! 2 | 3 | = help: try to add a message: panic!("Example".to_string()) 4 | = note: we will add the message to Result's Err 5 | 6 | --> tests/fails/create_person_two_issues.rs:12:9 7 | | 8 | 12 | panic!(); 9 | | ^^^^^^^^^ 10 | 11 | error: this macro can only be applied to a function that does not yet return a Result. Signature: Result < String, Person > 12 | --> tests/fails/create_person_two_issues.rs:10:56 13 | | 14 | 10 | fn create_person_two_issues(name: String, age: u32) -> Result { 15 | | ^^^^^^^^^^^^^^^^^^^^^^ 16 | -------------------------------------------------------------------------------- /ch7/ch7-proc-macro-errors/panic-to-result-usage/tests/fails/create_person_with_empty_panic.rs: -------------------------------------------------------------------------------- 1 | use panic_to_result_macro::panic_to_result; 2 | 3 | #[derive(Debug)] 4 | pub struct Person { 5 | name: String, 6 | age: u32, 7 | } 8 | 9 | #[panic_to_result] 10 | fn create_person_with_empty_panic(name: String, age: u32) -> Person { 11 | if age > 30 { 12 | panic!(); 13 | } 14 | Person { 15 | name, 16 | age, 17 | } 18 | } 19 | 20 | fn main() {} 21 | -------------------------------------------------------------------------------- /ch7/ch7-proc-macro-errors/panic-to-result-usage/tests/fails/create_person_with_empty_panic.stderr: -------------------------------------------------------------------------------- 1 | error: panic needs a message! 2 | 3 | = help: try to add a message: panic!("Example".to_string()) 4 | = note: we will add the message to Result's Err 5 | 6 | --> tests/fails/create_person_with_empty_panic.rs:12:9 7 | | 8 | 12 | panic!(); 9 | | ^^^^^^^^^ 10 | -------------------------------------------------------------------------------- /ch7/ch7-proc-macro-errors/panic-to-result-usage/tests/fails/create_person_with_result.rs: -------------------------------------------------------------------------------- 1 | use panic_to_result_macro::panic_to_result; 2 | 3 | #[derive(Debug)] 4 | pub struct Person { 5 | name: String, 6 | age: u32, 7 | } 8 | 9 | #[panic_to_result] 10 | fn create_person_with_result(name: String, age: u32) -> Result { 11 | if age > 30 { 12 | panic!("I hope I die before I get old"); 13 | } 14 | Ok(Person { 15 | name, 16 | age, 17 | }) 18 | } 19 | 20 | fn main() {} 21 | -------------------------------------------------------------------------------- /ch7/ch7-proc-macro-errors/panic-to-result-usage/tests/fails/create_person_with_result.stderr: -------------------------------------------------------------------------------- 1 | error: this macro can only be applied to a function that does not yet return a Result. Signature: Result < Person, String > 2 | --> tests/fails/create_person_with_result.rs:10:57 3 | | 4 | 10 | fn create_person_with_result(name: String, age: u32) -> Result { 5 | | ^^^^^^^^^^^^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /ch7/ch7-public-fields-exercise/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch7-public-fields-exercise" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | make-public-macro = { path = "./make-public-macro" } 8 | -------------------------------------------------------------------------------- /ch7/ch7-public-fields-exercise/README.md: -------------------------------------------------------------------------------- 1 | # Public fields 2 | 3 | ## Overview 4 | 5 | This is the solution to the exercise from chapter 7 where we ask you to mutate the `TokenStream` instead of creating a new one. 6 | 7 | We use the project setup from chapter 4, where this macro was first introduced. 8 | -------------------------------------------------------------------------------- /ch7/ch7-public-fields-exercise/make-public-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "make-public-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = "2.0.39" 9 | 10 | [lib] 11 | proc-macro = true -------------------------------------------------------------------------------- /ch7/ch7-public-fields-exercise/src/main.rs: -------------------------------------------------------------------------------- 1 | use make_public_macro::public; 2 | 3 | #[public] 4 | struct Example { 5 | first: String, 6 | pub second: u32, 7 | } 8 | 9 | fn main() { 10 | let _e = Example { 11 | first: "first".to_string(), 12 | second: 5, 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /ch7/ch7-result-instead-of-panic/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "panic-to-result-macro", 6 | "panic-to-result-usage" 7 | ] 8 | -------------------------------------------------------------------------------- /ch7/ch7-result-instead-of-panic/README.md: -------------------------------------------------------------------------------- 1 | # Result instead of panic 2 | 3 | ## Overview 4 | 5 | This is the third implementation of the 'panic-to-result' macro from chapter 7. 6 | 7 | After changing the signature and return, we turn to converting the panic into a `Result`. 8 | -------------------------------------------------------------------------------- /ch7/ch7-result-instead-of-panic/panic-to-result-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "panic-to-result-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | proc-macro2 = "1.0.69" 8 | quote = "1.0.33" 9 | syn = { version = "2.0.39", features = ["full", "extra-traits"]} 10 | 11 | [lib] 12 | proc-macro = true 13 | -------------------------------------------------------------------------------- /ch7/ch7-result-instead-of-panic/panic-to-result-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "panic-to-result-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | panic-to-result-macro = { path = "../panic-to-result-macro" } 8 | -------------------------------------------------------------------------------- /ch7/ch7-result-instead-of-panic/panic-to-result-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | use panic_to_result_macro::panic_to_result; 2 | 3 | #[allow(dead_code)] 4 | #[derive(Debug)] 5 | pub struct Person { 6 | name: String, 7 | age: u32, 8 | } 9 | 10 | #[panic_to_result] 11 | fn create_person(name: String, age: u32) -> Person { 12 | if age > 30 { 13 | panic!("I hope I die before I get old"); 14 | } 15 | Person { 16 | name, 17 | age, 18 | } 19 | } 20 | 21 | fn main() {} 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use super::*; 26 | 27 | #[test] 28 | fn happy_path() { 29 | let actual = create_person("Sam".to_string(), 22).unwrap(); 30 | 31 | assert_eq!(actual.name, "Sam".to_string()); 32 | assert_eq!(actual.age, 22); 33 | } 34 | 35 | #[test] 36 | fn should_err_on_invalid_age() { 37 | let actual = create_person("S".to_string(), 32); 38 | 39 | assert_eq!(actual.expect_err("This should be an err"), "I hope I die before I get old".to_string()); 40 | } 41 | } -------------------------------------------------------------------------------- /ch7/ch7-setup/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "panic-to-result-macro", 6 | "panic-to-result-usage" 7 | ] 8 | -------------------------------------------------------------------------------- /ch7/ch7-setup/README.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | ## Overview 4 | 5 | This is the first implementation of the 'panic-to-result' macro from chapter 7. 6 | 7 | It demonstrates the project setup we will be using in this chapter. Apart from that, it does not do anything, except returning the existing code and verifying the behavior that we will change later on. 8 | -------------------------------------------------------------------------------- /ch7/ch7-setup/panic-to-result-macro/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "panic-to-result-macro" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "quote", 10 | "syn", 11 | ] 12 | 13 | [[package]] 14 | name = "proc-macro2" 15 | version = "1.0.63" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" 18 | dependencies = [ 19 | "unicode-ident", 20 | ] 21 | 22 | [[package]] 23 | name = "quote" 24 | version = "1.0.29" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" 27 | dependencies = [ 28 | "proc-macro2", 29 | ] 30 | 31 | [[package]] 32 | name = "syn" 33 | version = "2.0.27" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" 36 | dependencies = [ 37 | "proc-macro2", 38 | "quote", 39 | "unicode-ident", 40 | ] 41 | 42 | [[package]] 43 | name = "unicode-ident" 44 | version = "1.0.10" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" 47 | -------------------------------------------------------------------------------- /ch7/ch7-setup/panic-to-result-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "panic-to-result-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["full", "extra-traits"]} 9 | 10 | [lib] 11 | proc-macro = true 12 | -------------------------------------------------------------------------------- /ch7/ch7-setup/panic-to-result-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::{ToTokens}; 3 | use syn::ItemFn; 4 | 5 | #[proc_macro_attribute] 6 | pub fn panic_to_result(_attr: TokenStream, item: TokenStream) -> TokenStream { 7 | let ast: ItemFn = syn::parse(item).unwrap(); 8 | ast.to_token_stream().into() 9 | } 10 | 11 | -------------------------------------------------------------------------------- /ch7/ch7-setup/panic-to-result-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "panic-to-result-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | panic-to-result-macro = { path = "../panic-to-result-macro" } 8 | -------------------------------------------------------------------------------- /ch7/ch7-setup/panic-to-result-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | use panic_to_result_macro::panic_to_result; 2 | 3 | #[allow(dead_code)] 4 | #[derive(Debug)] 5 | pub struct Person { 6 | name: String, 7 | age: u32, 8 | } 9 | 10 | #[panic_to_result] 11 | fn create_person(name: String, age: u32) -> Person { 12 | if age > 30 { 13 | panic!("I hope I die before I get old"); 14 | } 15 | Person { 16 | name, 17 | age, 18 | } 19 | } 20 | 21 | fn main() {} 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use super::*; 26 | 27 | #[test] 28 | fn happy_path() { 29 | let actual = create_person("Sam".to_string(), 22); 30 | 31 | assert_eq!(actual.name, "Sam".to_string()); 32 | assert_eq!(actual.age, 22); 33 | } 34 | 35 | #[test] 36 | #[should_panic] 37 | fn should_panic_on_invalid_age() { 38 | create_person("S".to_string(), 32); 39 | } 40 | } -------------------------------------------------------------------------------- /ch7/ch7-signature-changed/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "panic-to-result-macro", 6 | "panic-to-result-usage" 7 | ] 8 | -------------------------------------------------------------------------------- /ch7/ch7-signature-changed/README.md: -------------------------------------------------------------------------------- 1 | # Signature changed 2 | 3 | ## Overview 4 | 5 | This is the second implementation of the 'panic-to-result' macro from chapter 7. 6 | 7 | It shows how we change the signature of the function as well as the final return. 8 | -------------------------------------------------------------------------------- /ch7/ch7-signature-changed/panic-to-result-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "panic-to-result-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["full", "extra-traits"]} 9 | 10 | [lib] 11 | proc-macro = true 12 | -------------------------------------------------------------------------------- /ch7/ch7-signature-changed/panic-to-result-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::{quote, ToTokens}; 3 | use syn::{ItemFn, ReturnType, Stmt}; 4 | 5 | fn signature_output_as_result(ast: &ItemFn) -> ReturnType { 6 | let output = match ast.sig.output { 7 | ReturnType::Default => { 8 | quote! { 9 | -> Result<(), String> 10 | } 11 | } 12 | ReturnType::Type(_, ref ty) => { 13 | quote! { 14 | -> Result<#ty, String> 15 | } 16 | } 17 | }; 18 | syn::parse2(output).unwrap() 19 | } 20 | 21 | fn last_statement_as_result(last_statement: Option) -> Stmt { 22 | let last_unwrapped = last_statement.unwrap(); 23 | let last_modified = quote! { 24 | Ok(#last_unwrapped) 25 | }; 26 | Stmt::Expr(syn::parse2(last_modified).unwrap(), None) 27 | } 28 | 29 | #[proc_macro_attribute] 30 | pub fn panic_to_result(_attr: TokenStream, item: TokenStream) -> TokenStream { 31 | let mut ast: ItemFn = syn::parse(item).unwrap(); 32 | 33 | ast.sig.output = signature_output_as_result(&ast); 34 | let last_statement = ast.block.stmts.pop(); 35 | ast.block.stmts.push(last_statement_as_result(last_statement)); 36 | 37 | ast.to_token_stream().into() 38 | } 39 | 40 | -------------------------------------------------------------------------------- /ch7/ch7-signature-changed/panic-to-result-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "panic-to-result-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | panic-to-result-macro = { path = "../panic-to-result-macro" } 8 | -------------------------------------------------------------------------------- /ch7/ch7-signature-changed/panic-to-result-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | use panic_to_result_macro::panic_to_result; 2 | 3 | #[allow(dead_code)] 4 | #[derive(Debug)] 5 | pub struct Person { 6 | name: String, 7 | age: u32, 8 | } 9 | 10 | #[panic_to_result] 11 | fn create_person(name: String, age: u32) -> Person { 12 | if age > 30 { 13 | panic!("I hope I die before I get old"); 14 | } 15 | Person { 16 | name, 17 | age, 18 | } 19 | } 20 | 21 | fn main() {} 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use super::*; 26 | 27 | #[test] 28 | fn happy_path() { 29 | let actual = create_person("Sam".to_string(), 22).unwrap(); 30 | 31 | assert_eq!(actual.name, "Sam".to_string()); 32 | assert_eq!(actual.age, 22); 33 | } 34 | 35 | #[test] 36 | #[should_panic] 37 | fn should_panic_on_invalid_age() { 38 | let _ = create_person("S".to_string(), 32); 39 | } 40 | } -------------------------------------------------------------------------------- /ch7/ch7-syn-errors-exercise/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch7-syn-errors-exercise" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | syn-errors-exercise = { path = "./private-macro" } 8 | -------------------------------------------------------------------------------- /ch7/ch7-syn-errors-exercise/README.md: -------------------------------------------------------------------------------- 1 | # Syn errors 2 | 3 | ## Overview 4 | 5 | This is the solution to the exercise from chapter 7 where we ask you to change the `unimplemented` panic to `syn::Error`. 6 | -------------------------------------------------------------------------------- /ch7/ch7-syn-errors-exercise/private-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "syn-errors-exercise" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = "2.0.39" 9 | 10 | [lib] 11 | proc-macro = true 12 | -------------------------------------------------------------------------------- /ch7/ch7-syn-errors-exercise/src/main.rs: -------------------------------------------------------------------------------- 1 | use crate::other_file::Example; 2 | 3 | mod other_file; 4 | 5 | fn main() { 6 | let e = Example::new(); 7 | 8 | e.get_string_value(); 9 | e.get_number_value(); 10 | } 11 | -------------------------------------------------------------------------------- /ch7/ch7-syn-errors-exercise/src/other_file.rs: -------------------------------------------------------------------------------- 1 | use syn_errors_exercise::private; 2 | 3 | private!( 4 | struct Example { 5 | pub string_value: String, 6 | pub number_value: i32, 7 | } 8 | ); 9 | 10 | // will fail 11 | // private!( 12 | // enum Example { 13 | // First 14 | // } 15 | // ); 16 | -------------------------------------------------------------------------------- /ch8/ch8-build-back-better/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "builder-macro", 6 | "builder-code", 7 | "builder-usage" 8 | ] 9 | -------------------------------------------------------------------------------- /ch8/ch8-build-back-better/README.md: -------------------------------------------------------------------------------- 1 | # Build back better 2 | 3 | ## Overview 4 | 5 | This is the code for the 'type state' example from chapter 8. 6 | -------------------------------------------------------------------------------- /ch8/ch8-build-back-better/builder-code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-code" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | proc-macro2 = "1.0.69" 10 | -------------------------------------------------------------------------------- /ch8/ch8-build-back-better/builder-code/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod fields; 2 | mod util; 3 | 4 | use proc_macro2::{TokenStream}; 5 | use quote::{quote}; 6 | use syn::{DeriveInput, parse2}; 7 | use syn::Data::Struct; 8 | use syn::DataStruct; 9 | use syn::Fields::Named; 10 | use syn::FieldsNamed; 11 | use crate::fields::{builder_definition, builder_impl_for_struct, builder_methods, marker_trait_and_structs}; 12 | 13 | pub fn create_builder(item: TokenStream) -> TokenStream { 14 | let ast: DeriveInput = parse2(item).unwrap(); 15 | let name = ast.ident; 16 | 17 | let fields = match ast.data { 18 | Struct(DataStruct { fields: Named(FieldsNamed { ref named, .. }), .. }) => named, 19 | _ => unimplemented!("only implemented for structs"), 20 | }; 21 | let builder = builder_definition(&name, fields); 22 | let builder_method_for_struct = builder_impl_for_struct(&name, fields); 23 | let marker_and_structs = marker_trait_and_structs(&name, fields); 24 | let builder_methods = builder_methods(&name, fields); 25 | 26 | quote! { 27 | #builder 28 | #builder_method_for_struct 29 | #marker_and_structs 30 | #builder_methods 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ch8/ch8-build-back-better/builder-code/src/util.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::Ident; 2 | use quote::format_ident; 3 | 4 | pub fn create_builder_ident(name: &Ident) -> Ident { 5 | format_ident!("{}Builder", name) 6 | } 7 | 8 | pub fn create_field_struct_name(builder_name: &Ident, field_name: &Ident) -> Ident { 9 | format_ident!("{}Of{}", field_name, builder_name) 10 | } 11 | -------------------------------------------------------------------------------- /ch8/ch8-build-back-better/builder-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-code = { path = "../builder-code" } 8 | 9 | [lib] 10 | proc-macro = true 11 | -------------------------------------------------------------------------------- /ch8/ch8-build-back-better/builder-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use builder_code::create_builder; 3 | 4 | #[proc_macro_derive(Builder)] 5 | pub fn builder(item: TokenStream) -> TokenStream { 6 | create_builder(item.into()).into() 7 | } 8 | -------------------------------------------------------------------------------- /ch8/ch8-build-back-better/builder-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-macro = { path = "../builder-macro" } 8 | 9 | [dev-dependencies] 10 | trybuild = "1.0.85" 11 | -------------------------------------------------------------------------------- /ch8/ch8-build-back-better/builder-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use builder_macro::Builder; 6 | 7 | #[test] 8 | fn should_work_with_correct_order() { 9 | #[derive(Builder)] 10 | struct Gleipnir { 11 | roots_of: String, 12 | breath_of_a_fish: u8, 13 | anything_else: bool, 14 | } 15 | 16 | let gleipnir = Gleipnir::builder() 17 | .roots_of("mountains".to_string()) 18 | .breath_of_a_fish(1) 19 | .anything_else(true) 20 | .build(); 21 | 22 | assert_eq!(gleipnir.roots_of, "mountains".to_string()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ch8/ch8-build-back-better/builder-usage/tests/compilation_tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn should_not_compile() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/fails/*.rs"); 5 | } -------------------------------------------------------------------------------- /ch8/ch8-build-back-better/builder-usage/tests/fails/missing_prop.rs: -------------------------------------------------------------------------------- 1 | use builder_macro::Builder; 2 | 3 | #[derive(Builder)] 4 | struct Gleipnir { 5 | roots_of: String, 6 | breath_of_a_fish: u8, 7 | anything_else: bool, 8 | } 9 | 10 | fn main() { 11 | // not every property 12 | Gleipnir::builder() 13 | .roots_of("mountains".to_string()) 14 | .breath_of_a_fish(1) 15 | .build(); 16 | } 17 | -------------------------------------------------------------------------------- /ch8/ch8-builder-rename-exercise/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "builder-macro", 6 | "builder-code", 7 | "builder-usage" 8 | ] 9 | -------------------------------------------------------------------------------- /ch8/ch8-builder-rename-exercise/README.md: -------------------------------------------------------------------------------- 1 | # Builder rename 2 | 3 | ## Overview 4 | 5 | This is the solution to the exercise from chapter 8 where we ask you to rewrite the `#[rename("...")]` attribute to instead look like `#[builder(rename = "...")]`. 6 | -------------------------------------------------------------------------------- /ch8/ch8-builder-rename-exercise/builder-code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-code" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.27", features = ["extra-traits"]} 9 | proc-macro2 = "1.0.69" 10 | -------------------------------------------------------------------------------- /ch8/ch8-builder-rename-exercise/builder-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-code = { path = "../builder-code" } 8 | 9 | [lib] 10 | proc-macro = true 11 | -------------------------------------------------------------------------------- /ch8/ch8-builder-rename-exercise/builder-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use builder_code::create_builder; 3 | 4 | #[proc_macro_derive(Builder, attributes(builder))] 5 | pub fn builder(item: TokenStream) -> TokenStream { 6 | create_builder(item.into()).into() 7 | } 8 | -------------------------------------------------------------------------------- /ch8/ch8-builder-rename-exercise/builder-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-macro = { path = "../builder-macro" } 8 | -------------------------------------------------------------------------------- /ch8/ch8-builder-rename-exercise/builder-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use builder_macro::Builder; 6 | 7 | #[test] 8 | fn should_generate_builder_for_struct_with_one_property() { 9 | #[derive(Builder)] 10 | struct Gleipnir { 11 | roots_of: String, 12 | } 13 | 14 | let gleipnir = Gleipnir::builder() 15 | .roots_of("mountains".to_string()) 16 | .build(); 17 | 18 | assert_eq!(gleipnir.roots_of, "mountains".to_string()); 19 | } 20 | 21 | #[test] 22 | fn should_generate_builder_for_struct_with_one_renamed_property() { 23 | #[derive(Builder)] 24 | struct Gleipnir { 25 | #[builder(rename = "tops_of")] 26 | roots_of: String, 27 | } 28 | 29 | let gleipnir = Gleipnir::builder() 30 | .tops_of("mountains".to_string()) 31 | .build(); 32 | 33 | assert_eq!(gleipnir.roots_of, "mountains".to_string()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ch8/ch8-default-assertion-capital-letter-exercise/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "builder-macro", 6 | "builder-code", 7 | "builder-usage" 8 | ] 9 | -------------------------------------------------------------------------------- /ch8/ch8-default-assertion-capital-letter-exercise/README.md: -------------------------------------------------------------------------------- 1 | # Capital letter warning 2 | 3 | ## Overview 4 | 5 | This is the solution to the exercise from chapter 8 where we ask you to fix the `type __notDefaultAssertion should have an upper camel case name` warning. 6 | -------------------------------------------------------------------------------- /ch8/ch8-default-assertion-capital-letter-exercise/builder-code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-code" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | proc-macro2 = "1.0.69" 10 | -------------------------------------------------------------------------------- /ch8/ch8-default-assertion-capital-letter-exercise/builder-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-code = { path = "../builder-code" } 8 | 9 | [lib] 10 | proc-macro = true 11 | -------------------------------------------------------------------------------- /ch8/ch8-default-assertion-capital-letter-exercise/builder-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use builder_code::create_builder; 3 | 4 | #[proc_macro_derive(Builder, attributes(builder_defaults,rename))] 5 | pub fn builder(item: TokenStream) -> TokenStream { 6 | create_builder(item.into()).into() 7 | } 8 | -------------------------------------------------------------------------------- /ch8/ch8-default-assertion-capital-letter-exercise/builder-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-macro = { path = "../builder-macro" } 8 | 9 | [dev-dependencies] 10 | trybuild = "1.0.85" 11 | -------------------------------------------------------------------------------- /ch8/ch8-default-assertion-capital-letter-exercise/builder-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use builder_macro::Builder; 6 | 7 | #[test] 8 | fn should_use_defaults_when_attribute_is_present() { 9 | #[derive(Builder)] 10 | #[builder_defaults] 11 | struct Gleipnir { 12 | roots_of: String, 13 | } 14 | 15 | let gleipnir = Gleipnir::builder().build(); 16 | 17 | assert_eq!(gleipnir.roots_of, String::default()); 18 | } 19 | 20 | #[test] 21 | #[should_panic] 22 | fn should_panic_when_attribute_is_not_present() { 23 | #[derive(Builder)] 24 | struct Gleipnir { 25 | roots_of: String, 26 | } 27 | 28 | let gleipnir = Gleipnir::builder().build(); 29 | 30 | assert_eq!(gleipnir.roots_of, String::default()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ch8/ch8-default-assertion-capital-letter-exercise/builder-usage/tests/compilation_tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn should_not_compile() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/fails/*.rs"); 5 | } -------------------------------------------------------------------------------- /ch8/ch8-default-assertion-capital-letter-exercise/builder-usage/tests/fails/no_default.rs: -------------------------------------------------------------------------------- 1 | use builder_macro::Builder; 2 | 3 | struct DoesNotImplementDefault; 4 | 5 | #[derive(Builder)] 6 | #[builder_defaults] 7 | struct ExampleStruct { 8 | not: DoesNotImplementDefault 9 | } 10 | 11 | fn main() {} 12 | -------------------------------------------------------------------------------- /ch8/ch8-default-assertion-capital-letter-exercise/builder-usage/tests/fails/no_default.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `DoesNotImplementDefault: Default` is not satisfied 2 | --> tests/fails/no_default.rs:8:10 3 | | 4 | 8 | not: DoesNotImplementDefault 5 | | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `DoesNotImplementDefault` 6 | | 7 | = help: see issue #48214 8 | help: consider annotating `DoesNotImplementDefault` with `#[derive(Default)]` 9 | | 10 | 3 + #[derive(Default)] 11 | 4 | struct DoesNotImplementDefault; 12 | | 13 | -------------------------------------------------------------------------------- /ch8/ch8-defaults/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "builder-macro", 6 | "builder-code", 7 | "builder-usage" 8 | ] 9 | -------------------------------------------------------------------------------- /ch8/ch8-defaults/README.md: -------------------------------------------------------------------------------- 1 | # Defaults 2 | 3 | ## Overview 4 | 5 | This is the code for the 'builder' macro from chapter 8, where we add a 'defaults' attribute. 6 | -------------------------------------------------------------------------------- /ch8/ch8-defaults/builder-code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-code" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | proc-macro2 = "1.0.69" 10 | -------------------------------------------------------------------------------- /ch8/ch8-defaults/builder-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-code = { path = "../builder-code" } 8 | 9 | [lib] 10 | proc-macro = true 11 | -------------------------------------------------------------------------------- /ch8/ch8-defaults/builder-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use builder_code::create_builder; 3 | 4 | #[proc_macro_derive(Builder, attributes(builder_defaults,rename))] 5 | pub fn builder(item: TokenStream) -> TokenStream { 6 | create_builder(item.into()).into() 7 | } 8 | -------------------------------------------------------------------------------- /ch8/ch8-defaults/builder-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-macro = { path = "../builder-macro" } 8 | -------------------------------------------------------------------------------- /ch8/ch8-defaults/builder-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use builder_macro::Builder; 6 | 7 | #[test] 8 | fn should_use_defaults_when_attribute_is_present() { 9 | #[derive(Builder)] 10 | #[builder_defaults] 11 | struct Gleipnir { 12 | roots_of: String, 13 | } 14 | 15 | let gleipnir = Gleipnir::builder().build(); 16 | 17 | assert_eq!(gleipnir.roots_of, String::default()); 18 | } 19 | 20 | #[test] 21 | #[should_panic] 22 | fn should_panic_when_attribute_is_not_present() { 23 | #[derive(Builder)] 24 | struct Gleipnir { 25 | roots_of: String, 26 | } 27 | 28 | let gleipnir = Gleipnir::builder().build(); 29 | 30 | assert_eq!(gleipnir.roots_of, String::default()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ch8/ch8-error-for-defaults/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "builder-macro", 6 | "builder-code", 7 | "builder-usage" 8 | ] 9 | -------------------------------------------------------------------------------- /ch8/ch8-error-for-defaults/README.md: -------------------------------------------------------------------------------- 1 | # Error for defaults 2 | 3 | ## Overview 4 | 5 | This is the code for the 'builder' macro from chapter 8, where we add a 'defaults' attribute and show how to generate a better error when `Default` is not implemented. 6 | -------------------------------------------------------------------------------- /ch8/ch8-error-for-defaults/builder-code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-code" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | proc-macro2 = "1.0.69" 10 | -------------------------------------------------------------------------------- /ch8/ch8-error-for-defaults/builder-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-code = { path = "../builder-code" } 8 | 9 | [lib] 10 | proc-macro = true 11 | -------------------------------------------------------------------------------- /ch8/ch8-error-for-defaults/builder-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use builder_code::create_builder; 3 | 4 | #[proc_macro_derive(Builder, attributes(builder_defaults,rename))] 5 | pub fn builder(item: TokenStream) -> TokenStream { 6 | create_builder(item.into()).into() 7 | } 8 | -------------------------------------------------------------------------------- /ch8/ch8-error-for-defaults/builder-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-macro = { path = "../builder-macro" } 8 | 9 | [dev-dependencies] 10 | trybuild = "1.0.85" -------------------------------------------------------------------------------- /ch8/ch8-error-for-defaults/builder-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use builder_macro::Builder; 6 | 7 | #[test] 8 | fn should_use_defaults_when_attribute_is_present() { 9 | #[derive(Builder)] 10 | #[builder_defaults] 11 | struct Gleipnir { 12 | roots_of: String, 13 | } 14 | 15 | let gleipnir = Gleipnir::builder().build(); 16 | 17 | assert_eq!(gleipnir.roots_of, String::default()); 18 | } 19 | 20 | #[test] 21 | #[should_panic] 22 | fn should_panic_when_attribute_is_not_present() { 23 | #[derive(Builder)] 24 | struct Gleipnir { 25 | roots_of: String, 26 | } 27 | 28 | let gleipnir = Gleipnir::builder().build(); 29 | 30 | assert_eq!(gleipnir.roots_of, String::default()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ch8/ch8-error-for-defaults/builder-usage/tests/compilation_tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn should_not_compile() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/fails/*.rs"); 5 | } -------------------------------------------------------------------------------- /ch8/ch8-error-for-defaults/builder-usage/tests/fails/no_default.rs: -------------------------------------------------------------------------------- 1 | use builder_macro::Builder; 2 | 3 | struct DoesNotImplementDefault; 4 | 5 | #[derive(Builder)] 6 | #[builder_defaults] 7 | struct ExampleStruct { 8 | not: DoesNotImplementDefault 9 | } 10 | 11 | fn main() {} 12 | -------------------------------------------------------------------------------- /ch8/ch8-error-for-defaults/builder-usage/tests/fails/no_default.stderr: -------------------------------------------------------------------------------- 1 | warning: type `__notDefaultAssertion` should have an upper camel case name 2 | --> tests/fails/no_default.rs:8:5 3 | | 4 | 8 | not: DoesNotImplementDefault 5 | | ^^^ help: convert the identifier to upper camel case: `NotDefaultAssertion` 6 | | 7 | = note: `#[warn(non_camel_case_types)]` on by default 8 | 9 | error[E0277]: the trait bound `DoesNotImplementDefault: Default` is not satisfied 10 | --> tests/fails/no_default.rs:8:10 11 | | 12 | 8 | not: DoesNotImplementDefault 13 | | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `DoesNotImplementDefault` 14 | | 15 | = help: see issue #48214 16 | help: consider annotating `DoesNotImplementDefault` with `#[derive(Default)]` 17 | | 18 | 3 + #[derive(Default)] 19 | 4 | struct DoesNotImplementDefault; 20 | | 21 | -------------------------------------------------------------------------------- /ch8/ch8-other-attributes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "other-attributes" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | other-attributes-macro = { path = "other-attributes-macro" } 8 | -------------------------------------------------------------------------------- /ch8/ch8-other-attributes/README.md: -------------------------------------------------------------------------------- 1 | # Other attributes 2 | 3 | ## Overview 4 | 5 | This is the code from chapter 8, giving a brief demonstration of other attributes (mainly documentation related) 6 | 7 | -------------------------------------------------------------------------------- /ch8/ch8-other-attributes/other-attributes-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "other-attributes-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits", "parsing"]} 9 | 10 | [lib] 11 | proc-macro = true -------------------------------------------------------------------------------- /ch8/ch8-other-attributes/src/main.rs: -------------------------------------------------------------------------------- 1 | use other_attributes_macro::analyze; 2 | 3 | analyze!( 4 | /// outer comment 5 | /** comment block */ 6 | struct Example { 7 | //! inner comment 8 | /*! inner comment block */ 9 | val: String 10 | } 11 | ); 12 | 13 | fn main() {} 14 | -------------------------------------------------------------------------------- /ch8/ch8-public-macro-with-attributes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "make-public" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | make-public-macro = { path = "./make-public-macro" } 8 | -------------------------------------------------------------------------------- /ch8/ch8-public-macro-with-attributes/README.md: -------------------------------------------------------------------------------- 1 | # Public macro with attributes 2 | 3 | ## Overview 4 | 5 | This is the code for the 'public' macro from chapter 8, where we show how you can add properties to the custom attribute that you get when creating an attribute macro. 6 | 7 | The setup is that of chapter 4, where this macro first appeared. 8 | -------------------------------------------------------------------------------- /ch8/ch8-public-macro-with-attributes/make-public-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "make-public-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | 10 | [lib] 11 | proc-macro = true -------------------------------------------------------------------------------- /ch8/ch8-public-macro-with-attributes/src/example.rs: -------------------------------------------------------------------------------- 1 | use make_public_macro::public; 2 | 3 | #[public(exclude(fourth, third))] 4 | struct Example { 5 | first: String, 6 | pub second: u32, 7 | third: bool, 8 | fourth: String, 9 | } 10 | 11 | #[public] 12 | struct AlsoWorksExample { 13 | first: String, 14 | pub second: u32, 15 | } 16 | 17 | impl Example { 18 | pub fn new() -> Self { 19 | Example { 20 | first: "first".to_string(), 21 | second: 5, 22 | third: false, 23 | fourth: "fourth".to_string(), 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /ch8/ch8-public-macro-with-attributes/src/main.rs: -------------------------------------------------------------------------------- 1 | use crate::example::{AlsoWorksExample, Example}; 2 | 3 | mod example; 4 | 5 | fn main() { 6 | let _ = AlsoWorksExample { 7 | first: "".to_string(), 8 | second: 0, 9 | }; 10 | let e = Example::new(); 11 | println!("{}", e.first); 12 | println!("{}", e.second); 13 | // println!("{}", e.third); // won't work 14 | } 15 | -------------------------------------------------------------------------------- /ch8/ch8-rename-attribute-alternative/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "builder-macro", 6 | "builder-code", 7 | "builder-usage" 8 | ] 9 | -------------------------------------------------------------------------------- /ch8/ch8-rename-attribute-alternative/README.md: -------------------------------------------------------------------------------- 1 | # Rename attribute alternative 2 | 3 | ## Overview 4 | 5 | This is the code for the 'builder' macro from chapter 8, where we show an alternative way to add a rename attribute (using a 'name-value' instead of a 'list') 6 | -------------------------------------------------------------------------------- /ch8/ch8-rename-attribute-alternative/builder-code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-code" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | proc-macro2 = "1.0.69" 10 | -------------------------------------------------------------------------------- /ch8/ch8-rename-attribute-alternative/builder-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-code = { path = "../builder-code" } 8 | 9 | [lib] 10 | proc-macro = true 11 | -------------------------------------------------------------------------------- /ch8/ch8-rename-attribute-alternative/builder-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use builder_code::create_builder; 3 | 4 | #[proc_macro_derive(Builder, attributes(rename))] 5 | pub fn builder(item: TokenStream) -> TokenStream { 6 | create_builder(item.into()).into() 7 | } 8 | -------------------------------------------------------------------------------- /ch8/ch8-rename-attribute-alternative/builder-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-macro = { path = "../builder-macro" } 8 | -------------------------------------------------------------------------------- /ch8/ch8-rename-attribute-alternative/builder-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use builder_macro::Builder; 6 | 7 | #[test] 8 | fn should_generate_builder_for_struct_with_one_renamed_property() { 9 | #[derive(Builder)] 10 | struct Gleipnir { 11 | #[rename = "tops_of"] 12 | roots_of: String, 13 | } 14 | 15 | let gleipnir = Gleipnir::builder() 16 | .tops_of("mountains".to_string()) 17 | .build(); 18 | 19 | assert_eq!(gleipnir.roots_of, "mountains".to_string()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ch8/ch8-rename-attribute/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "builder-macro", 6 | "builder-code", 7 | "builder-usage" 8 | ] 9 | -------------------------------------------------------------------------------- /ch8/ch8-rename-attribute/README.md: -------------------------------------------------------------------------------- 1 | # Rename attribute 2 | 3 | ## Overview 4 | 5 | This is the code for the 'builder' macro from chapter 8, where we add a rename attribute. 6 | -------------------------------------------------------------------------------- /ch8/ch8-rename-attribute/builder-code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-code" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | proc-macro2 = "1.0.69" 10 | -------------------------------------------------------------------------------- /ch8/ch8-rename-attribute/builder-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-code = { path = "../builder-code" } 8 | 9 | [lib] 10 | proc-macro = true 11 | -------------------------------------------------------------------------------- /ch8/ch8-rename-attribute/builder-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use builder_code::create_builder; 3 | 4 | #[proc_macro_derive(Builder, attributes(rename))] 5 | pub fn builder(item: TokenStream) -> TokenStream { 6 | create_builder(item.into()).into() 7 | } 8 | -------------------------------------------------------------------------------- /ch8/ch8-rename-attribute/builder-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-macro = { path = "../builder-macro" } 8 | -------------------------------------------------------------------------------- /ch8/ch8-uppercasing-exercise/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "builder-macro", 6 | "builder-code", 7 | "builder-usage" 8 | ] 9 | -------------------------------------------------------------------------------- /ch8/ch8-uppercasing-exercise/README.md: -------------------------------------------------------------------------------- 1 | # Uppercase 2 | 3 | ## Overview 4 | 5 | This is the solution to the exercise from chapter 8 where we ask you to add an 'uppercase' attribute to our macro, to uppercase `String` properties. 6 | -------------------------------------------------------------------------------- /ch8/ch8-uppercasing-exercise/builder-code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-code" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | proc-macro2 = "1.0.69" 10 | -------------------------------------------------------------------------------- /ch8/ch8-uppercasing-exercise/builder-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-code = { path = "../builder-code" } 8 | 9 | [lib] 10 | proc-macro = true 11 | -------------------------------------------------------------------------------- /ch8/ch8-uppercasing-exercise/builder-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use builder_code::create_builder; 3 | 4 | #[proc_macro_derive(Builder, attributes(builder_defaults,rename,uppercase))] 5 | pub fn builder(item: TokenStream) -> TokenStream { 6 | create_builder(item.into()).into() 7 | } 8 | -------------------------------------------------------------------------------- /ch8/ch8-uppercasing-exercise/builder-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "builder-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | builder-macro = { path = "../builder-macro" } 8 | 9 | [dev-dependencies] 10 | trybuild = "1.0.85" 11 | -------------------------------------------------------------------------------- /ch8/ch8-uppercasing-exercise/builder-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use builder_macro::Builder; 6 | 7 | #[test] 8 | fn should_uppercase_the_attribute() { 9 | #[derive(Builder)] 10 | struct Gleipnir { 11 | #[uppercase] 12 | roots_of: String, 13 | } 14 | 15 | let gleipnir = Gleipnir::builder() 16 | .roots_of("upper".to_string()) 17 | .build(); 18 | 19 | assert_eq!(gleipnir.roots_of, "UPPER".to_string()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ch8/ch8-uppercasing-exercise/builder-usage/tests/compilation_tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn should_not_compile() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/fails/*.rs"); 5 | } -------------------------------------------------------------------------------- /ch8/ch8-uppercasing-exercise/builder-usage/tests/fails/wrong_type.rs: -------------------------------------------------------------------------------- 1 | use builder_macro::Builder; 2 | 3 | #[derive(Builder)] 4 | struct Gleipnir { 5 | #[uppercase] 6 | roots_of: i32, 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /ch8/ch8-uppercasing-exercise/builder-usage/tests/fails/wrong_type.stderr: -------------------------------------------------------------------------------- 1 | error: Can only use uppercase for String type 2 | --> tests/fails/wrong_type.rs:6:5 3 | | 4 | 6 | roots_of: i32, 5 | | ^^^^^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-better-input-modelling-exercise/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "iac-macro-usage", 6 | "iac-macro" 7 | ] 8 | -------------------------------------------------------------------------------- /ch9/ch9-better-input-modelling-exercise/README.md: -------------------------------------------------------------------------------- 1 | # Better input modelling 2 | 3 | ## Overview 4 | 5 | This is the solution to the exercise from chapter 9 where we ask you to improve the input modeling of `IacInput`. 6 | -------------------------------------------------------------------------------- /ch9/ch9-better-input-modelling-exercise/example/handler.js: -------------------------------------------------------------------------------- 1 | // you can zip this and add that to a bucket 2 | exports.handler = async (event) => { 3 | return { 4 | hello: 'world' 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /ch9/ch9-better-input-modelling-exercise/iac-macro-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iac-macro-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | iac-macro = { path = "../iac-macro" } 8 | -------------------------------------------------------------------------------- /ch9/ch9-better-input-modelling-exercise/iac-macro-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | //use iac_macro::iac; 2 | 3 | fn main() { 4 | //iac! { 5 | // lambda my_name 6 | //} 7 | //iac! { 8 | // bucket samvouniquename => lambda my_name mem 1024 time 15 9 | //} 10 | } 11 | -------------------------------------------------------------------------------- /ch9/ch9-better-input-modelling-exercise/iac-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iac-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | aws-config = "1.1.1" 8 | aws-sdk-s3 = "1.11.0" 9 | aws-sdk-lambda = "1.9.0" 10 | quote = "1.0.33" 11 | syn = { version = "2.0.39", features = ["extra-traits"]} 12 | proc-macro2 = "1.0.69" 13 | tokio = { version = "1.26.0", features = ["rt", "rt-multi-thread"] } 14 | 15 | [lib] 16 | proc-macro = true 17 | -------------------------------------------------------------------------------- /ch9/ch9-checking-resources-exercise/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "iac-macro-usage", 6 | "iac-macro" 7 | ] 8 | -------------------------------------------------------------------------------- /ch9/ch9-checking-resources-exercise/README.md: -------------------------------------------------------------------------------- 1 | # Checking resources 2 | 3 | ## Overview 4 | 5 | This is the solution to the exercise from chapter 9 where we ask you to check whether the requested resources exist before trying to create them. 6 | -------------------------------------------------------------------------------- /ch9/ch9-checking-resources-exercise/example/handler.js: -------------------------------------------------------------------------------- 1 | // you can zip this and add that to a bucket 2 | exports.handler = async (event) => { 3 | return { 4 | hello: 'world' 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /ch9/ch9-checking-resources-exercise/iac-macro-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iac-macro-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | iac-macro = { path = "../iac-macro" } 8 | -------------------------------------------------------------------------------- /ch9/ch9-checking-resources-exercise/iac-macro-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | //use iac_macro::iac; 2 | 3 | fn main() { 4 | //iac! { 5 | // lambda my_name 6 | //} 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-checking-resources-exercise/iac-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iac-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | aws-config = "1.1.1" 8 | aws-sdk-s3 = "1.11.0" 9 | aws-sdk-lambda = "1.9.0" 10 | quote = "1.0.33" 11 | syn = { version = "2.0.39", features = ["extra-traits"]} 12 | proc-macro2 = "1.0.69" 13 | tokio = { version = "1.26.0", features = ["rt", "rt-multi-thread"] } 14 | 15 | [lib] 16 | proc-macro = true 17 | -------------------------------------------------------------------------------- /ch9/ch9-start-alternative/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "iac-macro-usage", 6 | "iac-macro" 7 | ] 8 | -------------------------------------------------------------------------------- /ch9/ch9-start-alternative/README.md: -------------------------------------------------------------------------------- 1 | # Start alternative 2 | 3 | ## Overview 4 | 5 | This is the first alternative implementation of the 'iac' macro from chapter 9. 6 | 7 | In this phase, we are only parsing the input without doing anything with it. In this alternative, we use a `KeyValue` struct helper for parsing the `Lambda`. 8 | -------------------------------------------------------------------------------- /ch9/ch9-start-alternative/iac-macro-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iac-macro-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | iac-macro = { path = "../iac-macro" } 8 | 9 | [dev-dependencies] 10 | trybuild = "1.0.85" -------------------------------------------------------------------------------- /ch9/ch9-start-alternative/iac-macro-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | bucket uniquename 6 | } 7 | iac! { 8 | lambda (name = a_name) 9 | } 10 | iac! { 11 | lambda (name = my_name, mem = 1024, time = 15) 12 | } 13 | iac! { 14 | bucket uniquename => lambda (name = my_name, mem = 1024, time = 15) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ch9/ch9-start-alternative/iac-macro-usage/tests/compilation_tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn should_not_compile() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/fails/*.rs"); 5 | } 6 | -------------------------------------------------------------------------------- /ch9/ch9-start-alternative/iac-macro-usage/tests/fails/bucket_and_no_lambda_parentheses.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | bucket unique lambda 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start-alternative/iac-macro-usage/tests/fails/bucket_and_no_lambda_parentheses.stderr: -------------------------------------------------------------------------------- 1 | error: unexpected end of input, expected parentheses 2 | --> tests/fails/bucket_and_no_lambda_parentheses.rs:4:5 3 | | 4 | 4 | / iac! { 5 | 5 | | bucket unique lambda 6 | 6 | | } 7 | | |_____^ 8 | | 9 | = note: this error originates in the macro `iac` (in Nightly builds, run with -Z macro-backtrace for more info) 10 | -------------------------------------------------------------------------------- /ch9/ch9-start-alternative/iac-macro-usage/tests/fails/bucket_typo.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | bucke uniquename 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start-alternative/iac-macro-usage/tests/fails/bucket_typo.stderr: -------------------------------------------------------------------------------- 1 | error: only 'bucket' and 'lambda' resources are supported 2 | --> tests/fails/bucket_typo.rs:5:9 3 | | 4 | 5 | bucke uniquename 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-start-alternative/iac-macro-usage/tests/fails/lambda_colon_instead_of_equals.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | lambda (name :) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start-alternative/iac-macro-usage/tests/fails/lambda_colon_instead_of_equals.stderr: -------------------------------------------------------------------------------- 1 | error: prop name and value should be separated by an equals sign 2 | --> tests/fails/lambda_colon_instead_of_equals.rs:5:22 3 | | 4 | 5 | lambda (name :) 5 | | ^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-start-alternative/iac-macro-usage/tests/fails/no_bucket_name.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | bucket 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start-alternative/iac-macro-usage/tests/fails/no_bucket_name.stderr: -------------------------------------------------------------------------------- 1 | error: bucket needs a name 2 | --> tests/fails/no_bucket_name.rs:5:9 3 | | 4 | 5 | bucket 5 | | ^^^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-start-alternative/iac-macro-usage/tests/fails/no_lambda_for_event.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | bucket uniquename => 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start-alternative/iac-macro-usage/tests/fails/no_lambda_for_event.stderr: -------------------------------------------------------------------------------- 1 | error: a lambda is required for an event ('=>') 2 | --> tests/fails/no_lambda_for_event.rs:4:5 3 | | 4 | 4 | / iac! { 5 | 5 | | bucket uniquename => 6 | 6 | | } 7 | | |_____^ 8 | | 9 | = note: this error originates in the macro `iac` (in Nightly builds, run with -Z macro-backtrace for more info) 10 | -------------------------------------------------------------------------------- /ch9/ch9-start-alternative/iac-macro-usage/tests/fails/no_lambda_name.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | lambda (name =) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start-alternative/iac-macro-usage/tests/fails/no_lambda_name.stderr: -------------------------------------------------------------------------------- 1 | error: name property needs a value 2 | --> tests/fails/no_lambda_name.rs:5:23 3 | | 4 | 5 | lambda (name =) 5 | | ^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-start-alternative/iac-macro/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "hello-world-macro" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "quote", 10 | "syn", 11 | ] 12 | 13 | [[package]] 14 | name = "proc-macro2" 15 | version = "1.0.47" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" 18 | dependencies = [ 19 | "unicode-ident", 20 | ] 21 | 22 | [[package]] 23 | name = "quote" 24 | version = "1.0.21" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 27 | dependencies = [ 28 | "proc-macro2", 29 | ] 30 | 31 | [[package]] 32 | name = "syn" 33 | version = "1.0.105" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" 36 | dependencies = [ 37 | "proc-macro2", 38 | "quote", 39 | "unicode-ident", 40 | ] 41 | 42 | [[package]] 43 | name = "unicode-ident" 44 | version = "1.0.5" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 47 | -------------------------------------------------------------------------------- /ch9/ch9-start-alternative/iac-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iac-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | 10 | [lib] 11 | proc-macro = true -------------------------------------------------------------------------------- /ch9/ch9-start-another-alternative/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "iac-macro-usage", 6 | "iac-macro" 7 | ] 8 | -------------------------------------------------------------------------------- /ch9/ch9-start-another-alternative/README.md: -------------------------------------------------------------------------------- 1 | # Start another alternative 2 | 3 | ## Overview 4 | 5 | This is the second alternative implementation of the 'iac' macro from chapter 9. 6 | 7 | In this phase, we are only parsing the input without doing anything with it. In this alternative, we use a `LambdaProperty` enum helper for parsing the `Lambda`. 8 | -------------------------------------------------------------------------------- /ch9/ch9-start-another-alternative/iac-macro-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iac-macro-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | iac-macro = { path = "../iac-macro" } 8 | 9 | [dev-dependencies] 10 | trybuild = "1.0.85" 11 | -------------------------------------------------------------------------------- /ch9/ch9-start-another-alternative/iac-macro-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | bucket uniquename 6 | } 7 | iac! { 8 | lambda (name = a_name) 9 | } 10 | iac! { 11 | lambda (name = my_name, mem = 1024, time = 15) 12 | } 13 | iac! { 14 | bucket uniquename => lambda (name = my_name, mem = 1024, time = 15) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ch9/ch9-start-another-alternative/iac-macro-usage/tests/compilation_tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn should_not_compile() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/fails/*.rs"); 5 | } 6 | -------------------------------------------------------------------------------- /ch9/ch9-start-another-alternative/iac-macro-usage/tests/fails/bucket_and_no_lambda_parentheses.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | bucket unique lambda 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start-another-alternative/iac-macro-usage/tests/fails/bucket_and_no_lambda_parentheses.stderr: -------------------------------------------------------------------------------- 1 | error: unexpected end of input, expected parentheses 2 | --> tests/fails/bucket_and_no_lambda_parentheses.rs:4:5 3 | | 4 | 4 | / iac! { 5 | 5 | | bucket unique lambda 6 | 6 | | } 7 | | |_____^ 8 | | 9 | = note: this error originates in the macro `iac` (in Nightly builds, run with -Z macro-backtrace for more info) 10 | -------------------------------------------------------------------------------- /ch9/ch9-start-another-alternative/iac-macro-usage/tests/fails/bucket_typo.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | bucke uniquename 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start-another-alternative/iac-macro-usage/tests/fails/bucket_typo.stderr: -------------------------------------------------------------------------------- 1 | error: only 'bucket' and 'lambda' resources are supported 2 | --> tests/fails/bucket_typo.rs:5:9 3 | | 4 | 5 | bucke uniquename 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-start-another-alternative/iac-macro-usage/tests/fails/lambda_colon_instead_of_equals.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | lambda (name :) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start-another-alternative/iac-macro-usage/tests/fails/lambda_colon_instead_of_equals.stderr: -------------------------------------------------------------------------------- 1 | error: prop name and value should be separated by an equals sign 2 | --> tests/fails/lambda_colon_instead_of_equals.rs:5:22 3 | | 4 | 5 | lambda (name :) 5 | | ^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-start-another-alternative/iac-macro-usage/tests/fails/no_bucket_name.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | bucket 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start-another-alternative/iac-macro-usage/tests/fails/no_bucket_name.stderr: -------------------------------------------------------------------------------- 1 | error: bucket needs a name 2 | --> tests/fails/no_bucket_name.rs:5:9 3 | | 4 | 5 | bucket 5 | | ^^^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-start-another-alternative/iac-macro-usage/tests/fails/no_lambda_for_event.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | bucket uniquename => 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start-another-alternative/iac-macro-usage/tests/fails/no_lambda_for_event.stderr: -------------------------------------------------------------------------------- 1 | error: a lambda is required for an event ('=>') 2 | --> tests/fails/no_lambda_for_event.rs:4:5 3 | | 4 | 4 | / iac! { 5 | 5 | | bucket uniquename => 6 | 6 | | } 7 | | |_____^ 8 | | 9 | = note: this error originates in the macro `iac` (in Nightly builds, run with -Z macro-backtrace for more info) 10 | -------------------------------------------------------------------------------- /ch9/ch9-start-another-alternative/iac-macro-usage/tests/fails/no_lambda_name.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | lambda (name =) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start-another-alternative/iac-macro-usage/tests/fails/no_lambda_name.stderr: -------------------------------------------------------------------------------- 1 | error: name property needs a value 2 | --> tests/fails/no_lambda_name.rs:5:23 3 | | 4 | 5 | lambda (name =) 5 | | ^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-start-another-alternative/iac-macro/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "hello-world-macro" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "quote", 10 | "syn", 11 | ] 12 | 13 | [[package]] 14 | name = "proc-macro2" 15 | version = "1.0.47" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" 18 | dependencies = [ 19 | "unicode-ident", 20 | ] 21 | 22 | [[package]] 23 | name = "quote" 24 | version = "1.0.21" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 27 | dependencies = [ 28 | "proc-macro2", 29 | ] 30 | 31 | [[package]] 32 | name = "syn" 33 | version = "1.0.105" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" 36 | dependencies = [ 37 | "proc-macro2", 38 | "quote", 39 | "unicode-ident", 40 | ] 41 | 42 | [[package]] 43 | name = "unicode-ident" 44 | version = "1.0.5" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 47 | -------------------------------------------------------------------------------- /ch9/ch9-start-another-alternative/iac-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iac-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | proc-macro2 = "1.0.69" 10 | 11 | [lib] 12 | proc-macro = true -------------------------------------------------------------------------------- /ch9/ch9-start/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "iac-macro-usage", 6 | "iac-macro" 7 | ] 8 | -------------------------------------------------------------------------------- /ch9/ch9-start/README.md: -------------------------------------------------------------------------------- 1 | # Start 2 | 3 | ## Overview 4 | 5 | This is the first implementation of the 'iac' macro from chapter 9. 6 | 7 | In this phase, we are only parsing the input without doing anything with it. 8 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iac-macro-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | iac-macro = { path = "../iac-macro" } 8 | 9 | [dev-dependencies] 10 | trybuild = "1.0.85" 11 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | // won't accept '-', not valid for identifiers 5 | iac! { 6 | bucket uniquename 7 | } 8 | iac! { 9 | lambda a_name 10 | } 11 | iac! { 12 | lambda my_name mem 1024 time 15 13 | } 14 | iac! { 15 | bucket uniquename lambda anothername 16 | } 17 | iac! { 18 | lambda name bucket anothername 19 | } 20 | iac! { 21 | bucket uniquename => lambda anothername 22 | } 23 | iac! { 24 | bucket uniquename => lambda anothername mem 1024 time 15 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/compilation_tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn should_not_compile() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/fails/*.rs"); 5 | } 6 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/bucket_and_no_lambda_name.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | bucket unique lambda 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/bucket_and_no_lambda_name.stderr: -------------------------------------------------------------------------------- 1 | error: lambda needs a name 2 | --> tests/fails/bucket_and_no_lambda_name.rs:5:23 3 | | 4 | 5 | bucket unique lambda 5 | | ^^^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/bucket_typo.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | bucke uniquename 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/bucket_typo.stderr: -------------------------------------------------------------------------------- 1 | error: only 'bucket' and 'lambda' resources are supported 2 | --> tests/fails/bucket_typo.rs:5:9 3 | | 4 | 5 | bucke uniquename 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/lambda_fake_property.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | lambda name mem 10 fake 15 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/lambda_fake_property.stderr: -------------------------------------------------------------------------------- 1 | error: unknown property passed to lambda 2 | --> tests/fails/lambda_fake_property.rs:5:28 3 | | 4 | 5 | lambda name mem 10 fake 15 5 | | ^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/lambda_negative_mem.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | lambda name mem -10 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/lambda_negative_mem.stderr: -------------------------------------------------------------------------------- 1 | error: memory needs a positive value <= 10240 2 | --> tests/fails/lambda_negative_mem.rs:5:25 3 | | 4 | 5 | lambda name mem -10 5 | | ^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/lambda_negative_time.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | lambda name mem -10 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/lambda_negative_time.stderr: -------------------------------------------------------------------------------- 1 | error: memory needs a positive value <= 10240 2 | --> tests/fails/lambda_negative_time.rs:5:25 3 | | 4 | 5 | lambda name mem -10 5 | | ^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/lambda_time_not_a_number.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | lambda name mem "yes" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/lambda_time_not_a_number.stderr: -------------------------------------------------------------------------------- 1 | error: expected integer literal 2 | --> tests/fails/lambda_time_not_a_number.rs:5:25 3 | | 4 | 5 | lambda name mem "yes" 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/lambda_typo.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | lamba name 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/lambda_typo.stderr: -------------------------------------------------------------------------------- 1 | error: only 'bucket' and 'lambda' resources are supported 2 | --> tests/fails/lambda_typo.rs:5:9 3 | | 4 | 5 | lamba name 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/no_bucket_name.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | bucket 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/no_bucket_name.stderr: -------------------------------------------------------------------------------- 1 | error: bucket needs a name 2 | --> tests/fails/no_bucket_name.rs:5:9 3 | | 4 | 5 | bucket 5 | | ^^^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/no_lambda_for_event.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | bucket uniquename => 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/no_lambda_for_event.stderr: -------------------------------------------------------------------------------- 1 | error: a lambda is required for an event ('=>') 2 | --> tests/fails/no_lambda_for_event.rs:4:5 3 | | 4 | 4 | / iac! { 5 | 5 | | bucket uniquename => 6 | 6 | | } 7 | | |_____^ 8 | | 9 | = note: this error originates in the macro `iac` (in Nightly builds, run with -Z macro-backtrace for more info) 10 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/no_lambda_name.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | lambda 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro-usage/tests/fails/no_lambda_name.stderr: -------------------------------------------------------------------------------- 1 | error: lambda needs a name 2 | --> tests/fails/no_lambda_name.rs:5:9 3 | | 4 | 5 | lambda 5 | | ^^^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "hello-world-macro" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "quote", 10 | "syn", 11 | ] 12 | 13 | [[package]] 14 | name = "proc-macro2" 15 | version = "1.0.47" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" 18 | dependencies = [ 19 | "unicode-ident", 20 | ] 21 | 22 | [[package]] 23 | name = "quote" 24 | version = "1.0.21" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 27 | dependencies = [ 28 | "proc-macro2", 29 | ] 30 | 31 | [[package]] 32 | name = "syn" 33 | version = "1.0.105" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" 36 | dependencies = [ 37 | "proc-macro2", 38 | "quote", 39 | "unicode-ident", 40 | ] 41 | 42 | [[package]] 43 | name = "unicode-ident" 44 | version = "1.0.5" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 47 | -------------------------------------------------------------------------------- /ch9/ch9-start/iac-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iac-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | 10 | [lib] 11 | proc-macro = true 12 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "iac-macro-usage", 6 | "iac-macro" 7 | ] 8 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/README.md: -------------------------------------------------------------------------------- 1 | # Suggestions 2 | 3 | ## Overview 4 | 5 | This is the solution to the exercise from chapter 9 where we ask you to suggest the right resource in case of a type (using, in this case, Levenshtein distance). 6 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iac-macro-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | iac-macro = { path = "../iac-macro" } 8 | 9 | [dev-dependencies] 10 | trybuild = "1.0.85" 11 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | bucket uniquename 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/compilation_tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn should_not_compile() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/fails/*.rs"); 5 | } 6 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/another_bucket_typo.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | buck uniquename 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/another_bucket_typo.stderr: -------------------------------------------------------------------------------- 1 | error: only 'bucket' and 'lambda' resources are supported. Is this a typo for bucket? 2 | --> tests/fails/another_bucket_typo.rs:5:9 3 | | 4 | 5 | buck uniquename 5 | | ^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/bucket_and_no_lambda_name.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | bucket unique lambda 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/bucket_and_no_lambda_name.stderr: -------------------------------------------------------------------------------- 1 | error: lambda needs a name 2 | --> tests/fails/bucket_and_no_lambda_name.rs:5:23 3 | | 4 | 5 | bucket unique lambda 5 | | ^^^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/bucket_typo.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | bucke uniquename 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/bucket_typo.stderr: -------------------------------------------------------------------------------- 1 | error: only 'bucket' and 'lambda' resources are supported. Is this a typo for bucket? 2 | --> tests/fails/bucket_typo.rs:5:9 3 | | 4 | 5 | bucke uniquename 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/lambda_another_typo.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | lamba name 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/lambda_another_typo.stderr: -------------------------------------------------------------------------------- 1 | error: only 'bucket' and 'lambda' resources are supported. Is this a typo for lambda? 2 | --> tests/fails/lambda_another_typo.rs:5:9 3 | | 4 | 5 | lamba name 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/lambda_fake_property.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | lambda name mem 10 fake 15 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/lambda_fake_property.stderr: -------------------------------------------------------------------------------- 1 | error: unknown property passed to lambda 2 | --> tests/fails/lambda_fake_property.rs:5:28 3 | | 4 | 5 | lambda name mem 10 fake 15 5 | | ^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/lambda_negative_mem.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | lambda name mem -10 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/lambda_negative_mem.stderr: -------------------------------------------------------------------------------- 1 | error: memory needs a positive value <= 10240 2 | --> tests/fails/lambda_negative_mem.rs:5:25 3 | | 4 | 5 | lambda name mem -10 5 | | ^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/lambda_negative_time.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | lambda name mem -10 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/lambda_negative_time.stderr: -------------------------------------------------------------------------------- 1 | error: memory needs a positive value <= 10240 2 | --> tests/fails/lambda_negative_time.rs:5:25 3 | | 4 | 5 | lambda name mem -10 5 | | ^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/lambda_time_not_a_number.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | lambda name mem "yes" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/lambda_time_not_a_number.stderr: -------------------------------------------------------------------------------- 1 | error: expected integer literal 2 | --> tests/fails/lambda_time_not_a_number.rs:5:25 3 | | 4 | 5 | lambda name mem "yes" 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/lambda_typo.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | lamba name 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/lambda_typo.stderr: -------------------------------------------------------------------------------- 1 | error: only 'bucket' and 'lambda' resources are supported. Is this a typo for lambda? 2 | --> tests/fails/lambda_typo.rs:5:9 3 | | 4 | 5 | lamba name 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/no_bucket_name.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | bucket 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/no_bucket_name.stderr: -------------------------------------------------------------------------------- 1 | error: bucket needs a name 2 | --> tests/fails/no_bucket_name.rs:5:9 3 | | 4 | 5 | bucket 5 | | ^^^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/no_lambda_for_event.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | bucket uniquename => 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/no_lambda_for_event.stderr: -------------------------------------------------------------------------------- 1 | error: a lambda is required for an event ('=>') 2 | --> tests/fails/no_lambda_for_event.rs:4:5 3 | | 4 | 4 | / iac! { 5 | 5 | | bucket uniquename => 6 | 6 | | } 7 | | |_____^ 8 | | 9 | = note: this error originates in the macro `iac` (in Nightly builds, run with -Z macro-backtrace for more info) 10 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/no_lambda_name.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | lambda 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/no_lambda_name.stderr: -------------------------------------------------------------------------------- 1 | error: lambda needs a name 2 | --> tests/fails/no_lambda_name.rs:5:9 3 | | 4 | 5 | lambda 5 | | ^^^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/really_unknown_property.rs: -------------------------------------------------------------------------------- 1 | use iac_macro::iac; 2 | 3 | fn main() { 4 | iac! { 5 | somethingtotallyunknown name 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro-usage/tests/fails/really_unknown_property.stderr: -------------------------------------------------------------------------------- 1 | error: only 'bucket' and 'lambda' resources are supported 2 | --> tests/fails/really_unknown_property.rs:5:9 3 | | 4 | 5 | somethingtotallyunknown name 5 | | ^^^^^^^^^^^^^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "hello-world-macro" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "quote", 10 | "syn", 11 | ] 12 | 13 | [[package]] 14 | name = "proc-macro2" 15 | version = "1.0.47" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" 18 | dependencies = [ 19 | "unicode-ident", 20 | ] 21 | 22 | [[package]] 23 | name = "quote" 24 | version = "1.0.21" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 27 | dependencies = [ 28 | "proc-macro2", 29 | ] 30 | 31 | [[package]] 32 | name = "syn" 33 | version = "1.0.105" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" 36 | dependencies = [ 37 | "proc-macro2", 38 | "quote", 39 | "unicode-ident", 40 | ] 41 | 42 | [[package]] 43 | name = "unicode-ident" 44 | version = "1.0.5" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 47 | -------------------------------------------------------------------------------- /ch9/ch9-suggestions-exercise/iac-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iac-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | quote = "1.0.33" 8 | syn = { version = "2.0.39", features = ["extra-traits"]} 9 | edit-distance = "2.1.0" 10 | 11 | [lib] 12 | proc-macro = true 13 | -------------------------------------------------------------------------------- /ch9/ch9-with-sdk-calls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "iac-macro-usage", 6 | "iac-macro" 7 | ] 8 | -------------------------------------------------------------------------------- /ch9/ch9-with-sdk-calls/README.md: -------------------------------------------------------------------------------- 1 | # With SDK Calls 2 | 3 | ## Overview 4 | 5 | This is the final implementation of the 'iac' macro from chapter 9. 6 | 7 | We now actually call the AWS SDK to create the requested resources. We use the original parsing, not the alternatives. -------------------------------------------------------------------------------- /ch9/ch9-with-sdk-calls/example/handler.js: -------------------------------------------------------------------------------- 1 | // you can zip this and add that to a bucket 2 | exports.handler = async (event) => { 3 | return { 4 | hello: 'world' 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /ch9/ch9-with-sdk-calls/iac-macro-usage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iac-macro-usage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | iac-macro = { path = "../iac-macro" } 8 | -------------------------------------------------------------------------------- /ch9/ch9-with-sdk-calls/iac-macro-usage/src/main.rs: -------------------------------------------------------------------------------- 1 | // use iac_macro::iac; 2 | 3 | fn main() { 4 | //iac! { 5 | // bucket nameshouldbeunique 6 | //} 7 | //iac! { 8 | // lambda my_name 9 | //} 10 | //iac! { 11 | // lambda my_name mem 1024 time 15 12 | //} 13 | //iac! { 14 | // bucket nameshouldbeunique lambda anothername 15 | //} 16 | // iac! { 17 | // bucket nameshouldbeunique => lambda anothername 18 | // } 19 | //iac! { 20 | // bucket nameshouldbeunique => lambda anothername mem 1024 time 15 21 | //} 22 | } 23 | -------------------------------------------------------------------------------- /ch9/ch9-with-sdk-calls/iac-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iac-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | aws-config = "1.1.1" 8 | aws-sdk-s3 = "1.11.0" 9 | aws-sdk-lambda = "1.9.0" 10 | quote = "1.0.33" 11 | syn = { version = "2.0.39", features = ["extra-traits"]} 12 | proc-macro2 = "1.0.69" 13 | tokio = { version = "1.26.0", features = ["rt", "rt-multi-thread"] } 14 | 15 | [lib] 16 | proc-macro = true 17 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.75.0" 3 | components = [ "rustfmt", "clippy" ] 4 | --------------------------------------------------------------------------------