Error: builder error: relative URL without a base

I'm a Rust beginner and am coding my first Rust program: a weather app that gets the weather for 10 locations across Canada as XML data from the Canadian government meteorological site. I'm writing this program in stages. Stage 1 reads in a CSV file with the cities and the corresponding URLs of the XML files. This now works in Rust. Stage 2 is to see that the program can indeed retrieve the files and read through them line by line. Stage 3 is to fine tune this and extract the info for displaying current temperate, current conditions, high, low, and either Humidex or Windchill temperature.

The problem occurs in Stage 2, in the fn fetch_and_parse_xml. I've passed in the two strings for City name and associated URL. On the line before the get(url)? I print out the two strings and the URL is fully formed, with its base. As soon as it tries to get the file I get the error message: "Error: builder error: relative URL without a base"

Here is my unfinished program:

  1 use std::path::Path;
  2 use csv::ReaderBuilder;
  3 use reqwest::blocking::get;
  4 use std::io::BufRead;
  5 use std::io::BufReader;
  6 
  7 
  8 struct Site {
  9     city: String,
 10     url: String,
 11 }
 12 
 13 fn main() -> Result<(), Box<(dyn std::error::Error + 'static)>> {
 14     let path=Path::new("weather-sites.csv");
 15     let mut reader = ReaderBuilder::new().from_path(&path)?;
 16 
 17     let mut sites = Vec::new();
 18 
 19     for result in reader.records() {
 20         let record = result?;
 21         let city = record.get(0).unwrap_or("").to_string();
 22         let url  = record.get(1).unwrap_or("").to_string();
 23 
 24         sites.push(Site {city, url});
 25     }
 26 
 27     for site in &sites {
 28         let city_name = &site.city;
 29         let city_url = &site.url;
 30         // println!("City: {}, URL: {}", city_name, city_url);
 31         if let Err(e) = fetch_and_parse_xml(city_name, city_url) {
 32          eprintln!("Error: {}", e);
 33         }
 34 
 35     }
 36 
 37     Ok(())
 38 }
 39 
 40 
 41 fn fetch_and_parse_xml(city: &str, url: &str) -> Result<(), Box<dyn std::error::Error>> {
 42     // Check the incoming
 43     println!("{}, {}", city, url);
 44     // Fetch the XML file from the internet
 45     let response = get(url)?;
 46 
 47     // Check if the request was successful
 48     if !response.status().is_success() {
 49         return Err(format!("Failed to fetch XML file for city {}: {}", city, response.status()).into(
 50     }
 51 
 52     // Read the XML file line by line
 53     let reader = BufReader::new(response);
 54     for line in reader.lines() {
 55         let line = line?;
 56         println!("{}", line);
 57     }
 58 
 59     Ok(())
 60 }

I've based this on sample programs I've seen on the Net and attempted to modify them for my use. I may be missing something basic.

I have this app working in both Python and PHP, but wanted to see if I could replicate it in Rust.

This is the current output:

'Ladysmith', 'https://dd.weather.gc.ca/citypage_weather/xml/BC/s0000496_e.xml'
Error: builder error: relative URL without a base
'Kelowna', 'https://dd.weather.gc.ca/citypage_weather/xml/BC/s0000592_e.xml'
Error: builder error: relative URL without a base
'Arnprior', 'https://dd.weather.gc.ca/citypage_weather/xml/BC/s0000592_e.xml'
Error: builder error: relative URL without a base
'Ottawa', 'https://dd.weather.gc.ca/citypage_weather/xml/ON/s0000430_e.xml'
Error: builder error: relative URL without a base
'Sauble', 'https://dd.weather.gc.ca/citypage_weather/xml/ON/s0000724_e.xml'
Error: builder error: relative URL without a base
'Guelph', 'https://dd.weather.gc.ca/citypage_weather/xml/BC/s0000592_e.xml'
Error: builder error: relative URL without a base
'Hamilton', 'https://dd.weather.gc.ca/citypage_weather/xml/ON/s0000549_e.xml'
Error: builder error: relative URL without a base
'St_Catherines', 'https://dd.weather.gc.ca/citypage_weather/xml/ON/s0000691_e.xml'
Error: builder error: relative URL without a base
'Toronto', 'https://dd.weather.gc.ca/citypage_weather/xml/ON/s0000458_e.xml'
Error: builder error: relative URL without a base
'Mississauga', 'https://dd.weather.gc.ca/citypage_weather/xml/ON/s0000786_e.xml'
Error: builder error: relative URL without a base

At a guess, your city name and URL may literally have ' characters in them.

Maybe you need to use quote with your CSV reader.

Thinking about @quinedot 's answer, I just noticed this:
When printing something (using the Display trait for &str), it doesn't add ' characters.

For example:

    let url = "https://dd.weather.gc.ca/citypage_weather/xml/BC/s0000592_e.xml"
    println!("{}", url);
// Output, https://dd.weather.gc.ca/citypage_weather/xml/BC/s0000592_e.xml

It won't enclose that string with ' characters. It is a parsing error, not a reqwest error.

That is indeed what tipped me off :slight_smile:.

Thank you for taking the time to look at this issue. You've got good eyes. I realized now that my CSV data file had all the strings in single quotes. Once I removed them, the get() function worked perfectly and I have XML data streaming through my app.

It was good to learn that the CSV file shouldn't contain any single quotes. That's a mistake I won't make again.

It depends on the data and what's consuming it. You need some sort of quoting or escaping mechanism if your data contains commas.

That said, if you don't need the quoting mechanism, not quoting things will make your CSV file consumable by more things and/or with less hoops to jump through.