Welcome to Rust, I hope you enjoyed learning and using it :)
One major advice would be to run cargo clippy, which gives a lot of helpful hints. Apart from that I also have some feedback.
40 struct VendorOuiData {
41 vendor_organization: String, // Vendor Organization
These comments should be doc-comments (///) so that cargo doc picks them up.
49 fn fetch_oui_database(url_string: &String) -> Result<String, Box<dyn std::error::Error>> {
Not saying that your return type is bad, but in general you might be interested in crates thiserror and anyhow that simplify error handling.
51 .timeout(Duration::new(60, 0))
Duration::from_mins(1) would be much more readable.
66 panic!("{}", err);
You should almost never panic. Your function already returns a Result<>, so you should be using that for error handling.
70 } else {
71 // HTTP Status is not 200
Not Rust specific, but usually I recommend to handle errors first, and return from the function early. This way you reduce cognitive load on the reader, and reduce nesting of if-blocks.
150 let mut parts = line.splitn(2, '\t');
There’s a convenient method to split in two substrings that you could have used:
let (mac_address, vendor_organization) = line.split_once('\t').unwrap_or(("", ""));
166 vendor_oui_data.vendor_address += line
167 .trim()
168 .split(' ')
169 .filter(|s| !s.is_empty())
170 .collect::<Vec<_>>()
171 .join(" ")
172 .as_str();
I would probably write a regular expression that replaces all contiguous whitespace with a single space instead.
173 vendor_oui_data.vendor_address.push('\n');
Aren’t you trimming this new line off in line 181?
181 vendor_oui_data.vendor_address = vendor_oui_data.vendor_address.trim().to_string();
This is somewhat inefficient, since you’ll be allocating and copying your string here.





Conceptually, error handling in Rust is incredibly simple: if a function can fail, its return type is an enum of either the result of the function (in case of success) or a description of the error that happened. This enum is called Result. See:
You don’t. You can 100% handle errors without any additional dependencies, and probably you should be doing that in the beginning. The crates simply add a little bit of syntactic sugar to simplify some boilerplate that you’ll have to start writing as soon as your error handling gets sufficiently complex.