Compressing and decompressing a tar.gz file - rust

I'm trying to compress a directory into a tar.gz file and decompressing it after using Rust.
I'm using the crates tar = "0.4.35" and flate2 = "1.0.20". I'm on Windows.
My code is almost identical to the examples here.
let tar = File::create("a.tar.gz").unwrap();
let enc = GzEncoder::new(tar, Compression::default());
let mut a = tar::Builder::new(enc);
a.append_dir_all("", "in").unwrap();
let tar = File::open("a.tar.gz").unwrap();
let dec = GzDecoder::new(tar);
let mut a = Archive::new(dec);
a.unpack("out").unwrap();
I'm always getting an error when decompressing:
thread 'main' panicked at 'called Result::unwrap() on an Errvalue: Custom { kind: InvalidInput, error: TarError { desc: "failed to iterate over archive", io: Custom { kind: InvalidInput, error: "invalid gzip header" } } }', src\main.rs:21:26
I can open the generated tar.gz in 7-Zip without a problem.
What am I doing wrong or is it an issue with the crates?

I found the issue!
Apparently there are reading and writing encoders and decoders in flate2. They both have the same name so you can't distinguish them easily when importing. You have to be careful importing the right ones or you'll get strange errors.
In my example I have to import:
use flate2::write::{GzEncoder};
use flate2::read::{GzDecoder};

Related

Unable to override temporary file with `tempfile`

Getting an error when creating a tempfile for socket use.:
Error: Custom { kind: AlreadyExists, error: PathError { path: "/tmp", err: Custom { kind: AlreadyExists, error: "too many temporary files exist" } } }
use tempfile::{tempfile, Builder, NamedTempFile};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let file = Builder::new().prefix("testsock").rand_bytes(0).tempfile()?;
Ok(())
}
Is there any way I can override the file?
The two many files exist error occurs if the file it is trying to create already exists. You can solve this error by deleting the files in your temp dir. In your case this is probably in /tmp/testsock/, you may also need to remove the folder testsock if just deleting the files does not work.
The default temp file will be used if .tempfile()
However you can specify the tempfile if you use the following.
let tmp = Builder::new()
.prefix("example")
.rand_bytes(0)
.tempfile_in("./")?;
The documentation for the tempfile builder can be found:
https://docs.rs/tempfile/latest/tempfile/struct.Builder.html

Hitting upload limit on crates.io on a sys crate

I'm trying to publish a sys crate for libvmaf. Unfortunately I can not simply dynamically link to libvmaf because it's not distributed anywhere and I need to build it from source and include it in my library. Unfortunately libvmaf is absolutely huge and my .rlib file is ending up at 1.4 megabytes which is over the upload limit for crates.io. Am I boned here?
Here's my build.rs file
use meson_next;
use std::env;
use std::fs::canonicalize;
use std::path::PathBuf;
fn main() {
//env::set_var("RUST_BACKTRACE", "1");
let build_dir = PathBuf::from(env::var("OUT_DIR").unwrap()).join("build");
let lib_dir = build_dir.join("src");
let build_dir_str = build_dir.to_str().unwrap();
let lib_dir_str = lib_dir.to_str().unwrap();
meson_next::build("vmaf/libvmaf", build_dir_str);
println!("cargo:rustc-link-lib=static=vmaf");
println!("cargo:rustc-link-search=native={lib_dir_str}");
// Path to vendor header files
let headers_dir = PathBuf::from("vmaf/libvmaf/include");
let headers_dir_canonical = canonicalize(headers_dir).unwrap();
let include_path = headers_dir_canonical.to_str().unwrap();
// Generate bindings to libvmaf using rust-bindgen
let bindings = bindgen::Builder::default()
.header("vmaf/libvmaf/include/libvmaf/libvmaf.h")
.clang_arg(format!("-I{include_path}"))
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.generate()
.expect("Unable to generate bindings");
// Write bindings to build directory
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
}
In general you should not be including compiled libraries in your package. Include the source code, and have your build script perform the build.
This will usually result in a smaller package, and also means that your package works on any target architecture (that is supported by the library).

How to decode nested / imported protobuf using protoc command line or python

I am debugging the api response which in protobuf format.
I know that I can using the protoc command to make the response readable:
protoc --decode api.Response response.proto < res.bin
It works on the simple files.
However, I find that some data is wrapped into the response protobuf like:
// response.proto
...
...
message Response {
int64 status = 1;
google.protobuf.Any detail = 2
...
}
// login.proto
...
...
...
// Import UserDatas protobufs...
message LoginResponse {
string token = 1;
UserDetail user = 2;
UserLastLogin lastlogin = 3;
...
...
...
}
then I run the command
protoc --decode api.Response response.proto < res.bin
I got
status: 200
result {
type_url: "........login",
value: "(very long encoded text without formatting)"
}
I know that I can edit the proto file to change the Any to the LoginResponse,and decode it. However I have multiple API protobufs in this format, I cannot edit all the files each time I want to view.
I also ask the developer who using the response, but he told me he just checking the type_url and using different function to parse the value data. it looks not good (need to write many functions to handle it, generate the files for all proto and re-generate the library when the proto is updated).
I believe that there are better solutions.
Is there a simpler way to view the response without editing the proto file? (Hope that is able to do on protoc command or python)

How to read file from another(nested) folder?

This is my folder structure.
src
├── main.rs
└── assembly_files
└── toyRISC.asm
Why is this not enough or where is the error?
I tried with raw &str:
let filename = "./assembly_files/toyRISC.asm";
let result = fs::read_to_string(path).expect("Please provide valid file name");
Also tried using Path and OsStr , shown in code below.
// main.rs
use std::{ffi::OsStr, fs, path::Path};
fn main() {
let filename = "assembly_files/toyRISC.asm";
let path = Path::new("./assembly_files/toyRISC.asm");
println!("{:?}", path);
let result = fs::read_to_string(path).expect("Please provide valid file name");
}
This is the error
thread 'main' panicked at 'Please provide valid file name: Os { code: 3, kind: NotFound, message: "The system cannot find the path specified."
}', src\main.rs:6:43
When you create a relative Path to a file, e.g. Path::new("./assembly_files/toyRISC.asm"); the path is relative to the folder from which you call the executable. Note that this isn't necessarily the folder where the executable is stored!
If you simply run cargo run at the root of your project to run it, then you need to change the path to Path::new("./src/assembly_files/toyRISC.asm");

Command panics with process cannot access the file already being used

I'm trying to spawn a CLI in my Rust code via Comand::new. CLI file is extracting from binary files to exe file and then run with Command::new. But It gives 'ERROR: Os { code: 32, kind: Other, message: "The process cannot access the file because it is being used by another process." }' error.
let taskmgr_pid = get_pid_by_name("Taskmgr.exe");
let process_hide = asset::Asset::get("cli.exe").unwrap();
let file_path = "C:\\filepathhere\\cli.exe";
let mut file = File::create(file_path.to_string()).expect("Couldn't create file");
file.write_all(&process_hide);
let res = Command::new(file_path)
.arg(taskmgr_pid.to_string())
.output()
.expect("ERROR");
println!("PID: {}", taskmgr_pid);
println!("{:?}", res);
It's because you didn't close file prior to executing the command. The easiest way to resolve the issue, is to simply drop(file); prior to Command::new().
let mut file = File::create(file_path).expect("unable to create file");
file.write_all(&process_hide).expect("unable to write");
drop(file);
let res = Command::new(file_path)
.arg(taskmgr_pid.to_string())
.output()
.expect("ERROR");

Resources