Change relative location for PathBuf - rust

I have a few PathBufs in my Rust application:
let mut dog_path = PathBuf::from("./animals/dog.png");
let mut cow_path = PathBuf::from("./animals/bovine/cow.jpg");
How could I change these PathBufs so that they're being referred to from the ./animals directory?
// an operation on dog_path
// same operation on cow_path
assert_eq!(PathBuf::from("./dog.png"), dog_path);
assert_eq!(PathBuf::from("./bovine/cow.jpg"), cow_path);

I think you want Path::strip_prefix:
let dog_path = PathBuf::from("./animals/dog.png");
let cow_path = PathBuf::from("./animals/bovine/cow.jpg");
let dog_path_rel = dog_path.strip_prefix("./animals").unwrap();
let cow_path_rel = cow_path.strip_prefix("./animals").unwrap();
assert_eq!(Path::new("dog.png"), dog_path_rel);
assert_eq!(Path::new("bovine/cow.jpg"), cow_path_rel);
But that won't include the leading ./. If that's important to you, you can add it manually:
let dog_path_prefixed = Path::new("./").join(dog_path_rel);
let cow_path_prefixed = Path::new("./").join(cow_path_rel);
assert_eq!(PathBuf::from("./dog.png"), dog_path_prefixed);
assert_eq!(PathBuf::from("./bovine/cow.jpg"), cow_path_prefixed);
playground
Note that strip_prefix returns a Result, meaning it could fail if the path doesn't begin with the given prefix. You may want to handle this case instead of unwraping the result (causing your program to exit with a panic), or you may want to use .expect("your message here") instead to provide a meaningful error message.

If you want a general solution you could look at relative-path crate. It looks like it provide the functionality you want.
use std::path::PathBuf;
use relative_path::RelativePath;
fn main() {
let dog_path = PathBuf::from("./animals/dog.png");
let cow_path = PathBuf::from("./animals/bovine/cow.jpg");
let dog_path = RelativePath::from_path(&dog_path).unwrap();
let cow_path = RelativePath::from_path(&cow_path).unwrap();
let animals_dir = RelativePath::new("./animals");
let dog_path = animals_dir.relative(&dog_path).to_path(".");
let cow_path = animals_dir.relative(&cow_path).to_path(".");
assert_eq!(PathBuf::from("./dog.png"), dog_path);
assert_eq!(PathBuf::from("./bovine/cow.jpg"), cow_path);
}
This is a quick draft, but it shows how to do in a generic way what you are trying to accomplish. I think it could be further optimized, but I literally found this crate 10 minutes ago.

Related

error handling when unwrapping several try_into calls

I have a case where I need to parse some different values out from a vector.
I made a function for it, that returns a option, which either should give a option or a None, depending on whether the unwrapping succeeds.
Currently it looks like this:
fn extract_edhoc_message(msg : Vec<u8>)-> Option<EdhocMessage>{
let mtype = msg[0];
let fcnt = msg[1..3].try_into().unwrap();
let devaddr = msg[3..7].try_into().unwrap();
let msg = msg[7..].try_into().unwrap();
Some(EdhocMessage {
m_type: mtype,
fcntup: fcnt,
devaddr: devaddr,
edhoc_msg: msg,
})
}
But, I would like to be able to return a None, if any of the unwrap calls fail.
I can do that by pattern matching on each of them, and then explicitly return a None, if anything fails, but that would a lot of repeated code.
Is there any way to say something like:
"if any of these unwraps fail, return a None?"
This is exactly what ? does. It's even shorter than the .unwrap() version:
fn extract_error_message(msg: Vec<u8>) -> Option<EdhocMessage> {
let m_type = msg[0];
let fcntup = msg[1..3].try_into().ok()?;
let devaddr = msg[3..7].try_into().ok()?;
let edhoc_msg = msg[7..].try_into().ok()?;
Some(EdhocMessage {
m_type,
fcntup,
devaddr,
edhoc_msg
})
}
See this relevant part of the Rust Book.

is it possible using rust to generate jsx AST?

I'm not familiar with rust and trynna using swc to replace the babel
but I found #swc/core doesn't offer the api to generate code AST, so I want to write one using rust, is it possible to generate jsx ast?
Here is a good place to start: https://github.com/swc-project/swc/blob/main/crates/swc_ecma_parser/examples/typescript.rs
but link can get invalid so I also pasted the code:
use swc_common::{
self,
errors::{ColorConfig, Handler},
sync::Lrc,
FileName, SourceMap,
};
use swc_ecma_parser::{lexer::Lexer, Capturing, Parser, StringInput, Syntax};
fn main() {
let cm: Lrc<SourceMap> = Default::default();
let handler = Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(cm.clone()));
// Real usage
// let fm = cm
// .load_file(Path::new("test.js"))
// .expect("failed to load test.js");
let fm = cm.new_source_file(
FileName::Custom("test.js".into()),
"interface Foo {}".into(),
);
let lexer = Lexer::new(
Syntax::Typescript(Default::default()),
Default::default(),
StringInput::from(&*fm),
None,
);
let capturing = Capturing::new(lexer);
let mut parser = Parser::new_from(capturing);
for e in parser.take_errors() {
e.into_diagnostic(&handler).emit();
}
let _module = parser
.parse_typescript_module()
.map_err(|e| e.into_diagnostic(&handler).emit())
.expect("Failed to parse module.");
println!("Tokens: {:?}", parser.input().take());
}
I never really worked or cared about this. All it took was 5min of browsing the repository, all the resources are there but if you are new to rust, I recommend the rust book first.

reqwest send multipart form with very large attachment

As this answer explains, it's possible to use Body::wrap_stream(read_stream) to POST the contents of a file without first reading the entire contents into memory.
How can we do the same thing as part of a reqwest::multipart::Form? The following code fails with the error the trait `From<&mut dyn Stream<Item = std::result::Result<Vec<u8>, std::io::Error>>>` is not implemented for `Body` .
let metadata_json = "{ \"file_owner\": \"bob smith\" }";
let metadata_part = reqwest::multipart::Part::text(metadata_json);
let read_stream : Stream<Item = std::io::Result<Vec<u8>> = my_file_stream;
let stream_part = reqwest::multipart::Part::stream(read_stream);
let multipart_form = reqwest::multipart::Form::new()
.part("metadata", metadata_part)
.part("file", stream_part);
I tried supplying an implementation of From that just calls Body::wrap_stream but it's forbidden since neither From nor Body is defined in my own code.
I believe you need to use Body::wrap_stream like this:
let stream_part = reqwest::multipart::Part::stream(Body::wrap_stream(read_stream));

How can I pull data out of an Option for independent use?

Is there a way to 'pull' data out of an Option? I have an API call that returns Some(HashMap). I want to use the HashMap as if it weren't inside Some and play with the data.
Based on what I've read, it looks like Some(...) is only good for match comparisons and some built-in functions.
Simple API call pulled from crate docs:
use std::collections::HashMap;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let resp = reqwest::blocking::get("https://httpbin.org/ip")?
.json::<HashMap<String, String>>()?;
println!("{:#?}", resp.get("origin"));
Ok(())
}
Result:
Some("75.69.138.107")
if let Some(origin) = resp.get("origin") {
// use origin
}
If you can guarantee that it's impossible for the value to be None, then you can use:
let origin = resp.get("origin").unwrap();
Or:
let origin = resp.get("origin").expect("This shouldn't be possible!");
And, since your function returns a Result:
let origin = resp.get("origin").ok_or("This shouldn't be possible!")?;
Or with a custom error type:
let origin = resp.get("origin").ok_or(MyError::DoesntExist)?;
The most common way is with if let:
if let Some(origin) = resp.get("origin") {
origin.do_stuff()
}
For more fine grained control, you can use pattern matching:
match resp.get("origin") {
Some(origin) => origin.do_stuff(),
None => panic!("origin not found!")
}
You could also use unwrap, which will give you the underlying value of the option, or panic if it is None:
let origin = resp.get("origin").unwrap();
You can customize the panic message with expect:
let origin = resp.get("origin").expect("Oops!");
Or compute a default value with unwrap_or:
let origin = resp.get("origin").unwrap_or(&String::from("192.168.0.1"));
You can also return an error instead of panicking:
let origin = resp.get("origin").ok_or(Error::UnknownOrigin)?;
Your options are a plenty.
if let Some(origin) = resp.get("origin") {
// do stuff using origin
}
origin = resp.get("origin").unwrap()
// will panic if None
resp.get("origin").map(|origin| {
// do stuff using inner value, returning another option
})
resp.get("origin").and_then(|origin| {
// same as map but short-circuits if there is no inner value
})

Rust protobuf serialization is all messed up

I am using rust-protobuf version 2.4
I have following code
let mut msg = vec![];
let mut str = protobuf::CodedOutputStream::vec(&mut msg);
let mut rmsg = user_manager::user::user_data::new();
rmsg.set_id("1234".into());
rmsg.set_nick("test".into());
str.write_message(1, &rmsg).unwrap();
str.flush().unwrap();
println!("{:?}", msg);
let test: register_msg = protobuf::parse_from_bytes(&msg[..]).unwrap();
println!("serialized: {:?}\noriginal: {:?}", test, rmsg);
and the relevant proto description is as follows
message user_data{
string id = 1; //required
string nick = 2;
string theme = 3;
string admin_id = 4;
string lang = 5;
double credit = 6; //required
double bonus_credit = 7; //required
};
and what i get as a result is all messed up
First of all, why do I need to enter a number if im serializing the entire message? that's a really weird design.
Secondly, the first field as you may see in the proto file is the id but output from serialization shows the protobuf package serialized everything into the nick field which is the second field.
Am I doing something wrong or might this be a bug in the library?
UPDATE 1:
I have changed write_msg to write_to_bytes
this is how my code looks now.
let mut rmsg = user_manager::user::user_data::new();
rmsg.set_id("1234".into());
rmsg.set_nick("test".into());
let msg = rmsg.write_to_bytes().unwrap();
println!("{:?}", msg);
println!("{:?}", &msg[..]);
let test: register_msg = protobuf::parse_from_bytes(&msg).unwrap();
println!("serialized: {:?}\noriginal: {:?}", test, rmsg);
and still, the output is all messed up.
You can use Message::write_to_bytes to serialize a message that can be parsed with parse_from_bytes.
Also, make sure that you are deserializing the same type that you serialized.
Here's an example program that works correctly:
mod protos;
use protobuf::Message;
use protos::user_data::user_data;
fn main() {
let mut rmsg = user_data::new();
rmsg.set_id("1234".into());
rmsg.set_nick("test".into());
let msg = rmsg.write_to_bytes().unwrap();
println!("{:?}", msg);
let test: user_data = protobuf::parse_from_bytes(&msg).unwrap();
println!("serialized: {:?}\noriginal: {:?}", test, rmsg);
}
This line:
str.write_message(1, &rmsg).unwrap();
writes the message rmsg as a submessage with field id 1. So the fields end up one nesting level deeper, as if your .proto was:
message wrappermsg {
user_data msg = 1;
}
The correct method is:
rmsg.write_to(&mut str);

Resources