/// This assumes that the file is in the standard ZIP format as downloaded
/// from nemweb.com.au or FTP server.
/// This simply validates the file and stores which subfiles are present
/// but doesn't actually parse the data.
/// This is more memory efficient than using AemoFile and reading into memory
This assumes that the file is in the standard ZIP format as downloaded
from nemweb.com.au or FTP server.
This simply validates the file and stores which subfiles are present
but doesn't actually parse the data.
This is more memory efficient than using AemoFile and reading into memory
This assumes that the file is in the standard ZIP format as downloaded from nemweb . com . au or FTP server . This simply validates the file and stores which subfiles are present but doesn ' t actually parse the data . This is more memory efficient than using AemoFile and reading into memory
pub fn from_path(path: impl AsRef<std::path::Path>) -> Result<Self> {
        // baked in assumption that data is a ZIP and contains only one file.
        let f = fs::File::open(path.as_ref())?;
        let mut zip = zip::ZipArchive::new(f)?;
        let (header, keys) = {
            let inner_file = zip.by_index(0)?;
            let mut reader = csv::ReaderBuilder::new()
                .has_headers(false)
                .flexible(true)
                .from_reader(inner_file);
            let mut records = reader.records();
            let first = records.next().ok_or(Error::MissingHeaderRecord)??;
            let header: AemoHeader = first.deserialize(None).map_err(|cause| Error::CsvRowDe {
                cause,
                headings: None,
                data: first,
            })?;
            // placeholder
            let mut footer: Result<AemoFooter> = Err(Error::MissingFooterRecord);
            let mut keys: collections::HashMap<FileKey, csv::StringRecord> =
                collections::HashMap::new();
            let mut num_rows = 1;
            for record in records {
                let record = record?;
                match record.get(0) {
                    Some("D") => {
                        num_rows += 1;
                        let row_len = record.len();
                        if row_len < 5 {
                            return Err(Error::TooShortRow(row_len));
                        }
                    }
                    Some("I") => {
                        num_rows += 1;
                        let row_len = record.len();
                        if row_len < 5 {
                            return Err(Error::TooShortRow(row_len));
                        }
                        let key = FileKey {
                            data_set_name: record[1].to_string(),
                            table_name: match &record[2] {
                                "" => None,
                                otherwise => Some(otherwise.to_string()),
                            },
                            version: record[3].parse()?,
                        };
                        let lowercased = record.iter().map(|v| v.to_lowercase()).collect();
                        keys.insert(key, lowercased);
                    }
                    Some("C") => {
                        num_rows += 1;
                        footer = record.deserialize(None).map_err(|cause| Error::CsvRowDe {
                            cause,
                            headings: None,
                            data: record,
                        });
                    }
                    Some(t) => return Err(Error::UnexpectedRowType(t.into())), //unexpected row, as correct files only have "C", "I" and "D"
                    None => return Err(Error::EmptyRow),
                }
            }
            let expected_line_count = footer?.line_count_inclusive;
            if num_rows != expected_line_count {
                return Err(Error::IncorrectLineCount {
                    got: num_rows,
                    expected: expected_line_count,
                });
            }
            (header, keys)
        };
        Ok(AemoDiskFile {
            header,
            handle: zip,
            keys,
        })
    }
pub fn from_path ( path : impl AsRef < std :: path :: Path > ) -> Result < Self > { // baked in assumption that data is a ZIP and contains only one file. let f = fs :: File :: open ( path . as_ref ( ) ) ? ; let mut zip = zip :: ZipArchive :: new ( f ) ? ; let ( header , keys ) = { let inner_file = zip . by_index ( 0 ) ? ; let mut reader = csv :: ReaderBuilder :: new ( ) . has_headers ( false ) . flexible ( true ) . from_reader ( inner_file ) ; let mut records = reader . records ( ) ; let first = records . next ( ) . ok_or ( Error :: MissingHeaderRecord ) ? ? ; let header : AemoHeader = first . deserialize ( None ) . map_err ( | cause | Error :: CsvRowDe { cause , headings : None , data : first , } ) ? ; // placeholder let mut footer : Result < AemoFooter > = Err ( Error :: MissingFooterRecord ) ; let mut keys : collections :: HashMap < FileKey , csv :: StringRecord > = collections :: HashMap :: new ( ) ; let mut num_rows = 1 ; for record in records { let record = record ? ; match record . get ( 0 ) { Some ( " " ) => { num_rows += 1 ; let row_len = record . len ( ) ; if row_len < 5 { return Err ( Error :: TooShortRow ( row_len ) ) ; } } Some ( " " ) => { num_rows += 1 ; let row_len = record . len ( ) ; if row_len < 5 { return Err ( Error :: TooShortRow ( row_len ) ) ; } let key = FileKey { data_set_name : record [ 1 ] . to_string ( ) , table_name : match & record [ 2 ] { " " => None , otherwise => Some ( otherwise . to_string ( ) ) , } , version : record [ 3 ] . parse ( ) ? , } ; let lowercased = record . iter ( ) . map ( | v | v . to_lowercase ( ) ) . collect ( ) ; keys . insert ( key , lowercased ) ; } Some ( " " ) => { num_rows += 1 ; footer = record . deserialize ( None ) . map_err ( | cause | Error :: CsvRowDe { cause , headings : None , data : record , } ) ; } Some ( t ) => return Err ( Error :: UnexpectedRowType ( t . into ( ) ) ) , //unexpected row, as correct files only have "C", "I" and "D" None => return Err ( Error :: EmptyRow ) , } } let expected_line_count = footer ? . line_count_inclusive ; if num_rows != expected_line_count { return Err ( Error :: IncorrectLineCount { got : num_rows , expected : expected_line_count , } ) ; } ( header , keys ) } ; Ok ( AemoDiskFile { header , handle : zip , keys , } ) }
This assumes that the file is in the standard ZIP format as downloaded
from nemweb.com.au or FTP server.
This assumes that the file is in the standard ZIP format as downloaded from nemweb . com . au or FTP server .
path
impl AsRef<std::path::Path>