1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2024-11-28 18:11:07 +02:00

pt-BR: Some updates and fixes (#994)

pt-BR: Some updates and fixes to the
Brazilian Portuguese translation.
This commit is contained in:
Henri Fontana 2023-07-18 03:10:24 -07:00 committed by GitHub
parent 2f86a259b6
commit b0380e1f0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Comprehensive Rust 🦀\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: 2023-07-13 16:10-0700\n"
"PO-Revision-Date: 2023-07-17 12:30-0700\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: pt_BR\n"
@ -141,7 +141,7 @@ msgstr "Conversões Implícitas"
#: src/SUMMARY.md:39
msgid "Arrays and for Loops"
msgstr "Vetores e Laços For"
msgstr "Matrizes e Loops for"
#: src/SUMMARY.md:41
msgid "Day 1: Afternoon"
@ -185,11 +185,11 @@ msgstr "Gerenciamento de Memória Baseado em Escopo"
#: src/SUMMARY.md:52
msgid "Garbage Collection"
msgstr "Garbage Collection (Coletor de lixo)"
msgstr "Gerenciamento Automático de Memória"
#: src/SUMMARY.md:53
msgid "Rust Memory Management"
msgstr "Gerenciamento de Memória do Rust"
msgstr "Gerenciamento de Memória no Rust"
#: src/SUMMARY.md:54
msgid "Comparison"
@ -201,7 +201,7 @@ msgstr "Ownership"
#: src/SUMMARY.md:56
msgid "Move Semantics"
msgstr "Semântica do move (mover)"
msgstr "Semântica do Move (mover)"
#: src/SUMMARY.md:57
msgid "Moved Strings in Rust"
@ -257,7 +257,7 @@ msgstr "Structs"
#: src/SUMMARY.md:77
msgid "Tuple Structs"
msgstr "Structs como Tuplas"
msgstr "Estruturas de Tuplas (Tuple Structs)"
#: src/SUMMARY.md:78
msgid "Field Shorthand Syntax"
@ -301,7 +301,7 @@ msgstr "Desestruturando Matrizes"
#: src/SUMMARY.md:89
msgid "Match Guards"
msgstr "Guardas de Correspondência (match)"
msgstr "Guardas de Correspondência (Match Guards)"
#: src/SUMMARY.md:91
msgid "Health Statistics"
@ -3413,7 +3413,7 @@ msgid ""
"* We create a slice by borrowing `a` and specifying the starting and ending "
"indexes in brackets.\n"
"\n"
"* If the slice starts at index 0, Rust's range syntax allows us to drop the "
"* If the slice starts at index 0, Rusts range syntax allows us to drop the "
"starting index, meaning that `&a[0..a.len()]` and `&a[..a.len()]` are "
"identical.\n"
" \n"
@ -3490,10 +3490,10 @@ msgid ""
msgstr ""
"```rust,editable\n"
"fn main() {\n"
" let s1: &str = \"World\";\n"
" let s1: &str = \"Mundo\";\n"
" println!(\"s1: {s1}\");\n"
"\n"
" let mut s2: String = String::from(\"Hello \");\n"
" let mut s2: String = String::from(\"Olá \");\n"
" println!(\"s2: {s2}\");\n"
" s2.push_str(s1);\n"
" println!(\"s2: {s2}\");\n"
@ -3615,6 +3615,33 @@ msgid ""
"}\n"
"```"
msgstr ""
"```rust,editable\n"
"fn main() {\n"
" imprimir_fizzbuzz_para(20);\n"
"}\n"
"\n"
"fn eh_divisivel(n: u32, divisor: u32) -> bool {\n"
" if divisor == 0 {\n"
" return false;\n"
" }\n"
" n % divisor == 0\n"
"}\n"
"\n"
"fn fizzbuzz(n: u32) -> String {\n"
" let fizz = if eh_divisivel(n, 3) { \"fizz\" } else { \"\" };\n"
" let buzz = if eh_divisivel(n, 5) { \"buzz\" } else { \"\" };\n"
" if fizz.is_empty() && buzz.is_empty() {\n"
" return format!(\"{n}\");\n"
" }\n"
" format!(\"{fizz}{buzz}\")\n"
"}\n"
"\n"
"fn imprimir_fizzbuzz_para(n: u32) {\n"
" for i in 1..=n {\n"
" println!(\"{}\", fizzbuzz(i));\n"
" }\n"
"}\n"
"```"
#: src/basic-syntax/functions.md:35
msgid ""
@ -3638,8 +3665,8 @@ msgstr ""
"* Algumas funções não têm valor de retorno e retornam o 'tipo unitário', "
"`()`. O compilador irá inferir isso se o tipo de retorno `-> ()` for "
"omitido.\n"
"* A expressão de intervalo no loop `for` em `fizzbuzz_to()` contém `=n`, o "
"que faz com que inclua o limite superior."
"* A expressão de intervalo no loop `for` em `imprimir_fizzbuzz_para()` "
"contém `=n`, o que faz com que inclua o limite superior."
#: src/basic-syntax/rustdoc.md:1
msgid "# Rustdoc"
@ -3672,7 +3699,7 @@ msgstr ""
"/// Determine se o primeiro argumento é divisível pelo segundo argumento.\n"
"///\n"
"/// Se o segundo argumento for zero, o resultado é falso.\n"
"fn is_divisible_by(lhs: u32, rhs: u32) -> bool {\n"
"fn divisivel_por(lhs: u32, rhs: u32) -> bool {\n"
" if rhs == 0 {\n"
" return false; // Caso excepcional, retorne antes\n"
" }\n"
@ -3895,7 +3922,7 @@ msgid ""
msgstr ""
"* Conversões implícitas entre tipos.\n"
"\n"
"* Matrizes (_Arrays_) e laços (loops) `for`."
"* Matrizes (_Arrays_) e _loops_ (laços) `for`."
#: src/exercises/day-1/morning.md:11
msgid "A few things to consider while solving the exercises:"
@ -4055,7 +4082,7 @@ msgstr ""
#: src/exercises/day-1/for-loops.md:1
msgid "# Arrays and `for` Loops"
msgstr "# Matrizes (Arrays) e Laços (Loops) `for`"
msgstr "# Matrizes (_Arrays_) e _Loops_ (Laços) `for`"
#: src/exercises/day-1/for-loops.md:3
msgid "We saw that an array can be declared like this:"
@ -4429,7 +4456,8 @@ msgid ""
"Globally-scoped names for values can be given with static variables and "
"constant definitions."
msgstr ""
"Nomes com escopo global podem ser dados com variáveis estáticas e constantes."
"Nomes com escopo global podem ser obtidos por meio de variáveis estáticas e "
"definições de constantes."
#: src/basic-syntax/static-and-const.md:5
msgid "## `const`"
@ -4534,6 +4562,10 @@ msgid ""
"Because `static` variables are accessible from any thread, mutable static "
"variables require manual, unsafe, synchronization of accesses."
msgstr ""
"Nós iremos ver [dados estáticos mutáveis](../unsafe/mutable-static-variables."
"md) no capítulo sobre Rust _inseguro_.\n"
"Variáveis estáticas mutáveis são acessíveis por qualquer _thread_ e por isso "
"necessitam de sincronização de acessos manual e insegura (_unsafe_)."
#: src/basic-syntax/static-and-const.md:50
msgid ""
@ -4836,8 +4868,8 @@ msgid ""
"If not done with care, this can lead to crashes, bugs, security "
"vulnerabilities, and memory leaks."
msgstr ""
"Se não for feito com cuidado, isso pode levar a travamentos, bugs, "
"vulnerabilidades de segurança e vazamentos de memória."
"Se isto não for feito com cuidado, travamentos, bugs, vulnerabilidades de "
"segurança e vazamentos de memória podem ocorrer."
#: src/memory-management/manual.md:7
msgid "## C Example"
@ -5035,8 +5067,7 @@ msgstr ""
#: src/memory-management/rust.md:10
msgid "Rust achieves this by modeling _ownership_ explicitly."
msgstr ""
"O Rust consegue isso modelando a propriedade (_ownership_) explicitamente."
msgstr "O Rust consegue isso modelando o _ownership_ (posse) explicitamente."
#: src/memory-management/rust.md:14
msgid ""
@ -5185,7 +5216,7 @@ msgstr "# Semântica do `Move` (Mover)"
#: src/ownership/move-semantics.md:3
msgid "An assignment will transfer ownership between variables:"
msgstr "Uma atribuição transferirá a _ownership_ entre variáveis:"
msgstr "Uma atribuição transferirá o _ownership_ entre variáveis:"
#: src/ownership/move-semantics.md:5
msgid ""
@ -5215,7 +5246,7 @@ msgid ""
"* When `s2` goes out of scope, the string data is freed.\n"
"* There is always _exactly_ one variable binding which owns a value."
msgstr ""
"* A atribuição de `s1` a `s2` transfere a _ownership_.\n"
"* A atribuição de `s1` a `s2` transfere o _ownership_.\n"
"* Os dados foram _movidos_ de `s1` e `s1` não está mais acessível.\n"
"* Quando `s1` sai do escopo, nada acontece: ele não tem _ownership_.\n"
"* Quando `s2` sai do escopo, os dados da string são liberados.\n"
@ -5564,8 +5595,8 @@ msgid ""
"struct Point(i32, i32);\n"
"\n"
"fn main() {\n"
" let p2 = p1;\n"
" let p1 = Point(3, 4);\n"
" let p2 = p1;\n"
" println!(\"p1: {p1:?}\");\n"
" println!(\"p2: {p2:?}\");\n"
"}\n"
@ -6643,7 +6674,7 @@ msgstr ""
#: src/structs.md:1
msgid "# Structs"
msgstr "# Estruturas (Structs)"
msgstr "# _Structs_ (Estruturas)"
#: src/structs.md:3
msgid "Like C and C++, Rust has support for custom structs:"
@ -6742,7 +6773,7 @@ msgstr ""
#: src/structs/tuple-structs.md:1
msgid "# Tuple Structs"
msgstr "# Estruturas Tupla (Tuple Structs)"
msgstr "# Estruturas de Tuplas (_Tuple Structs_)"
#: src/structs/tuple-structs.md:3
msgid "If the field names are unimportant, you can use a tuple struct:"
@ -6802,7 +6833,7 @@ msgstr ""
"struct Newtons(f64);\n"
"\n"
"fn calcular_forca_nas_turbinas() -> LibrasDeForca {\n"
" todo!(“Pergunte para um cientista de foguetes da NASA”)\n"
" todo!(\"Pergunte para um cientista de foguetes da NASA\")\n"
"}\n"
"\n"
"fn definir_forca_nas_turbinas(force: Newtons) {\n"
@ -6826,20 +6857,20 @@ msgid ""
"`OddNumber(u32)`.\n"
"* Demonstrate how to add a `f64` value to a `Newtons` type by accessing the "
"single field in the newtype.\n"
" * Rust generally doesn't like inexplicit things, like automatic "
" * Rust generally doesnt like inexplicit things, like automatic "
"unwrapping or for instance using booleans as integers.\n"
" * Operator overloading is discussed on Day 3 (generics).\n"
"* The example is a subtle reference to the [Mars Climate Orbiter](https://en."
"wikipedia.org/wiki/Mars_Climate_Orbiter) failure."
msgstr ""
"_Newtypes_ são uma ótima maneira de codificar informações adicionais sobre o "
"valor em um tipo primitivo, por exemplo:\n"
" * O número é medido em alguma unidade: `Newtons` no exemplo acima.\n"
"* _Newtypes_ são uma ótima maneira de codificar informações adicionais sobre "
"o valor em um tipo primitivo, por exemplo:\n"
" * O número é medido em algumas unidades: `Newtons` no exemplo acima.\n"
" * O valor passou por alguma validação quando foi criado, então não é "
"preciso validá-lo novamente a cada uso: `NumeroTelefone(String)` ou "
"`NumeroImpar(u32)`.\n"
"* Demonstre como somar um valor `f64` em um valor do tipo `Newtons` "
"acessando o campo único do _newtype_.\n"
"acessando o campo único no _newtype_.\n"
" * Geralmente, Rust não gosta de coisas implícitas, como _unwrapping_ "
"automático ou, por exemplo, usar booleanos como inteiros.\n"
" * Sobrecarga de operadores é discutido no Dia 3 (_generics_).\n"
@ -6992,10 +7023,10 @@ msgstr ""
" ```\n"
"\n"
"* Métodos são definidos no bloco `impl`.\n"
"* Use struct update syntax to define a new structure using `peter`. Note "
"that the variable `peter` will no longer be accessible afterwards.\n"
"* Utilize `{:#?}` para imprimir _structs_ para utilizar a representação "
"`Debug` (de Depuração)."
"* Use a sintaxe de atualização de estruturas para definir uma nova `struct` "
"usando `pedro`. Note que a variável `pedro` não será mais acessível após.\n"
"* Utilize `{:#?}` para imprimir _structs_ utilizando a representação de "
"depuração (`Debug`)."
#: src/enums.md:1
msgid "# Enums"
@ -7007,7 +7038,7 @@ msgid ""
"different variants:"
msgstr ""
"A palavra-chave `enum` permite a criação de um tipo que possui algumas\n"
"variações diferentes:"
"variantes diferentes:"
#: src/enums.md:6
msgid ""
@ -7215,7 +7246,7 @@ msgid ""
"Rust enums are packed tightly, taking constraints due to alignment into "
"account:"
msgstr ""
"Enums, em Rust, são empacotados firmemente, levando em consideração as "
"Enums, em Rust, são agrupados de maneira compacta, levando em consideração "
"restrições devido ao alinhamento:"
#: src/enums/sizes.md:5
@ -8277,7 +8308,7 @@ msgstr ""
#: src/exercises/day-2/health-statistics.md:1
msgid "# Health Statistics"
msgstr "# Estatísticas de saúde"
msgstr "# Estatísticas de Saúde"
#: src/exercises/day-2/health-statistics.md:3
msgid ""
@ -8312,7 +8343,6 @@ msgstr ""
"que estão faltando:"
#: src/exercises/day-2/health-statistics.md:13
#, fuzzy
msgid ""
"```rust,should_panic\n"
"// TODO: remove this when you're done with your implementation.\n"
@ -8422,6 +8452,20 @@ msgstr ""
" nome: String,\n"
" idade: u32,\n"
" peso: f32,\n"
" num_visitas: usize,\n"
" ult_pressao_sang: Option<(u32, u32)>,\n"
"}\n"
"\n"
"pub struct Medicoes {\n"
" altura: f32,\n"
" pressao_sangue: (u32, u32),\n"
"}\n"
"\n"
"pub struct RelatorioSaude<'a> {\n"
" nome_paciente: &'a str,\n"
" num_visitas: u32,\n"
" dif_altura: f32,\n"
" dif_pressao_sangue: Option<(i32, i32)>,\n"
"}\n"
"\n"
"impl Usuario {\n"
@ -8437,7 +8481,11 @@ msgstr ""
" unimplemented!()\n"
" }\n"
"\n"
" pub fn peso(&self) -> f32 {\n"
" pub fn altura(&self) -> f32 {\n"
" unimplemented!()\n"
" }\n"
"\n"
" pub fn visitas_medico(&self) -> u32 {\n"
" unimplemented!()\n"
" }\n"
"\n"
@ -8445,7 +8493,7 @@ msgstr ""
" unimplemented!()\n"
" }\n"
"\n"
" pub fn definir_peso(&mut self, novo_peso: f32) {\n"
" pub fn definir_altura(&mut self, novo_altura: f32) {\n"
" unimplemented!()\n"
" }\n"
"}\n"
@ -8456,13 +8504,13 @@ msgstr ""
"}\n"
"\n"
"#[test]\n"
"fn test_peso() {\n"
"fn test_altura() {\n"
" let beto = Usuario::new(String::from(\"Beto\"), 32, 155.2);\n"
" assert_eq!(beto.peso(), 155.2);\n"
" assert_eq!(beto.altura(), 155.2);\n"
"}\n"
"\n"
"#[test]\n"
"fn test_set_age() {\n"
"fn test_definir_idade() {\n"
" let mut beto = Usuario::new(String::from(\"Beto\"), 32, 155.2);\n"
" assert_eq!(beto.idade(), 32);\n"
" beto.definir_idade(33);\n"
@ -8472,7 +8520,7 @@ msgstr ""
#: src/exercises/day-2/points-polygons.md:1
msgid "# Polygon Struct"
msgstr "# Estrutura para polígono"
msgstr "# _Struct_ para Polígono"
#: src/exercises/day-2/points-polygons.md:3
msgid ""
@ -8482,7 +8530,7 @@ msgid ""
"the\n"
"tests pass:"
msgstr ""
"Vamos criar uma estrutura `Poligono` que contém alguns `Pontos`. Copie o "
"Vamos criar um _struct_ `Poligono` que contém alguns `Pontos`. Copie o "
"código abaixo\n"
"em <https://play.rust-lang.org/> e preencha os métodos que faltam para fazer "
"os\n"
@ -10701,7 +10749,7 @@ msgid ""
"\n"
" ```rust,ignore\n"
" #[path = \"some/path.rs\"]\n"
" mod some_module { }\n"
" mod some_module;\n"
" ```\n"
"\n"
" This is useful, for example, if you would like to place tests for a module "
@ -11065,7 +11113,6 @@ msgid ""
msgstr ""
"Rust oferece suporte a tipos genéricos, que permitem algoritmos ou "
"estruturas de dados\n"
" (como ordenação ou árvore binária)\n"
"abstrair os tipos de dados usados ou armazenados."
@ -11515,7 +11562,6 @@ msgstr ""
" '- - - - - - - - - - - - - - - - - - - - - "
"- - -'\n"
"\n"
"```"
#: src/traits/trait-objects.md:72
@ -17085,7 +17131,7 @@ msgid ""
"exist. Once a pin is\n"
" moved out of the port struct nobody else can take it.\n"
" * Changing the configuration of a pin consumes the old pin instance, so you "
"can't keep use the old\n"
"cant keep use the old\n"
" instance afterwards.\n"
" * The type of a value indicates the state that it is in: e.g. in this case, "
"the configuration state\n"
@ -18152,9 +18198,9 @@ msgid ""
" writeln!(uart, \"main({x0:#x}, {x1:#x}, {x2:#x}, {x3:#x})\").unwrap();\n"
"\n"
" loop {\n"
" if let Some(b) = uart.read_byte() {\n"
" uart.write_byte(b);\n"
" match b {\n"
" if let Some(byte) = uart.read_byte() {\n"
" uart.write_byte(byte);\n"
" match byte {\n"
" b'\\r' => {\n"
" uart.write_byte(b'\\n');\n"
" }\n"
@ -21024,8 +21070,7 @@ msgstr "Seu arquivo `src/main.rs` deve se parecer com isto:"
#: src/exercises/concurrency/link-checker.md:57
msgid ""
"```rust,compile_fail\n"
"use reqwest::blocking::{get, Response};\n"
"use reqwest::Url;\n"
"use reqwest::{blocking::Client, Url};\n"
"use scraper::{Html, Selector};\n"
"use thiserror::Error;\n"
"\n"
@ -21033,34 +21078,57 @@ msgid ""
"enum Error {\n"
" #[error(\"request error: {0}\")]\n"
" ReqwestError(#[from] reqwest::Error),\n"
" #[error(\"bad http response: {0}\")]\n"
" BadResponse(String),\n"
"}\n"
"\n"
"fn extract_links(response: Response) -> Result<Vec<Url>, Error> {\n"
" let base_url = response.url().to_owned();\n"
" let document = response.text()?;\n"
" let html = Html::parse_document(&document);\n"
" let selector = Selector::parse(\"a\").unwrap();\n"
"#[derive(Debug)]\n"
"struct CrawlCommand {\n"
" url: Url,\n"
" extract_links: bool,\n"
"}\n"
"\n"
" let mut valid_urls = Vec::new();\n"
" for element in html.select(&selector) {\n"
" if let Some(href) = element.value().attr(\"href\") {\n"
" match base_url.join(href) {\n"
" Ok(url) => valid_urls.push(url),\n"
" Err(err) => {\n"
" println!(\"On {base_url}: could not parse {href:?}: "
"{err} (ignored)\",);\n"
" }\n"
"fn visit_page(client: &Client, command: &CrawlCommand) -> Result<Vec<Url>, "
"Error> {\n"
" println!(\"Checking {:#}\", command.url);\n"
" let response = client.get(command.url.clone()).send()?;\n"
" if !response.status().is_success() {\n"
" return Err(Error::BadResponse(response.status().to_string()));\n"
" }\n"
"\n"
" let mut link_urls = Vec::new();\n"
" if !command.extract_links {\n"
" return Ok(link_urls);\n"
" }\n"
"\n"
" let base_url = response.url().to_owned();\n"
" let body_text = response.text()?;\n"
" let document = Html::parse_document(&body_text);\n"
"\n"
" let selector = Selector::parse(\"a\").unwrap();\n"
" let href_values = document\n"
" .select(&selector)\n"
" .filter_map(|element| element.value().attr(\"href\"));\n"
" for href in href_values {\n"
" match base_url.join(href) {\n"
" Ok(link_url) => {\n"
" link_urls.push(link_url);\n"
" }\n"
" Err(err) => {\n"
" println!(\"On {base_url:#}: ignored unparsable {href:?}: "
"{err}\");\n"
" }\n"
" }\n"
" }\n"
"\n"
" Ok(valid_urls)\n"
" Ok(link_urls)\n"
"}\n"
"\n"
"fn main() {\n"
" let client = Client::new();\n"
" let start_url = Url::parse(\"https://www.google.org\").unwrap();\n"
" let response = get(start_url).unwrap();\n"
" match extract_links(response) {\n"
" let crawl_command = CrawlCommand{ url: start_url, extract_links: "
"true };\n"
" match visit_page(&client, &crawl_command) {\n"
" Ok(links) => println!(\"Links: {links:#?}\"),\n"
" Err(err) => println!(\"Could not extract links: {err:#}\"),\n"
" }\n"
@ -21068,11 +21136,11 @@ msgid ""
"```"
msgstr ""
#: src/exercises/concurrency/link-checker.md:100
#: src/exercises/concurrency/link-checker.md:120
msgid "Run the code in `src/main.rs` with"
msgstr "Execute o código em `src/main.rs` com"
#: src/exercises/concurrency/link-checker.md:102
#: src/exercises/concurrency/link-checker.md:122
#, fuzzy
msgid ""
"```shell\n"
@ -21083,12 +21151,12 @@ msgstr ""
"$ cargo run\n"
"```"
#: src/exercises/concurrency/link-checker.md:106
#: src/exercises/concurrency/link-checker.md:126
#: src/exercises/concurrency/chat-app.md:140
msgid "## Tasks"
msgstr "## Tarefas"
#: src/exercises/concurrency/link-checker.md:108
#: src/exercises/concurrency/link-checker.md:128
msgid ""
"* Use threads to check the links in parallel: send the URLs to be checked to "
"a\n"
@ -22847,7 +22915,7 @@ msgstr "# Dia 1 Exercícios matinais"
#: src/exercises/day-1/solutions-morning.md:3
msgid "## Arrays and `for` Loops"
msgstr "## Vetores e laços `for`"
msgstr "## Matrizes e Loops `for`"
#: src/exercises/day-1/solutions-morning.md:5
msgid "([back to exercise](for-loops.md))"
@ -24668,6 +24736,211 @@ msgid ""
"```"
msgstr ""
#: src/exercises/concurrency/solutions-morning.md:104
#, fuzzy
msgid "## Link Checker"
msgstr "# Verificador de links _multi-threads_"
#: src/exercises/concurrency/solutions-morning.md:106
#, fuzzy
msgid "([back to exercise](link-checker.md))"
msgstr "([voltar ao exercício](luhn.md))"
#: src/exercises/concurrency/solutions-morning.md:108
msgid ""
"```rust,compile_fail\n"
"// Copyright 2022 Google LLC\n"
"//\n"
"// Licensed under the Apache License, Version 2.0 (the \"License\");\n"
"// you may not use this file except in compliance with the License.\n"
"// You may obtain a copy of the License at\n"
"//\n"
"// http://www.apache.org/licenses/LICENSE-2.0\n"
"//\n"
"// Unless required by applicable law or agreed to in writing, software\n"
"// distributed under the License is distributed on an \"AS IS\" BASIS,\n"
"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
"// See the License for the specific language governing permissions and\n"
"// limitations under the License.\n"
"\n"
"use std::{sync::Arc, sync::Mutex, sync::mpsc, thread};\n"
"\n"
"// ANCHOR: setup\n"
"use reqwest::{blocking::Client, Url};\n"
"use scraper::{Html, Selector};\n"
"use thiserror::Error;\n"
"\n"
"#[derive(Error, Debug)]\n"
"enum Error {\n"
" #[error(\"request error: {0}\")]\n"
" ReqwestError(#[from] reqwest::Error),\n"
" #[error(\"bad http response: {0}\")]\n"
" BadResponse(String),\n"
"}\n"
"// ANCHOR_END: setup\n"
"\n"
"// ANCHOR: visit_page\n"
"#[derive(Debug)]\n"
"struct CrawlCommand {\n"
" url: Url,\n"
" extract_links: bool,\n"
"}\n"
"\n"
"fn visit_page(client: &Client, command: &CrawlCommand) -> Result<Vec<Url>, "
"Error> {\n"
" println!(\"Checking {:#}\", command.url);\n"
" let response = client.get(command.url.clone()).send()?;\n"
" if !response.status().is_success() {\n"
" return Err(Error::BadResponse(response.status().to_string()));\n"
" }\n"
"\n"
" let mut link_urls = Vec::new();\n"
" if !command.extract_links {\n"
" return Ok(link_urls);\n"
" }\n"
"\n"
" let base_url = response.url().to_owned();\n"
" let body_text = response.text()?;\n"
" let document = Html::parse_document(&body_text);\n"
"\n"
" let selector = Selector::parse(\"a\").unwrap();\n"
" let href_values = document\n"
" .select(&selector)\n"
" .filter_map(|element| element.value().attr(\"href\"));\n"
" for href in href_values {\n"
" match base_url.join(href) {\n"
" Ok(link_url) => {\n"
" link_urls.push(link_url);\n"
" }\n"
" Err(err) => {\n"
" println!(\"On {base_url:#}: ignored unparsable {href:?}: "
"{err}\");\n"
" }\n"
" }\n"
" }\n"
" Ok(link_urls)\n"
"}\n"
"// ANCHOR_END: visit_page\n"
"\n"
"struct CrawlState {\n"
" domain: String,\n"
" visited_pages: std::collections::HashSet<String>,\n"
"}\n"
"\n"
"impl CrawlState {\n"
" fn new(start_url: &Url) -> CrawlState {\n"
" let mut visited_pages = std::collections::HashSet::new();\n"
" visited_pages.insert(start_url.as_str().to_string());\n"
" CrawlState {\n"
" domain: start_url.domain().unwrap().to_string(),\n"
" visited_pages,\n"
" }\n"
" }\n"
"\n"
" /// Determine whether links within the given page should be extracted.\n"
" fn should_extract_links(&self, url: &Url) -> bool {\n"
" let Some(url_domain) = url.domain() else {\n"
" return false;\n"
" };\n"
" url_domain == self.domain\n"
" }\n"
"\n"
" /// Mark the given page as visited, returning true if it had already\n"
" /// been visited.\n"
" fn mark_visited(&mut self, url: &Url) -> bool {\n"
" self.visited_pages.insert(url.as_str().to_string())\n"
" }\n"
"}\n"
"\n"
"type CrawlResult = Result<Vec<Url>, (Url, Error)>;\n"
"fn spawn_crawler_threads(\n"
" command_receiver: mpsc::Receiver<CrawlCommand>,\n"
" result_sender: mpsc::Sender<CrawlResult>,\n"
" thread_count: u32,\n"
") {\n"
" let command_receiver = Arc::new(Mutex::new(command_receiver));\n"
"\n"
" for _ in 0..thread_count {\n"
" let result_sender = result_sender.clone();\n"
" let command_receiver = command_receiver.clone();\n"
" thread::spawn(move || {\n"
" let client = Client::new();\n"
" loop {\n"
" let command_result = {\n"
" let receiver_guard = command_receiver.lock().unwrap();\n"
" receiver_guard.recv()\n"
" };\n"
" let Ok(crawl_command) = command_result else {\n"
" // The sender got dropped. No more commands coming in.\n"
" break;\n"
" };\n"
" let crawl_result = match visit_page(&client, &crawl_command) "
"{\n"
" Ok(link_urls) => Ok(link_urls),\n"
" Err(error) => Err((crawl_command.url, error)),\n"
" };\n"
" result_sender.send(crawl_result).unwrap();\n"
" }\n"
" });\n"
" }\n"
"}\n"
"\n"
"fn control_crawl(\n"
" start_url: Url,\n"
" command_sender: mpsc::Sender<CrawlCommand>,\n"
" result_receiver: mpsc::Receiver<CrawlResult>,\n"
") -> Vec<Url> {\n"
" let mut crawl_state = CrawlState::new(&start_url);\n"
" let start_command = CrawlCommand { url: start_url, extract_links: "
"true };\n"
" command_sender.send(start_command).unwrap();\n"
" let mut pending_urls = 1;\n"
"\n"
" let mut bad_urls = Vec::new();\n"
" while pending_urls > 0 {\n"
" let crawl_result = result_receiver.recv().unwrap();\n"
" pending_urls -= 1;\n"
"\n"
" match crawl_result {\n"
" Ok(link_urls) => {\n"
" for url in link_urls {\n"
" if crawl_state.mark_visited(&url) {\n"
" let extract_links = crawl_state."
"should_extract_links(&url);\n"
" let crawl_command = CrawlCommand { url, "
"extract_links };\n"
" command_sender.send(crawl_command).unwrap();\n"
" pending_urls += 1;\n"
" }\n"
" }\n"
" }\n"
" Err((url, error)) => {\n"
" bad_urls.push(url);\n"
" println!(\"Got crawling error: {:#}\", error);\n"
" continue;\n"
" }\n"
" }\n"
" }\n"
" bad_urls\n"
"}\n"
"\n"
"fn check_links(start_url: Url) -> Vec<Url> {\n"
" let (result_sender, result_receiver) = mpsc::channel::<CrawlResult>();\n"
" let (command_sender, command_receiver) = mpsc::channel::"
"<CrawlCommand>();\n"
" spawn_crawler_threads(command_receiver, result_sender, 16);\n"
" control_crawl(start_url, command_sender, result_receiver)\n"
"}\n"
"\n"
"fn main() {\n"
" let start_url = reqwest::Url::parse(\"https://www.google.org\")."
"unwrap();\n"
" let bad_urls = check_links(start_url);\n"
" println!(\"Bad URLs: {:#?}\", bad_urls);\n"
"}\n"
"```"
msgstr ""
#: src/exercises/concurrency/solutions-afternoon.md:1
#, fuzzy
msgid "# Concurrency Afternoon Exercise"