cli.rs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. use clap::{ArgAction, Parser};
  2. use crate::{MissingTilePolicy, TileSelectionPolicy, VacancyPolicy};
  3. /// Program to generate a larger image made up of different image tiles of the
  4. /// same size
  5. #[cfg_attr(test, derive(Clone, Debug))]
  6. #[derive(Parser)]
  7. #[command(author, version, about)]
  8. #[deny(missing_docs)]
  9. pub struct Arguments {
  10. /// Number of rows
  11. pub rows: u16,
  12. /// Number of columns
  13. pub cols: u16,
  14. /// Optional connector name that tiles must exhibit towards the border of
  15. /// the image; if this option is omitted, bordering tiles may have any
  16. /// connection towards the border.
  17. #[arg(short, long, value_name = "CONNECTOR_NAME")]
  18. pub border: Option<String>,
  19. /// Optional path to a tile configuration file; overrides any configuration
  20. /// file in the tile directory.
  21. #[arg(short, long, group = "config_arg_group", value_name = "PATH")]
  22. pub config: Option<String>,
  23. /// Optional path to a tile directory; if omitted, assume the current
  24. /// working directory.
  25. ///
  26. /// The directory may contain a tile configuration file named “config.yaml”.
  27. #[arg(short = 'd', long, value_name = "PATH")]
  28. pub tile_directory: Option<String>,
  29. #[rustfmt::skip]
  30. /// Optional policy name that specifies how to behave in case there is no
  31. /// tile with a required connectors list.
  32. ///
  33. /// MISSING_TILE_POLICY may have one of the following values:
  34. ///
  35. /// avoid:{n}
  36. /// Try to avoid leaving empty positions in the tile grid. This may
  37. /// take a considerable amount of time and memory to finish due to
  38. /// backtracking. The run may terminate unsuccessfully in case there
  39. /// is no solution.
  40. ///
  41. /// exit:{n}
  42. /// Exit immediately if there is no fitting tile; this may be useful
  43. /// for debugging tile configurations.
  44. #[arg(short, long, default_value_t = MissingTilePolicy::Avoid)]
  45. pub missing_tile_policy: MissingTilePolicy,
  46. /// Do not use any user defined tile configuration; Instead, assume all
  47. /// tiles have the connectors
  48. /// { north: “c”, east: “c”, south: “c”, west: “c” } and a weight of 1.
  49. // TODO: Implement error message when no configuration is passed and no_config is false.
  50. #[arg(short, long, group = "config_arg_group", action = ArgAction::SetTrue)]
  51. pub no_config: bool,
  52. /// Path to the output file.
  53. #[arg(short, long, value_name = "PATH", default_value = "output/tiled")]
  54. pub output: String,
  55. #[rustfmt::skip]
  56. /// Optional policy name that specifies how to handle vacant areas.
  57. ///
  58. /// VACANCY_POLICY may have one of the following values:
  59. ///
  60. /// avoid:{n}
  61. /// Try to avoid leaving vacant areas in the tile grid. This may take a
  62. /// considerable amount of time and memory to finish due to backtracking.
  63. /// The run may terminate unsuccessfully in case there is no solution.
  64. ///
  65. /// avoid-strict:{n}
  66. /// Same as avoid, but searches the entire problem space for a valid
  67. /// solution, if necessary, which might be much slower.
  68. ///
  69. /// ignore:{n}
  70. /// Ignore and vacant areas in the tile grid untouched.
  71. #[arg(short, long, default_value_t = VacancyPolicy::Avoid)]
  72. pub vacancy_policy: VacancyPolicy,
  73. #[rustfmt::skip]
  74. /// Optional policy name that specifies how to select tiles in case multiple
  75. /// fit into a slot.
  76. ///
  77. /// TILE_SELECTION_POLICY may have one of the following values:
  78. ///
  79. /// random:{n}
  80. /// Select a fitting tile at random using the provided weights. The tile
  81. /// weights need to be non-negative and their sum must not be zero.
  82. ///
  83. /// priority:{n}
  84. /// Select the tile with the greatest weight that fits.
  85. #[arg(short = 's', long, default_value_t = TileSelectionPolicy::Random)]
  86. pub tile_selection_policy: TileSelectionPolicy,
  87. }
  88. #[cfg(test)]
  89. mod tests {
  90. use super::*;
  91. use crate::test_utils::{
  92. RANDOM_BOOL,
  93. RANDOM_MISSING_TILE_POLICY,
  94. RANDOM_STRINGS,
  95. RANDOM_TILE_SELECTION_POLICY,
  96. RANDOM_USIZES,
  97. RANDOM_VACANCY_POLICY,
  98. };
  99. #[test]
  100. fn test_initialization() {
  101. let arguments = Arguments {
  102. rows: RANDOM_USIZES[0] as u16 + 1,
  103. cols: RANDOM_USIZES[1] as u16 + 1,
  104. config: Some(RANDOM_STRINGS[0].clone()),
  105. border: Some(RANDOM_STRINGS[1].clone()),
  106. missing_tile_policy: RANDOM_MISSING_TILE_POLICY.clone(),
  107. no_config: *RANDOM_BOOL,
  108. output: RANDOM_STRINGS[2].clone(),
  109. tile_directory: Some(RANDOM_STRINGS[3].clone()),
  110. vacancy_policy: RANDOM_VACANCY_POLICY.clone(),
  111. tile_selection_policy: RANDOM_TILE_SELECTION_POLICY.clone(),
  112. };
  113. assert_eq!(RANDOM_USIZES[0] as u16 + 1, arguments.rows);
  114. assert_eq!(RANDOM_USIZES[1] as u16 + 1, arguments.cols);
  115. assert_eq!(Some(RANDOM_STRINGS[0].clone()), arguments.config);
  116. assert_eq!(Some(RANDOM_STRINGS[1].clone()), arguments.border);
  117. assert_eq!(
  118. arguments.missing_tile_policy,
  119. RANDOM_MISSING_TILE_POLICY.clone()
  120. );
  121. assert_eq!(arguments.no_config, *RANDOM_BOOL);
  122. assert_eq!(arguments.output, RANDOM_STRINGS[2].clone());
  123. assert_eq!(arguments.tile_directory, Some(RANDOM_STRINGS[3].clone()));
  124. assert_eq!(arguments.vacancy_policy, RANDOM_VACANCY_POLICY.clone());
  125. assert_eq!(
  126. arguments.tile_selection_policy,
  127. RANDOM_TILE_SELECTION_POLICY.clone()
  128. );
  129. }
  130. }