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.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  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(vec![id("result")], output),
  85. ],
  86. ),
  87. ],
  88. )],
  89. )
  90. }
  91. fn update(&mut self, msg: Msg) -> Cmd<Self, Msg> {
  92. match msg {
  93. Msg::Click => self.output = Some(self.generate_domain()),
  94. Msg::ReceivedWords(result) => match result {
  95. Ok(words) => self.words = words,
  96. Err(_) => {}
  97. },
  98. }
  99. Cmd::none()
  100. }
  101. }
  102. #[wasm_bindgen(start)]
  103. pub fn main() {
  104. console_log::init_with_level(log::Level::Trace).unwrap();
  105. console_error_panic_hook::set_once();
  106. Program::mount_to_body(App::new());
  107. }