How to create a generic reader

Hi,

I again need some newbie help needed here :slight_smile: It is probably a trivial problem but i am struggling with it.

so what i want to do is to construct an object that accepts a file during the construction phase and then determines if a file is raw or compressed and than accordingly creates a reader which i can use to move within a file. (actually i am not quite sure if this is a good object design or not, thus any reasons why i should move this into an object method is more than appreciated).

The way i envisioned this process is to pass a file path during object constriction phase and read initial magic bytes to evaluate whether a file is zipped or not and then accordingly create a reader.

U can see my attempt to achieve this below:

use flate2::read::MultiGzDecoder;
use std::env::args;
use std::io::{Cursor, Read};
use std::path::Path;

struct ObjA<R: Read> {
    reader: R,
}

impl<'a, R: 'a + Read> ObjA<R> {
    pub fn new(reader: R) -> ObjA<R> {
        ObjA { reader: reader }
    }
}

struct ObjB {
    parser: ObjA<Box<dyn Read>>,
}

impl ObjB {
    pub fn new<T: AsRef<Path>>(path: Option<T>) -> Self {
        let mut reader: Box<dyn Read> = match path {
            None => Box::new(std::io::stdin()),
            Some(x) => Box::new(std::fs::File::open(x).unwrap()),
        };

        let mut magic_bytes = [0u8; 4];
        reader.read_exact(&mut magic_bytes).unwrap();
        let mut reader = Cursor::new(magic_bytes.to_vec()).chain(reader);
        let parser = if &magic_bytes[..2] == b"\x1f\x8b" {
            let reader = MultiGzDecoder::new(reader);
            ObjA::new(reader)
        } else {
            ObjA::new(reader)
        };

        ObjB { parser: parser }
    }
}

fn main() {
    let filename = args().nth(1);
    println!("{:?}", &filename);
    let path = match filename.as_ref().map(String::as_ref) {
        None | Some("-") => None,
        Some(name) => Some(name),
    };

    let obj: ObjB = ObjB::new(path);
}

The reason i "captured ObjA" within ObjB is because I m trying to replicate the real case scenario as closely i can with this toy example so feel free to ignore nesting of the two if this is annoying to read.

Error i get is what i consider to be a classic for me by now :

error[E0308]: `if` and `else` have incompatible types
  --> src/main.rs:34:13
   |
30 |           let parser = if &magic_bytes[..2] == b"\x1f\x8b" {
   |  ______________________-
31 | |         let reader = MultiGzDecoder::new(reader);
32 | |             ObjA::new(reader)
   | |             ----------------- expected because of this
33 | |         } else {
34 | |             ObjA::new(reader)
   | |             ^^^^^^^^^^^^^^^^^ expected struct `flate2::read::MultiGzDecoder`, found struct `std::io::Chain`
35 | |         };
   | |_________- `if` and `else` have incompatible types
   |
   = note: expected type `ObjA<flate2::read::MultiGzDecoder<std::io::Chain<_, Box<dyn std::io::Read>>>>`
            found struct `ObjA<std::io::Chain<_, Box<dyn std::io::Read>>>`



Any idea on how to achieve my initial intent (read magic bytes, evaluate them and create a generic reader that i can use to read both type of files: raw and compressed)

Thank you !!!

You need to put them in a box.

1 Like

In more detail, just like you got a Box<dyn Read> at the top:

        let mut reader: Box<dyn Read> = match path {
            None => Box::new(std::io::stdin()),
            Some(x) => Box::new(std::fs::File::open(x).unwrap()),
        };

You need to get a Box<dyn Read> to put into ObjA:

        let reader: Box<dyn Read> = if &magic_bytes[..2] == b"\x1f\x8b" {
            Box::new(MultiGzDecoder::new(reader))
        } else {
            Box::new(reader)
        };

And then do so.

        ObjB { parser: ObjA::new(reader) }

Playground.

I didn't tackle this part, but instead of that chain business, you can seek to the beginning of the file.

Seams i do not understand Boxing very well. Thank you and sorry for not making the example more concise

Thnx :slight_smile:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.