I have a problme serializing/desserializing an struct with serde_json in rust, i had used the impl's (dont used derive) that are on serde_json:ser and serde_json:de, but something is happening when i try to write it in a file, i hava thread (this is the unique thread that open that file) that is in a loop saving some data to a file named "config.json", but some times it saves an extra '}' at the end of the file and i don't know why.
{"Alive":true,...,"Threads":20}}
pub fn write_config(data: &Struct_object) -> () {
let file = OpenOptions::new().read(true).write(true).create(true).open("config.json");
let mut refval: Struct_object = data.clone();
match to_string::<Struct_object>(&mut refval) {
Ok(u) => {
match file {
Ok(mut s) => match s.write(u.as_bytes()) {
Ok(_) => (),
Err(u) => warn!("Config.json couldn't be writen, data: {{ {} }}, Error: {{ {} }}", data, u),
},
Err(t) => {
warn!("Config.json couldn't be writen, data: {{ {} }}, Error: {{ {} }}", data, t);
},
}
},
Err(v) => warn!("Config.json couldn't be writen, data: {{ {} }}, Error: {{ {} }}", data, v),
}
}
pub fn read_config() -> Struct_object{
if file_exist() {
let file = OpenOptions::new().read(true).open("config.json");
let mut buf = String::new();
match file {
Ok(mut s) => {
let _ = s.read_to_string(&mut buf);
match from_str::<Struct_object>(&buf) {
Ok(t) => t,
Err(u) => {
warn!("config.json couldn't be loaded, loading default configuration, Error: {}", u);
Struct_object::default()
}
}
},
Err(v) => {warn!("config.json couldn't be readed, loading default configuration, Error: {}", v);
Struct_object::default()},
}
} else {
//write_config(&Struct_object::default());
Struct_object::default()
}
}
there is the code to read and write the data, and the loop is something like this:
pub fn daemon_variable_set(val: Struct_object) {
loop {
let data = read_config();
val.write_rpc_url(data.read_rpc_url());
thread::sleep(Duration::from_secs(1));
write_config(&val);
thread::sleep(Duration::from_secs(1));
if !val.read_alive() {
break;
}
}
info!("Thread variable_set stop");
}
the information in the Struct_object are Arc<RwLock>, but in the serde is saving just the information of the primitive, and at read, i create the Arc to encapsulate the Primitives, so, i dont understand why is adding this extra '}' at the end of the line
Also i tried with the next, but was worst
use serde_json::de::from_reader;
use serde_json::ser::to_writer;
You're overwriting the config.txt file but you aren't truncating it before writing to it, so data from previous writes can be seen if the data being written is shorter.
Before:
{"foo":12}
After writing {"foo":5} without truncating the file (since its 1 character shorter):
{"foo":5}}
You should simply use File::create:
Opens a file in write-only mode.
This function will create a file if it does not exist, and will truncate it if it does.
It will internally use .write(true).create(true).truncate(true) which is usually what you want when writing to a file like this.
You are missing a call to std::fs::OpenOptions::truncate(true) in your write_config() function.
Without that, the file will be written to, overwriting the previous content, but leaving the previous content in place. If the file's conent from the previous iteration of write_config happens to be longer by exactly one character (for instance: some value changes from 123 to 99), that will show up as an "extra" } during the next read.
Related
I wrote this code and it works. But I have a custom class for outputting logs to stdout. I want to call my logging function if I get an Error instead of panic! macro.
fn create_logfile() -> File {
let timestamp = Log::format_date(chrono::Local::now());
let filename = format!("{}.log", timestamp);
let logfile = match File::create(filename) {
Ok(file) => file,
Err(error) => {
panic!("There was a problem creating the file: {:?}", error)
}
};
logfile
}
For example I want get something like that:
let logfile = match File::create(filename) {
Ok(file) => file,
Err(e) => {
Log::error("Log file creation failed, reason: {}", e);
process::exit(1)
}
};
But compiler says:
[E0308] `match` arms have incompatible types.
[Note] expected struct `File`, found `()
How can I solve this problem?
If I put the error data to stderr will it help?
Your revised example with std::process::exit() works for me: (link)
use chrono; // 0.4.23
use std::fs::File;
use log; // 0.4.17
struct Log { }
impl Log {
fn format_date(date: chrono::DateTime<chrono::offset::Local>) -> i64 {
return 0;
}
}
fn old_create_logfile() -> File {
let timestamp = Log::format_date(chrono::Local::now());
let filename = format!("{}.log", timestamp);
let logfile = match File::create(filename) {
Ok(file) => file,
Err(error) => {
panic!("There was a problem creating the file: {:?}", error)
}
};
logfile
}
fn new_create_logfile() -> File {
let timestamp = Log::format_date(chrono::Local::now());
let filename = format!("{}.log", timestamp);
let logfile = match File::create(filename) {
Ok(file) => file,
Err(e) => {
// Instead of using `Log::error`, we'll use `log::error!` for show.
log::error!("Log file creation failed, reason: {}", e);
// This compiles.
std::process::exit(1)
}
};
logfile
}
fn main() {
new_create_logfile();
}
Normally, you need to make sure the return types of all match arms have the same type -- here, you are returning std::fs::File under the Ok branch, so the Err branch can "escape" the requirement by returning ! (pronounced "never") (link).
Since computation never returns from the std::process::exit() and its return type is marked as !, it's passes the type-checking stage.
For a toy example, say I'd like to make a cache for files on disk and log its output. Here's what I hoped would work:
struct Cache {
data: HashMap<String, String>
}
impl Cache {
fn load(&mut self, filename: &str) -> String {
let result = match self.data.get(filename) {
Some(s) => s.clone(),
None => {
let s = fs::read_to_string(filename).expect("couldn't read");
self.data.insert(String::from(filename), s.clone());
return s;
}
};
println!("my result: {}", result);
return result;
}
}
But the None => {...} clause isn't executed like its own function, so its return exits the whole load function. I tried a couple different ways, but couldn't get multi-line match clauses to return a value. Is there any way to get this type of match working in rust?
rust version: 1.50.0
language edition: 2018
The final statement in a block is returned, that applies to any block, i.e. if you put s as the last line in the None match arm, it will be returned from that block.
You only need to use return when returning from a function or closure.
I'm trying to read a file, decrypt it, and return the data. Because the file is potentially very big, I want to do this in a stream.
I cannot find a good pattern to implement the stream. I'm trying to do something like this:
let stream = stream::unfold(decrypted_init_length, |decrypted_length| async move {
if decrypted_length < start + length {
let mut encrypted_chunk = vec![0u8; encrypted_block_size];
match f.read(&mut encrypted_chunk[..]) {
Ok(size) => {
if size > 0 {
let decrypted = my_decrypt_fn(&encrypted_chunk[..]);
let updated_decrypted_length = decrypted_length + decrypted.len();
Some((decrypted, updated_decrypted_length))
} else {
None
}
}
Err(e) => {
println!("Error {}", e);
None
}
}
} else {
None
}
});
The problem is that f.read is not allowed in the above async closure with the following error:
89 | | match f.read(&mut encrypted_chunk[..]) {
| | -
| | |
| | move occurs because `f` has type `std::fs::File`, which does not implement the `Copy` trait
| | move occurs due to use in generator
I don't want to open f inside the closure itself. Is there any better way to fix this? I am OK with using a different crate or trait, or method (i.e. not stream::unfold).
I found a solution: using async-stream crate at here.
One of the reasons stream::unfold did not work for me is that the async move closure does not allow access mut variables outside, for example the f file handle.
Now with async-stream, I changed my code to the following, and it works: (note the yield added by this crate).
use async_stream::try_stream;
<snip>
try_stream! {
while decrypted_length < start + length {
match f.read(&mut encrypted_chunk[..]) {
Ok(size) =>
if size > 0 {
println!("read {} bytes", size);
let decrypted = my_decrypt_fn(&encrypted_chunk[..size], ..);
decrypted_length = decrypted_length + decrypted.len();
yield decrypted;
} else {
break
}
Err(e) => {
println!("Error {}", e);
break
}
}
}
}
UPDATE:
I found that async-stream has some limitations that I cannot ignore. I ended up implementing Stream directly and no longer using async-stream. Now my code looks like this:
pub struct DecryptFileStream {
f: File,
<other_fields>,
}
impl Stream for DecryptFileStream {
type Item = io::Result<Vec<u8>>;
fn poll_next(self: Pin<&mut Self>,
_cx: &mut Context<'_>) -> Poll<Option<io::Result<Vec<u8>>>> {
// read the file `f` of self and business_logic
//
if decrypted.len() > 0 {
Poll::Ready(Some(Ok(decrypted)))
} else {
Poll::Ready(None)
}
}
}
//. then use the above stream:
let stream = DecryptFileStream::new(...);
Response::new(Body::wrap_stream(stream))
stream::unfold is only for types that implement Stream, which in Rust is used exclusively for asynchronous programming. If you want to do synchronous reading, what you're calling a "stream" is tagged as implementing Read in Rust. Thus you can call Read::read() to read some data off the current position of your File (limited by the length of the buffer you pass in), and decrypt that data.
How to fix issue on a screenshot? I already tried to make it mutable, but that is not the point. What can it be, how to get rid of it? I will be thankful for the changes in the code.
screenshot:
(source: i.ibb.co)
let mut buf = vec![0 as u8; 4096];
for stream in listener.incoming() {
match stream {
Ok(mut stream) => {
match stream.read(&mut buf) {
Ok(size) => {
//Get List of names
let names: LinkedList<String> = serde_json::from_slice(&buf[..size])?;
for name in names.iter() {
if (*data).available.contains_key(&*name) {
//If file already exist just update Vec of IP
(*data)
.available
.get_mut(&*name)
.unwrap()
.push(stream.peer_addr().unwrap());
} else {
//In another case - adding file with first IP that share it
let mut v: Vec<SocketAddr> = Vec::new();
v.push(stream.peer_addr().unwrap());
(*data).available.insert(*name, v);
}
}
}
Err(_) => {
println!("An error occurred, {}", stream.peer_addr().unwrap());
}
}
}
Err(e) => {
println!("Error: {}", e);
}
}
}
Are you sure you want a LinkedList and not a Vec as output from your JSON parser? From the LinkedList docs:
It is almost always better to use Vec or VecDeque because array-based containers are generally faster, more memory efficient, and make better use of CPU cache.
To solve your problem, you should loop over names instead of names.iter(). This will make the list unusable after the for loop.
You will then have to remove the dereferences in your code, i.e. write &name instead of "&*name" and name instead of *name. However, you shouldn't have written &*name at all because the & and * in &*name are cancelling each other out.
fn lines_from_file<F>(filename: F) -> Result<io::Lines<BufReader<File>>, io::Error>
where
F: std::convert::AsRef<std::path::Path>,
{
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
fn main() {
let filename: &str = "input.pdl";
// This works fine
match lines_from_file(filename) {
Ok(lines) => {
for line in lines {
println!("{:?}", line);
},
}
Err(e) => println!("Error {:?}", e),
}
}
I'd like to use this instead:
if let lines = Ok(lines_from_file(filename)) {
for line in lines {
println!("{:?}", line);
}
} else {
println!("Error {:?}" /*what goes here?*/,)
}
But that gives an error:
| if let lines = Ok(lines_from_file(filename)) {
| ^^ cannot infer type for `E`
What is the idiomatic way of using an if-let binding when matching a Result and still being able to capture the error?
[...] using an if-let binding when matching a Result and still being able to capture the error?
This is fundamentally impossible with one if let. The if let construct's only purpose is to make life easier in the case where you only want to destructure one pattern. If you want to destructure both cases of a Result, you have to use match (or multiple if let or unwrap(), but this is not a good solution). Why you don't want to use match in the first place?
Regarding your compiler error: you added the Ok() on the wrong side:
if let Ok(lines) = lines_from_file(filename) { ... }
This is the correct way of using if let: the destructuring pattern to the left, the expression producing a value to the right.