Explicar cada resultado de prontidão

RustBeginner
Pratique Agora

Introdução

Uma lista confiável de ativos é apenas o começo da verificação de um kit. A equipe precisa de uma explicação para cada tipo de equipamento: quantos ativos em bom estado existem, quais estão danificados, o que falta e o que excede o plano. Um item não planejado ou uma quantidade exigida igual a zero não torna a entrada inválida.

Neste laboratório, você implementará essa lógica de conciliação e interpretará seu relatório usando dois conjuntos diferentes de arquivos de entrada reais. Este ambiente é independente: o analisador de estados e o coletor de identidades do laboratório anterior já estão fornecidos. Você não precisa de arquivos da VM anterior. É necessário conhecer as regras de identidade e conflito daquele laboratório, além de estruturas, enumerações, HashMap, Result, leitura de arquivos e laços em Rust. A estimativa do autor é de 45 minutos, incluindo leitura e correções comuns; não é um tempo de conclusão medido com iniciantes.

Conciliar cada tipo e ativo

Nesta etapa, construa a conciliação em três edições: identifique os tipos relevantes, colete seus conjuntos completos de ativos e calcule faltas e excedentes. Abra Terminal. Acrescente o Cargo ao caminho de busca deste shell e entre no projeto preparado de forma independente. Repita o export em um novo terminal e repita cd se perder sua localização:

export PATH=/home/labex/.cargo/bin:$PATH
cd /home/labex/project/field-kit

Primeiro inspecione um resultado normal que já funciona. O parser de estados e o coletor de identidades são fornecidos neste laboratório:

cat data/plan.tsv
cargo run --quiet -- assets data/scans.tsv

A tabela tem seis IDs físicos, não sete leituras. a, b e c são ativos light-a em bom estado. d é um light-b danificado; e e f são ativos em bom estado e danificado do tipo não planejado other. O nome de exibição Flashlight pertence a dois IDs de tipo planejados diferentes. O plano também exige duas cordas sem leituras e zero sobressalentes sem leituras. Preserve essas diferenças ao decidir o que um relatório completo deve explicar.

Edite apenas src/readiness.rs, preservando seu nome de função pública e tipos de parâmetros/retorno. Mantenha os outros arquivos de código-fonte, Cargo.toml, rust-toolchain.toml e todos os arquivos originais de data/ inalterados. Você criará entradas experimentais separadas no diretório do projeto. A interface de comandos lê os caminhos reais que você nomeia; não substitui por um exemplo.

O contrato TSV controlado permanece igual: cabeçalhos exatos de três campos type_id, name, required para planos e asset_id, type_id, condition para leituras, com tabulações literais entre campos. IDs começam com uma letra ASCII minúscula e continuam com letras minúsculas, dígitos ou -. Nomes do plano são ASCII imprimível não vazio, incluindo espaços. Quantidades são dígitos decimais com valor de 0 a 999, permitindo zeros à esquerda. IDs de tipo do plano não podem se repetir, embora nomes de exibição possam. Estados são exatamente good ou damaged. LF/CRLF, ausência de quebra de linha final e linhas posteriores completamente vazias são aceitos. Campos não são aparados; linhas só com espaços em branco não são vazias, e retornos de carro isolados não são finais de linha. São registros de texto sintéticos, não inspeções físicas.

Leia os tipos fornecidos e a função incompleta:

cat src/lib.rs
cat src/readiness.rs

Need contém o ID de tipo, nome e quantidade exigida do plano. Scan contém ID de ativo, ID de tipo, estado e linha de diagnóstico. reconcile recebe &[Need] e &[Scan], fatias emprestadas que não deve alterar. Os ativos já foram validados quanto ao formato e coletados por identidade, portanto cada ID físico ocorre uma vez. O Vec<TypeSummary> retornado deve incluir a união dos tipos planejados e lidos, conjuntos completos de IDs em bom estado/danificados, quantidade exigida, quantidade faltante e excedente. O formatador preparado fornece nomes de exibição, ordena linhas e listas de IDs e imprime a relação planned ou unplanned de cada ativo.

Primeira edição: preservar os tipos relevantes

Considere um plano sem ativos lidos. Uma exigência positiva ainda é uma falta, enquanto uma exigência zero tem falta zero. Crie uma entrada separada de leituras vazias; não esvazie o original protegido. printf imprime sua string de formato, interpretando \t como tabulação e \n como quebra de linha. Aspas simples preservam essas barras invertidas para printf, e > grava este arquivo experimental nomeado. O cabeçalho é sua única linha:

printf 'asset_id\ttype_id\tcondition\n' > empty-scans.tsv
cat empty-scans.tsv

Antes de alterar o código, preveja quais tipos esta entrada deve preservar: light-a, light-b, rope e zero, com exigências 2, 1, 2 e 0. Não há tipo exclusivo das leituras nesta entrada específica. Mesmo assim, a função geral deve descobrir tipos de ambas as fatias para que as leituras reais posteriores acrescentem other.

Crie um ponto de controle apenas do código-fonte. mktemp -d cria um novo diretório substituindo os seis caracteres X por um sufixo único; não reutiliza um backup anterior. $() captura o caminho impresso na variável da sessão. && executa cp -p somente após a criação bem-sucedida do diretório. A cópia preserva os metadados do código-fonte, e as aspas na variável mantêm unido seu caminho. Se uma operação falhar, leia o erro e obtenha um backup antes de editar:

rs03_before_types=$(mktemp -d .edit-before-types.XXXXXX) && cp -p src/readiness.rs "$rs03_before_types/readiness.rs"

Abra o arquivo existente com nano; -c mostra a posição do cursor:

nano -c src/readiness.rs

O nano usa ^ para significar Ctrl. Pressione Ctrl+W, busque pub fn reconcile e pressione Enter. Leia as chaves de abertura e fechamento dessa função. Use as setas e Backspace/Delete para editar apenas seu corpo e as vinculações dos parâmetros não usados. Renomeie _needs e _assets para needs e assets, preservando seus tipos. Mantenha a importação fornecida use crate::{Need, Scan, TypeSummary};; esta fase ainda não usa Condition.

Construa esta primeira fase a partir das relações dos dados. Crie um vetor types vazio. Acrescente cada ID de tipo planejado. Depois percorra os ativos e acrescente um ID de tipo somente quando types.contains(&asset.type_id) for falso. Um plano já tem IDs de tipo únicos. .clone() copia uma string própria de ID de tipo de um registro emprestado para que seu resultado possa possuí-la.

Depois crie um vetor summaries vazio e percorra cada ID de tipo coletado. Comece sua quantidade exigida em zero e procure esse tipo no plano. usize::from(need.required) converte o valor u16 do plano para o tipo de contagem usado pelos comprimentos de vetor. Por enquanto, use vetores good_ids e damaged_ids vazios. Enquanto nenhum ativo utilizável foi contado, atribua missing a partir de required e defina surplus como zero. Acrescente um TypeSummary usando essas variáveis e faça de summaries a expressão final da função. A notação abreviada de campos, como type_id, required, usa variáveis com nomes correspondentes. Você ainda não implementou a classificação de ativos.

Salve com Ctrl+O, Enter para confirmar o nome atual e Ctrl+X para sair. Leia a função salva antes de executá-la:

cat src/readiness.rs

cargo run compila e executa o programa; --quiet oculta o progresso do Cargo, e -- passa os argumentos seguintes ao programa. Teste o caso limitado sem leituras:

cargo run --quiet -- check data/plan.tsv empty-scans.tsv

Espere aqueles quatro tipos planejados, zero ativos em bom estado e conjuntos de IDs vazios, quantidades faltantes 2, 1, 2 e 0 e excedente zero. Não deve haver linhas de dados de ativos após o cabeçalho ASSET. Esta primeira fase só está correta para esta observação de leituras vazias. Não é uma implementação completa para uma lista de ativos não vazia. Não interprete um status de saída zero como prova de que todas as regras de conciliação estão concluídas.

Segunda edição: preservar conjuntos completos de estados

Agora preveja os conjuntos completos das leituras reais: light-a tem IDs bons a,b,c; light-b tem o ID danificado d; other tem o ID bom e e o danificado f. rope e zero ainda precisam de seus próprios resumos mesmo sem ativos. Preserve todos os conjuntos em vez de escolher um ativo “extra” preferido.

Preserve sua fase funcional de união de tipos:

rs03_before_sets=$(mktemp -d .edit-before-sets.XXXXXX) && cp -p src/readiness.rs "$rs03_before_sets/readiness.rs"
nano -c src/readiness.rs

No início, altere a importação de crate para incluir a enumeração que você usará agora:

use crate::{Condition, Need, Scan, TypeSummary};

Esta é uma edição explícita do código-fonte: o código inicial original não importa Condition. crate refere-se a esta biblioteca; use traz seus nomes para este módulo. Acrescentar a importação junto com seu primeiro uso também evita um aviso de importação não usada na fase anterior.

Busque pub fn reconcile. Mantenha a lógica de união de tipos e quantidade exigida. Torne good_ids e damaged_ids mutáveis porque acrescentará IDs. Dentro do laço de cada tipo, percorra todos os ativos, selecione os que têm o type_id atual e faça a correspondência de seus estados. Para a variante Good, acrescente uma cópia clonada do ID do ativo ao vetor de bons; para Damaged, acrescente ao vetor de danificados. Ambas as variantes são entradas válidas. Não exclua ativos danificados ou não planejados. Deixe as duas atribuições temporárias de falta/excedente inalteradas até a próxima edição.

Salve, saia e execute com as leituras reais:

cargo run --quiet -- check data/plan.tsv data/scans.tsv

Leia IDs de tipo, quantidades exigidas, contagens de bons, conjuntos completos de IDs e linhas ASSET. Devem corresponder à sua previsão, incluindo other. As colunas de falta e excedente ainda são temporárias e não são resultados gerais válidos. Por exemplo, a falta temporária de light-a ainda pode mostrar 2, embora seus três IDs bons agora estejam presentes. Explique por que isso não pode ser um relatório concluído. Você corrigirá esses dois cálculos a seguir; não afirme que esta tabela intermediária passa na verificação final.

Terceira edição: calcular faltas e excedentes apenas com bons ativos

Somente ativos únicos em bom estado atendem à demanda. Para light-a, exigência 2 e bons 3 significam falta 0, excedente 1. Para light-b, seu ativo danificado não atende à exigência: falta 1, excedente 0. rope tem falta de 2. zero permanece 0/0. O other não planejado tem exigência zero e um ativo bom, portanto excedente 1; seu ID danificado é preservado, mas não aumenta o excedente.

Salve a fase que já coleta os conjuntos corretos:

rs03_before_totals=$(mktemp -d .edit-before-totals.XXXXXX) && cp -p src/readiness.rs "$rs03_before_totals/readiness.rs"
nano -c src/readiness.rs

Busque pub fn reconcile e substitua somente as expressões temporárias de falta e excedente. O len() de um vetor fornece sua contagem. saturating_sub subtrai sem cair abaixo de zero, evitando subfluxo de inteiros sem sinal. As regras são missing = max(required - good, 0) e surplus = max(good - required, 0); aplique-as a good_ids.len(), nunca à quantidade de leituras brutas ou IDs danificados. Mantenha intactos a união de tipos e os vetores completos. Salve com Ctrl+O, Enter e Ctrl+X.

cargo run --quiet -- check data/plan.tsv data/scans.tsv
TYPE	type_id	name	required	good	missing	surplus	good_ids	damaged_ids
TYPE	light-a	Flashlight	2	3	0	1	a,b,c	-
TYPE	light-b	Flashlight	1	0	1	0	-	d
TYPE	other	-	0	1	0	1	e	f
TYPE	rope	Rope	2	0	2	0	-	-
TYPE	zero	Spare	0	0	0	0	-	-
ASSET	asset_id	type_id	condition	plan
ASSET	a	light-a	good	planned
ASSET	b	light-a	good	planned
ASSET	c	light-a	good	planned
ASSET	d	light-b	damaged	planned
ASSET	e	other	good	unplanned
ASSET	f	other	damaged	unplanned

Cada quantidade agora é rastreável: light-b preserva o ID danificado d, mas tem uma falta; other preserva ambos os IDs, mas apenas e cria excedente. A leitura repetida de a nunca criou outro ativo físico. Repita a verificação anterior de origem vazia com a função concluída:

cargo run --quiet -- check data/plan.tsv empty-scans.tsv

Seus quatro tipos planejados e faltas sem leituras ainda devem estar corretos. Somente agora se espera que a verificação completa de conciliação desta etapa passe. Ordens internas equivalentes de coleta continuam válidas porque o formatador preparado ordena as linhas e listas de IDs exibidas.

Reparar uma edição de código-fonte sem reiniciar o projeto

O erro do Cargo nomeia um arquivo e uma linha. Reabra esse arquivo, localize a função com Ctrl+W e repare a edição indicada. Se uma alteração acidental não foi salva, Ctrl+X e N na pergunta sobre salvar a descartam; isso não desfaz uma edição salva anteriormente.

Somente ao recuperar um erro salvo, escolha o ponto de controle da fase que deseja refazer: .edit-before-types.* contém o código inicial, .edit-before-sets.* contém a fase de união de tipos e .edit-before-totals.* contém os conjuntos completos de IDs antes dos cálculos finais. Em um novo Terminal, repita PATH e cd, depois liste os nomes reais dos backups:

ls -d .edit-before-*

-d lista nomes de diretório em vez de conteúdo, e * corresponde aos sufixos gerados. Execute o comando seguinte; enquanto o shell aguarda, cole um nome de diretório exato impresso e pressione Enter. read armazena sua seleção nesta variável da sessão:

read rs03_restore

Confirme o código-fonte selecionado antes de copiá-lo:

cat "$rs03_restore/readiness.rs"

Se o caminho ou a fase estiver errado, repita a seleção e a inspeção. A ausência de backup correspondente significa que você deve reparar a função usando sua referência em vez de adivinhar um caminho. Após confirmar a fase desejada, este comando substitui deliberadamente apenas o código-fonte de prontidão permitido:

cp -p "$rs03_restore/readiness.rs" src/readiness.rs

Refaça depois as edições e os testes afetados; um ponto de controle de código-fonte não altera suas entradas. Nunca altere o plano original para corresponder a um algoritmo incorreto. A referência abaixo serve para comparar uma parte específica após sua tentativa, não para substituir por padrão o arquivo inteiro.

Referência completa de conciliação para comparação e recuperação
use crate::{Condition, Need, Scan, TypeSummary};

pub fn reconcile(needs: &[Need], assets: &[Scan]) -> Vec<TypeSummary> {
    let mut types = Vec::new();
    for need in needs {
        types.push(need.type_id.clone());
    }
    for asset in assets {
        if !types.contains(&asset.type_id) {
            types.push(asset.type_id.clone());
        }
    }
    let mut summaries = Vec::new();
    for type_id in types {
        let mut required = 0;
        for need in needs {
            if need.type_id == type_id {
                required = usize::from(need.required);
            }
        }
        let mut good_ids = Vec::new();
        let mut damaged_ids = Vec::new();
        for asset in assets {
            if asset.type_id == type_id {
                match asset.condition {
                    Condition::Good => good_ids.push(asset.asset_id.clone()),
                    Condition::Damaged => damaged_ids.push(asset.asset_id.clone()),
                }
            }
        }
        let missing = required.saturating_sub(good_ids.len());
        let surplus = good_ids.len().saturating_sub(required);
        summaries.push(TypeSummary {
            type_id, required, good_ids, damaged_ids, missing, surplus,
        });
    }
    summaries
}

Explicar o atendimento parcial a partir de outra origem

Nesta etapa, você usará caminhos de entrada diferentes para inspecionar faltas parciais e um tipo não planejado representado apenas por danos. Continue em Terminal, em /home/labex/project/field-kit. Preserve todos os arquivos originais de data/; crie, em vez disso, os dois arquivos de teste separados abaixo.

Os blocos curtos de entrada abaixo usam um here-document. cat recebe as linhas seguintes como entrada, > grava o arquivo experimental nomeado e <<'TSV' inicia a entrada literal até uma linha contendo apenas TSV. O delimitador entre aspas impede a expansão do conteúdo pelo shell. Copie ambas as linhas delimitadoras e preserve as tabulações literais. São entradas pequenas de teste, não substituições de um módulo Rust. O nome Water filter contém um espaço dentro de um campo; tabulações separam campos.

cat > trial-plan.tsv <<'TSV'
type_id	name	required
lamp	Lamp	2
filter	Water filter	5
zero	Spare	0
TSV
cat > trial-scans.tsv <<'TSV'
asset_id	type_id	condition
l-one	lamp	good
l-two	lamp	damaged
f-one	filter	good
f-two	filter	good
u-one	unplanned	damaged
TSV

Antes de executar, preveja a falta de lanternas, a falta de filtros e se o tipo não planejado apenas danificado permanecerá presente. Agora especifique estes arquivos reais:

cargo run --quiet -- check trial-plan.tsv trial-scans.tsv
TYPE	type_id	name	required	good	missing	surplus	good_ids	damaged_ids
TYPE	filter	Water filter	5	2	3	0	f-one,f-two	-
TYPE	lamp	Lamp	2	1	1	0	l-one	l-two
TYPE	unplanned	-	0	0	0	0	-	u-one
TYPE	zero	Spare	0	0	0	0	-	-
ASSET	asset_id	type_id	condition	plan
ASSET	f-one	filter	good	planned
ASSET	f-two	filter	good	planned
ASSET	l-one	lamp	good	planned
ASSET	l-two	lamp	damaged	planned
ASSET	u-one	unplanned	damaged	unplanned

Antes de interpretar o relatório, preveja as faltas de lanternas e filtros a partir dos ativos bons, não da quantidade de leituras. A falta de lanternas é um, apesar das duas linhas de leitura. Dois filtros bons atendem apenas a dois dos cinco exigidos, deixando três faltantes. O ativo não planejado danificado continua visível com zero bons e zero excedente. zero permanece presente mesmo sem ser mencionado nas leituras de teste.

Tente uma alteração controlada sem editar os dados originais nem suas primeiras leituras de teste. cp -p cria uma cópia preservando os metadados do arquivo copiado. Use este nome novo separado; se já o tiver usado em uma tentativa anterior, preserve esse trabalho ou escolha outro nome ainda não usado e utilize-o consistentemente nos comandos seguintes.

cp -p trial-scans.tsv trial-scans-one-repair.tsv
nano -c trial-scans-one-repair.tsv

Pressione Ctrl+W, busque l-two e pressione Enter. Somente nessa linha, substitua o estado damaged por good, usando as setas e Backspace/Delete. Preserve seu ID de ativo, ID de tipo, cabeçalho e separadores de tabulação literais. Salve com Ctrl+O, Enter e saia com Ctrl+X. Esta é uma edição intencional do seu experimento separado, não um reparo de evidências protegidas.

Preveja as duas linhas afetadas do relatório antes de executar: TYPE lamp agora deve ter dois IDs bons, nenhum ID danificado, falta zero e excedente zero. ASSET l-two deve mudar seu estado para bom, permanecendo planejado. Todas as outras linhas de tipo e ativo devem permanecer inalteradas; as exigências de filtros e o ativo não planejado danificado não mudaram.

cargo run --quiet -- check trial-plan.tsv trial-scans-one-repair.tsv

Leia essas duas linhas e confirme que as outras ainda correspondem. Se mais coisas mudaram, inspecione sua cópia com cat e corrija o campo não intencional; não altere o algoritmo apenas para corresponder a uma edição acidental da entrada. Retorne à sua primeira origem de teste inalterada:

cargo run --quiet -- check trial-plan.tsv trial-scans.tsv

A falta de lanternas deve voltar a um, com l-two danificado. Os dois relatórios diferem porque suas entradas reais nomeadas diferem; nenhuma origem é substituída silenciosamente. A mesma implementação completa de conciliação deve explicar ambos os casos.

Um erro de entrada não deve se transformar em relatório de faltas nem recorrer silenciosamente ao exemplo original. O próximo nome incorreto é intencional e deve retornar um valor diferente de zero:

cargo run --quiet -- check trial-plans.tsv trial-scans.tsv

O erro nomeia o trial-plans.tsv inexistente. Corrija o plural e execute novamente:

cargo run --quiet -- check trial-plan.tsv trial-scans.tsv

Para um erro deliberado de formato, use o arquivo de teste existente com estado desconhecido. Este comando também deve retornar um valor diferente de zero e nomear data/bad-status.tsv com sua linha:

cargo run --quiet -- check trial-plan.tsv data/bad-status.tsv

broken é uma palavra inválida, enquanto damaged é um estado válido que não atende à demanda. Recupere selecionando novamente as leituras de teste válidas:

cargo run --quiet -- check trial-plan.tsv trial-scans.tsv

Por fim, retorne às entradas originais:

cargo run --quiet -- check data/plan.tsv data/scans.tsv

O relatório original retorna porque seu programa lê os caminhos reais, não um relatório em cache ou indicador de prontidão fixo no código. Tanto os arquivos originais quanto os de teste permanecem intactos. Você pode inspecionar qualquer um deles com cat; para recuperar uma edição acidental em um arquivo de teste, repita seu bloco completo de criação acima. Os arquivos originais protegidos não são o lugar para reparar um erro de código.

Resumo

Você implementou a conciliação completa de tipos a partir de um plano e de registros únicos de ativos. Apenas ativos em bom estado atenderam às exigências; o atendimento parcial ainda deixou uma falta, e tanto os equipamentos danificados quanto os excedentes continuaram explicáveis. Você testou outro par real de caminhos de entrada, tratou erros comuns e preservou todas as origens. O desafio pedirá que você combine coleta de identidades e conciliação para um kit de outra viagem.