Я очень новичок в Rust и все еще пытаюсь научиться работать с ним. Это круто, но я явно что-то упускаю в упражнении, которое я дал себе. Для справки я использую rustc 1.39.0.
Я хотел попробовать написать простую программу для чтения XML из анализа кода MSBuild, которая выводит довольно простой XML. Я думаю, что проблема в том, что есть элемент (PATH
), который обычно пуст, но иногда может содержать элементы под ним. Большая проблема в том, что я не умею работать с Rust (и я обычно не имею дело с XML), и я не совсем уверен, как правильно настроить структуры, необходимые для десериализации. Я использую Serde и quick_xml. Когда я установил PATH
в качестве строки и работал с XML, в котором не было элемента SFA в PATH, мои тесты работали. Но как только я выяснил, как использовать этот тег предполагается , и соответственно обновил свои структуры, я постоянно получаю сообщение об ошибке:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Custom("missing field `FILEPATH`")', src\libcore\result.rs:1165:5
... даже если all дефектов в тестовом XML-файле имеют элемент SFA в PATH.
Все XML-файлы, с которыми я имею дело, выглядят так:
<?xml version="1.0" encoding="utf-8"?>
<DEFECTS>
<DEFECT>
<SFA>
<FILEPATH>c:\projects\source\repos\defecttest\defecttest</FILEPATH>
<FILENAME>source.cpp</FILENAME>
<LINE>8</LINE>
<COLUMN>5</COLUMN>
</SFA>
<DEFECTCODE>26496</DEFECTCODE>
<DESCRIPTION>The variable 'y' is assigned only once, mark it as const (con.4).</DESCRIPTION>
<FUNCTION>main</FUNCTION>
<DECORATED>main</DECORATED>
<FUNCLINE>6</FUNCLINE>
<PATH></PATH>
</DEFECT>
<DEFECT>
<SFA>
<FILEPATH>c:\projects\source\repos\defecttest\defecttest</FILEPATH>
<FILENAME>source.cpp</FILENAME>
<LINE>9</LINE>
<COLUMN>5</COLUMN>
</SFA>
<DEFECTCODE>26496</DEFECTCODE>
<DESCRIPTION>The variable 'z' is assigned only once, mark it as const (con.4).</DESCRIPTION>
<FUNCTION>main</FUNCTION>
<DECORATED>main</DECORATED>
<FUNCLINE>6</FUNCLINE>
<PATH></PATH>
</DEFECT>
</DEFECTS>
Во многих случаях PATH
пусто, но в некоторых он содержит свой собственный элемент SFA
:
<DEFECT>
<SFA>
<FILEPATH>c:\projects\source\repos\defecttest\defecttest</FILEPATH>
<FILENAME>source.cpp</FILENAME>
<LINE>9</LINE>
<COLUMN>5</COLUMN>
</SFA>
<DEFECTCODE>26496</DEFECTCODE>
<DESCRIPTION>The variable 'z' is assigned only once, mark it as const (con.4).</DESCRIPTION>
<FUNCTION>main</FUNCTION>
<DECORATED>main</DECORATED>
<FUNCLINE>6</FUNCLINE>
<PATH>
<SFA>
<FILEPATH>c:\projects\source\repos\defecttest\defecttest</FILEPATH>
<FILENAME>source.cpp</FILENAME>
<LINE>12</LINE>
<COLUMN>3</COLUMN>
</SFA>
</PATH>
</DEFECT>
До того, как я это понял, все поля в структуре DEFECT были установлены в String. Это работает правильно, если предположить, что ни один из дефектов в XML-файле не имеет подэлементов в PATH. Когда я изменил его на SFA вместо String, он выдает ошибку отсутствующего поля, упомянутую выше. Пример кода, с которым я тестирую:
main.rs
extern crate quick_xml;
extern crate serde;
use std::default::Default;
use std::env;
use std::vec::Vec;
use quick_xml::de::from_str;
use serde::{Serialize, Deserialize};
/*
* Structs for the defect XML
*/
#[derive(Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct DEFECTS {
#[serde(rename = "DEFECT", default)]
pub defects: Vec<DEFECT>,
}
#[derive(Default, Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct DEFECT {
#[serde(default)]
pub SFA: SFA,
pub DEFECTCODE: String,
pub DESCRIPTION: String,
pub FUNCTION: String,
pub DECORATED: String,
pub FUNCLINE: String,
#[serde(default)]
pub PATH: Vec<SFA>,
}
#[derive(Default, Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct SFA {
pub FILEPATH: String,
pub FILENAME: String,
pub LINE: String,
pub COLUMN: String,
}
/*
* Main app code
*/
fn main() {
// Expect the path to the XML file to be passed as the first and only argument
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
panic!("Invalid argument count. Specify a single file to process.");
}
let processing_file = &args[1];
println!("Will attempt to process file: '{}'", &processing_file);
// Try to load the contents of the file
let file_content : String = match std::fs::read_to_string(&processing_file) {
Ok(file_content) => file_content,
Err(e) => {
panic!("Failed to read file: '{}' -- {}", &processing_file, e);
}
};
// Now, try to deserialize the XML we have in file_content
let defect_list : DEFECTS = from_str(&file_content).unwrap();
// Assuming the unwrap above didn't blow up, we should get a count here
println!("Retrieved {} defects from file '{}'", defect_list.defects.len(), &processing_file);
}
Cargo.toml
[package]
name = "rust_xml_test"
version = "0.1.0"
authors = ["fny82"]
edition = "2018"
[dependencies]
quick-xml = { version = "0.17", features = [ "serialize" ] }
serde = { version = "1.0", features = [ "derive" ] }
Пример выходных данных
C:\Development\RustXmlTest>cargo run -- "c:\development\rustxmltest\test3.xml"
Compiling rust_xml_test v0.1.0 (C:\Development\RustXmlTest)
Finished dev [unoptimized + debuginfo] target(s) in 1.56s
Running `target\debug\rust_xml_test.exe c:\development\rustxmltest\test3.xml`
Will attempt to process file: 'c:\development\rustxmltest\test3.xml'
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Custom("missing field `FILEPATH`")', src\libcore\result.rs:1165:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
error: process didn't exit successfully: `target\debug\rust_xml_test.exe c:\development\rustxmltest\test3.xml` (exit code: 101)
Я уверен, что делаю что-то глупое, и отчасти это связано с тем, что я забегаю вперед в отношении объемазадачи по сравнению с моим текущим уровнем понимания работы с Rust. Может кто-нибудь помочь мне разобраться, что я пропускаю и что делаю неправильно?
Несколько связано: с тех пор я узнал, что могу использовать свойство rename
, чтобы мои структуры соответствовали Rustсоглашения об именах, но пока я не хотел начинать возиться с этим, пока не заработал базовую функциональность.
---- EDIT ----
Для справки, с исправлением от @edwardw теперь работающий код:
extern crate quick_xml;
extern crate serde;
use std::default::Default;
use std::env;
use std::vec::Vec;
use quick_xml::de::from_str;
use serde::{Serialize, Deserialize};
/*
* Structs for the defect XML
*/
#[derive(Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct DEFECTS {
#[serde(rename = "DEFECT", default)]
pub defects: Vec<DEFECT>,
}
#[derive(Default, Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct DEFECT {
#[serde(default)]
pub SFA: SFA,
pub DEFECTCODE: String,
pub DESCRIPTION: String,
pub FUNCTION: String,
pub DECORATED: String,
pub FUNCLINE: String,
pub PATH: PATH,
}
#[derive(Default, Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct SFA {
pub FILEPATH: String,
pub FILENAME: String,
pub LINE: String,
pub COLUMN: String,
}
#[derive(Default, Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct PATH {
pub SFA: Option<SFA>,
}
/*
* Main app code
*/
fn main() {
// Expect the path to the XML file to be passed as the first and only argument
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
panic!("Invalid argument count. Specify a single file to process.");
}
let processing_file = &args[1];
println!("Will attempt to process file: '{}'", &processing_file);
// Try to load the contents of the file
let file_content : String = match std::fs::read_to_string(&processing_file) {
Ok(file_content) => file_content,
Err(e) => {
panic!("Failed to read file: '{}' -- {}", &processing_file, e);
}
};
// Now, try to deserialize the XML we have in file_content
let defect_list : DEFECTS = from_str(&file_content).unwrap();
// Assuming the unwrap above didn't blow up, we should get a count here
println!("Retrieved {} defects from file '{}'", defect_list.defects.len(), &processing_file);
}
Пример:
C:\Development\RustXmlTest>cargo run -- "c:\development\rustxmltest\test1.xml"
Compiling rust_xml_test v0.1.0 (C:\Development\RustXmlTest)
Finished dev [unoptimized + debuginfo] target(s) in 1.66s
Running `target\debug\rust_xml_test.exe c:\development\rustxmltest\test1.xml`
Will attempt to process file: 'c:\development\rustxmltest\test1.xml'
Retrieved 2 defects from file 'c:\development\rustxmltest\test1.xml'
, где test1.xml содержит:
<?xml version="1.0" encoding="utf-8"?>
<DEFECTS>
<DEFECT>
<SFA>
<FILEPATH>c:\projects\source\repos\defecttest\defecttest</FILEPATH>
<FILENAME>source.cpp</FILENAME>
<LINE>8</LINE>
<COLUMN>5</COLUMN>
</SFA>
<DEFECTCODE>26496</DEFECTCODE>
<DESCRIPTION>The variable 'y' is assigned only once, mark it as const (con.4).</DESCRIPTION>
<FUNCTION>main</FUNCTION>
<DECORATED>main</DECORATED>
<FUNCLINE>6</FUNCLINE>
<PATH></PATH>
</DEFECT>
<DEFECT>
<SFA>
<FILEPATH>c:\projects\source\repos\defecttest\defecttest</FILEPATH>
<FILENAME>source.cpp</FILENAME>
<LINE>9</LINE>
<COLUMN>5</COLUMN>
</SFA>
<DEFECTCODE>26496</DEFECTCODE>
<DESCRIPTION>The variable 'z' is assigned only once, mark it as const (con.4).</DESCRIPTION>
<FUNCTION>main</FUNCTION>
<DECORATED>main</DECORATED>
<FUNCLINE>6</FUNCLINE>
<PATH>
<SFA>
<FILEPATH>c:\projects\source\repos\defecttest\defecttest</FILEPATH>
<FILENAME>source.cpp</FILENAME>
<LINE>12</LINE>
<COLUMN>3</COLUMN>
</SFA>
</PATH>
</DEFECT>
</DEFECTS>