You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lib.rs 3.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. use rand::seq::SliceRandom;
  2. use sauron::html::attributes::{class, id};
  3. use sauron::html::events::on_click;
  4. use sauron::html::{div, text};
  5. use sauron::prelude::*;
  6. use sauron::{Cmd, Component, Node, Program};
  7. mod tlds;
  8. pub enum Msg {
  9. Click,
  10. ReceivedWords(Result<Vec<String>, JsValue>),
  11. }
  12. pub struct App {
  13. output: Option<String>,
  14. words: Vec<String>,
  15. }
  16. impl App {
  17. pub fn new() -> Self {
  18. App {
  19. output: None,
  20. words: vec![],
  21. }
  22. }
  23. fn generate_domain(&mut self) -> String {
  24. let mut domain: String = String::from("");
  25. while domain.is_empty() {
  26. let tld = self.get_random_tld();
  27. tld.map(|tld| {
  28. let word = self.get_random_word(&tld);
  29. if word.is_some() {
  30. let mut word = word.unwrap();
  31. word.truncate(word.len() - 2);
  32. domain = format!("{}.{}", word, tld);
  33. }
  34. });
  35. }
  36. domain
  37. }
  38. fn get_random_tld(&mut self) -> Option<String> {
  39. crate::tlds::tlds()
  40. .choose(&mut rand::thread_rng())
  41. .map(|v| String::from(v))
  42. }
  43. fn get_random_word(&self, tld: &str) -> Option<String> {
  44. self.words
  45. .clone()
  46. .into_iter()
  47. .filter(|word| word.ends_with(tld))
  48. .collect::<Vec<String>>()
  49. .choose(&mut rand::thread_rng())
  50. .map(|v| v.to_string().to_lowercase())
  51. }
  52. }
  53. impl Component<Msg> for App {
  54. fn init(&self) -> Cmd<Self, Msg> {
  55. Http::fetch_with_text_response_decoder(
  56. "/words.txt",
  57. |r: String| r.lines().map(|l| l.to_string()).collect(),
  58. Msg::ReceivedWords,
  59. )
  60. }
  61. fn view(&self) -> Node<Msg> {
  62. let output = match &self.output {
  63. Some(s) => vec![text(s)],
  64. None => vec![],
  65. };
  66. let loading = self.words.len() == 0;
  67. sauron::html::main(
  68. vec![],
  69. vec![div(
  70. vec![class("container")],
  71. vec![
  72. h1(vec![class("heading")], vec![text("Domain Hack Generator")]),
  73. div(
  74. vec![class("button-container")],
  75. vec![
  76. button(
  77. vec![
  78. class("button"),
  79. disabled(loading),
  80. on_click(|_: MouseEvent| Msg::Click),
  81. ],
  82. vec![text(if loading { "Loading..." } else { "Generate" })],
  83. ),
  84. p(
  85. vec![
  86. id("result"),
  87. class(if output.is_empty() { "empty" } else { "" }),
  88. ],
  89. output,
  90. ),
  91. ],
  92. ),
  93. ],
  94. )],
  95. )
  96. }
  97. fn update(&mut self, msg: Msg) -> Cmd<Self, Msg> {
  98. match msg {
  99. Msg::Click => self.output = Some(self.generate_domain()),
  100. Msg::ReceivedWords(result) => match result {
  101. Ok(words) => self.words = words,
  102. Err(_) => {}
  103. },
  104. }
  105. Cmd::none()
  106. }
  107. }
  108. #[wasm_bindgen(start)]
  109. pub fn main() {
  110. console_log::init_with_level(log::Level::Trace).unwrap();
  111. console_error_panic_hook::set_once();
  112. Program::mount_to_body(App::new());
  113. }