Skip to content

Commit

Permalink
Merge pull request #740 from antoine-de/gtfs_from_read
Browse files Browse the repository at this point in the history
add a gtfs::from_read method
  • Loading branch information
datanel authored Feb 8, 2021
2 parents 52250ed + a38ba99 commit b820be2
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 28 deletions.
42 changes: 27 additions & 15 deletions src/gtfs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,13 +333,7 @@ where
/// Imports a `Model` from the [GTFS](https://gtfs.org/reference/static)
/// files in the `path` directory.
///
/// The `config_path` argument allows you to give a path to a file
/// containing a json representing the contributor and dataset used
/// for this GTFS. If not given, default values will be created.
///
/// The `prefix` argument is a string that will be prepended to every
/// identifiers, allowing to namespace the dataset. By default, no
/// prefix will be added to the identifiers.
/// The `Configuration` is used to control various parameters during the import.
pub fn read_from_path<P: AsRef<Path>>(p: P, configuration: Configuration) -> Result<Model> {
let mut file_handle = read_utils::PathFileHandler::new(p.as_ref().to_path_buf());
read(&mut file_handle, configuration)
Expand All @@ -348,15 +342,33 @@ pub fn read_from_path<P: AsRef<Path>>(p: P, configuration: Configuration) -> Res
/// Imports a `Model` from a zip file containing the
/// [GTFS](https://gtfs.org/reference/static).
///
/// The `config_path` argument allows you to give a path to a file
/// containing a json representing the contributor and dataset used
/// for this GTFS. If not given, default values will be created.
/// The `Configuration` is used to control various parameters during the import.
pub fn read_from_zip<P: AsRef<Path>>(p: P, configuration: Configuration) -> Result<Model> {
let reader = std::fs::File::open(p.as_ref())?;
let mut file_handler = read_utils::ZipHandler::new(reader, p)?;
read(&mut file_handler, configuration)
}

/// Imports a `Model` from an object implementing `Read` and `Seek` and containing the
/// [GTFS](https://gtfs.org/reference/static).
///
/// The `prefix` argument is a string that will be prepended to every
/// identifiers, allowing to namespace the dataset. By default, no
/// prefix will be added to the identifiers.
pub fn read_from_zip<P: AsRef<Path>>(path: P, configuration: Configuration) -> Result<Model> {
let mut file_handler = read_utils::ZipHandler::new(path)?;
/// This method makes it possible to read from a variety of sources like read a GTFS
/// from the network.
///
/// ```
// let url = "http://some_url/gtfs.zip";
// let resp = reqwest::blocking::get(url)?; // or async call
// let data = std::io::Cursor::new(resp.bytes()?.to_vec());
// let model = transit_model::gtfs::from_read(data, &url, configuration)?;
/// ```
///
/// The `source_name` is needed to have nicer error messages.
/// The `Configuration` is used to control various parameters during the import.
pub fn from_read<R>(reader: R, source_name: &str, configuration: Configuration) -> Result<Model>
where
R: std::io::Seek + std::io::Read,
{
let mut file_handler = read_utils::ZipHandler::new(reader, &source_name)?;
read(&mut file_handler, configuration)
}

Expand Down
32 changes: 19 additions & 13 deletions src/read_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ use crate::{
use failure::{format_err, ResultExt};
use log::info;
use serde::Deserialize;
use std::collections::BTreeMap;
use std::fs::File;
use std::path;
use std::path::{Path, PathBuf};
use std::{collections::BTreeMap, io::Read};
use std::{fs::File, io::Seek};
use typed_index_collection::{CollectionWithId, Id};

#[derive(Deserialize, Debug)]
Expand Down Expand Up @@ -97,7 +97,7 @@ pub(crate) trait FileHandler
where
Self: std::marker::Sized,
{
type Reader: std::io::Read;
type Reader: Read;

fn get_file_if_exists(self, name: &str) -> Result<(Option<Self::Reader>, PathBuf)>;

Expand Down Expand Up @@ -142,24 +142,26 @@ impl<'a, P: AsRef<Path>> FileHandler for &'a mut PathFileHandler<P> {
/// Unlike ZipArchive, it gives access to a file by its name not regarding its path in the ZipArchive
/// It thus cannot be correct if there are 2 files with the same name in the archive,
/// but for transport data if will make it possible to handle a zip with a sub directory
pub(crate) struct ZipHandler {
archive: zip::ZipArchive<File>,
pub(crate) struct ZipHandler<R: Seek + Read> {
archive: zip::ZipArchive<R>,
archive_path: PathBuf,
index_by_name: BTreeMap<String, usize>,
}

impl ZipHandler {
pub(crate) fn new<P: AsRef<Path>>(path: P) -> Result<Self> {
let file = File::open(path.as_ref())?;
let mut archive = zip::ZipArchive::new(file)?;
impl<R> ZipHandler<R>
where
R: Seek + Read,
{
pub(crate) fn new<P: AsRef<Path>>(r: R, path: P) -> Result<Self> {
let mut archive = zip::ZipArchive::new(r)?;
Ok(ZipHandler {
index_by_name: Self::files_by_name(&mut archive),
archive,
archive_path: path.as_ref().to_path_buf(),
})
}

fn files_by_name(archive: &mut zip::ZipArchive<File>) -> BTreeMap<String, usize> {
fn files_by_name(archive: &mut zip::ZipArchive<R>) -> BTreeMap<String, usize> {
(0..archive.len())
.filter_map(|i| {
let file = archive.by_index(i).ok()?;
Expand All @@ -172,7 +174,10 @@ impl ZipHandler {
}
}

impl<'a> FileHandler for &'a mut ZipHandler {
impl<'a, R> FileHandler for &'a mut ZipHandler<R>
where
R: Seek + Read,
{
type Reader = zip::read::ZipFile<'a>;
fn get_file_if_exists(self, name: &str) -> Result<(Option<Self::Reader>, PathBuf)> {
let p = self.archive_path.join(name);
Expand Down Expand Up @@ -273,8 +278,9 @@ mod tests {

#[test]
fn zip_file_handler() {
let mut file_handler =
ZipHandler::new(PathBuf::from("tests/fixtures/file-handler.zip")).unwrap();
let p = "tests/fixtures/file-handler.zip";
let reader = File::open(p).unwrap();
let mut file_handler = ZipHandler::new(reader, p).unwrap();

{
let (mut hello, _) = file_handler.get_file("hello.txt").unwrap();
Expand Down

0 comments on commit b820be2

Please sign in to comment.