How to retrieve data from RediSearch with Rust? - database

I'm trying to use Rust and get autocomplete data from RediSearch with the FT.SUGGET command but it ends up retrieving a None value even though when I run FT.SUGGET directly with the command prompt it properly gives responses. This is my code which format works perfectly well with sets and gets in Redis.
FT.SUGADD works properly in this as well. Thanks for the future help!
pub fn ft_sugadd(index: String, field: String) -> redis::RedisResult<()>
{
let client = redis::Client::open("redis://127.0.0.1/")?;
let mut con = client.get_connection()?;
redis::cmd("FT.SUGADD").arg(index).arg(field).arg("1".to_string()).query(&mut con)?;
Ok(())
}
pub fn ft_sugget(index: String, field: String) -> redis::RedisResult<String>
{
let client = redis::Client::open("redis://127.0.0.1/")?;
let mut con = client.get_connection()?;
let data_value = redis::cmd("FT.SUGGET").arg(index).arg(field).arg("fuzzy".to_string()).query(&mut con)?;
Ok(data_value)
}
fn main()
{
ft_sugadd("dictionary".to_string(), "Bob".to_string()).ok();
let remember = ft_sugget("dictionary".to_string(), "Bo".to_string()).ok();
println!("Hello {:?}", remember);
}

This question has been solved - the reason it wasn't working properly is that the function ft_sugget is defined to return Strings when it should in fact be returning a Vector of Strings.

Related

Rust Programming - Trying to compare user input to lines from a file

So I am trying to compare user input to the lines from a separate file name fruits.txt. I got it mostly working I believe, but I am running into this error:
error[E0658]: use of unstable library feature 'option_result_contains'
--> src/main.rs:19:20
|
19 | s if s.contains(&ask) => println!("{} is a fruit!", ask),
| ^^^^^^^^
|
= note: see issue #62358 <https://github.com/rust-lang/rust/issues/62358> for more information
For more information about this error, try `rustc --explain E0658`.
error: could not compile `learn_arrays` due to previous error
I have tried several types of ways to match it in rust and this is the closest where it doesn't complain that I am trying to match a string to whatever type lines is. here is what it looks like
use std::fs::File;
use std::io::{BufReader, BufRead, Error, stdin};
fn main() -> Result<(), Error>{
let path = "fruits.txt";
let input = File::open(path)?;
let buffered = BufReader::new(input);
let mut ask = String::new();
stdin()
.read_line(&mut ask)
.expect("Failed to read line");
let ask: String = ask.trim().parse().expect("Please type a valid string!");
for line in buffered.lines() {
match line {
s if s.contains(&ask) => println!("{} is a fruit!", ask),
_ => println!("{} is either not in the list or not a fruit", ask),
}
}
Ok(())
}
Is there a way where I can use the unstable feature or is there another better method to compare user input to lines from a file.
I was able to fix the issue my changing the part where I am attempting to match the input with:
let mut found = false;
println!("Result");
for line in buffered.lines() {
let s = line.unwrap();
if s.find(&ask).is_some() {
println!("{} is a fruit!", ask);
found = true;
break;
}
}
if !found {
println!("{} is either not in the list or not a fruit", ask)
}

Load file contents into a static array of bytes

I have a static array initialized with some constant value:
static PROG_ROM: [u8; 850] = [0x12, 0x1d, ...];
I would like to instead load at compile-time the contents of a file into it. Sounds like a job for std::include_bytes!, however, I have two problems with it:
The type of include_bytes!("foo.dat") is &[u8; 850] i.e. it is a reference. I need this to be a bonafide static array.
Even if there was an include_bytes_static! macro with type [u8;850], I would have to use it like this:
static PROG_ROM: [u8; 850] = include_bytes_static!("foo.dat");
I.e. I would have to hardcode the length of the file. Instead, I
would like to take the length from the length of the file contents.
So the ideal replacement for my code would be a macro to replace the whole definition, i.e. look something like this:
define_included_bytes!(PROG_ROM, "foo.dat")
and it would expand to
static PROG_ROM: [u8; 850] = [0x12, 0x1d, ...];
So how do I do this?
Use *include_bytes!(..) to get a [u8; _] instead of &[u8; _] (since arrays implement Copy), and use include_bytes!(..).len() (which is a const method) to specify the length of the array in the type:
static PROG_ROM: [u8; include_bytes!("foo.dat").len()] = *include_bytes!("foo.dat");
As Chayim Friedman pointed out you can easily define that proc macro yourself:
#[proc_macro]
pub fn define_included_bytes(token_stream: TokenStream) -> TokenStream {
let [ident, _comma, file] = &token_stream.into_iter().collect::<Vec<_>>()[..] else {
panic!("expected invocation: `define_included_bytes!(IDENTIFIER, \"file_name\");");
};
let file = file.to_string().trim_matches('\"').to_string();
let data: Vec<u8> = std::fs::read(&file).expect(&format!("File {:?} could not be read", file));
format!("const {ident}: [u8; {}] = {:?};", data.len(), data).parse().unwrap()
}
Obviously this is just a hacked together proof of concept and you should thouroughly check the tokens instead of just assuming they're correct.
Based on cafce25's answer, I ended up writing the following version using syn and quote:
extern crate proc_macro;
extern crate syn;
extern crate quote;
use proc_macro::TokenStream;
use syn::parse::{Parse, ParseStream, Result};
use syn::{parse_macro_input, Ident, Token, LitStr};
use quote::quote;
struct StaticInclude {
name: Ident,
filepath: String,
}
impl Parse for StaticInclude {
fn parse(input: ParseStream) -> Result<Self> {
let name: Ident = input.parse()?;
input.parse::<Token![=]>()?;
let filepath: String = input.parse::<LitStr>()?.value();
Ok(StaticInclude{ name, filepath })
}
}
#[proc_macro]
pub fn progmem_include_bytes(tokens: TokenStream) -> TokenStream {
let StaticInclude{ name, filepath } = parse_macro_input!(tokens as StaticInclude);
let data: Vec<u8> = std::fs::read(&filepath).expect(&format!("File {:?} could not be read", filepath));
let len = data.len();
TokenStream::from(quote! {
#[link_section = ".progmem.data"]
static #name: [u8; #len] = [#(#data),*];
})
}
(never mind the link_section attribute, that's an AVR-ism).
This works well, except Cargo is not tracking the dependency on the external file, so if its content changes, the program using progmem_include_bytes! is not recompiled by cargo build.

rust fltk loop callback error use of moved value: `frame` value moved into closure here, in previous iteration of loop

I am having a bit of a problem with constantly updating an fltk frame. I have tried to google a few things but anything that should have worked from other answers (e.g: using & / &mut but it doesnt seem to work)
fn main() {
println!("{}", &time_tick());
let app = app::App::default();
let mut wind = Window::default()
.with_size(500, 600)
.center_screen()
.with_label("Counter");
let mut frame = Frame::default()
.with_size(100, 40)
.center_of(&wind)
.with_label(&time_tick());
wind.make_resizable(true);
wind.end();
wind.show();
/* Event handling */
thread::spawn(move || {
app.run().unwrap();
});
loop {
wind.set_callback( move |_| frame.set_label(&time_tick()));
}
}
Here is the rust playground version
Updates can be performed in the wait loop.
fn main() -> Result<(), Box<dyn std::error::Error>> {
let app = app::App::default();
let mut wind = Window::default().with_size(400, 400).with_label("Counter");
let mut frame = Frame::default()
.with_size(200, 300)
.center_of(&wind)
.with_label(&time_tick());
wind.end();
wind.show();
while app.wait() {
frame.set_label(&time_tick());
wind.redraw();
}
Ok(())
}
I recommend reading the documentation, I had zero experience with the framework and figured it out from docs.

Cannot assign value of type [Employees] to type 'PublishSubject<[Employees]>

I have successfully parsed json data using URLSession and now I want to add the parsed data to an array. Doing this using an ordinary array works fine, but I'm learning Rx and thus want to use a subject.
So, this works:
var parsedJson = [Employees]()
self.parsedJson = decodedJson.people
But this gives an error:
var parsedJson: PublishSubject<[Employees]> = PublishSubject<[Employees]>()
self.parsedJson = decodedJson.people
Cannot assign value of type '[Employees]' to type 'PublishSubject<[Employees]>'
Here is the URLSession code:
// var parsedJson = [Employees]()
var parsedJson: PublishSubject<[Employees]> = PublishSubject<[Employees]>()
func getJSON(completion: #escaping () -> Void) {
guard let url = URL(string:"https://api.myjson.com/bins/jmos6") else { return }
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else { return }
do {
let jsonDecoder = JSONDecoder()
jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase
jsonDecoder.dateDecodingStrategy = .iso8601
let decodedJson = try jsonDecoder.decode(People.self, from: data)
self.parsedJson = decodedJson.people
completion()
} catch {
print(error)
}
}.resume()
}
Anyone know how to do this and why there is an error in the first place? Doesn't the <> simply indicate which type should be observed? Didn't get .accept() to work either.
EDIT
let parsedJson: BehaviorRelay<[Employees]> = BehaviorRelay(value: [])
self.parsedJson.accept(decodedJson.people)
This worked, but what is the equivalent to BehaviorSubject and PublishSubjuct?
The error message is pretty clear: you have a type-mismatch. You would get the same error message if you tried to assign a String to an Int variable, for example. A PublishSubject is not an array. Its a mechanism (think of it as a pipeline) for sending a stream of certain types of values (here an array of Employees).
You typically use Subjects by subscribing to them like so:
var parsedJson = PublishSubject<[Employee]>()
// the 'next' block will fire every time an array of employees is sent through the pipeline
parsedJson.next { [weak self] employees in
print(employees)
}
The above next block will fire every time you send an array through the PublishSubject like so:
let decodedJson = try jsonDecoder.decode(People.self, from: data)
self.parsedJson.onNext(decodedJson.people)
From your EDIT it seems that you moved on to trying to use a BehaviorRelay. I would recommend reading up on the differences between these two classes before decided which is appropriate for your use case. This article was really helpful to me when trying to learn the differences between the different types of Subjects and Relays: https://medium.com/#dimitriskalaitzidis/rxswift-subjects-a2c9ff32a185
Good luck!
Try
self.parsedJSON.onNext(decodedJson.people)

Take numbers from a file and put into a Vec<i32> but keep getting error

Code:
use std::io::Read;
fn main() {
let mut file = std::fs::File::open("numbs").unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
let mut v: Vec<i32> = Vec::new();
for s in contents.lines() {
v.push(s.parse::<i32>().unwrap());
}
}
Error:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParseIntError { kind: Empty }', src/libcore/result.rs:1165:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
Most likely, you have a trailing newline character \n at the end of your file i.e. an empty last line. You might also have empty lines in the middle of your file.
The easiest way to fix this for your use case is to just ignore empty lines:
for s in contents.lines() {
if !s.is_empty() {
v.push(s.parse::<i32>().unwrap());
}
}
However, it is generally not a good idea to just unwrap a Result especially if you cannot guarantee that it will never panic. A more robust solution is to handle each possible outcome of the Result appropriately. Another advantage of this solution is that it will not just ignore empty lines but also strings that cannot be parsed as an i32. Whether this is what you want or if you wish to handle this error explicitly is up to you. In the following example, we will use if-let to only insert values into the vector if they were successfully parsed as an i32:
for s in contents.lines() {
if let Ok(i) = s.parse::<i32>() {
v.push(i);
}
}
Side Note: You don't need to read the entire file into a string and then parse that line-by-line. Refer to Read large files line by line in Rust to see how to achieve this more idiomatically
Combining the aforementioned point and the use of flatten and flat_map, we can greatly simplify the logic to:
use std::fs::File;
use std::io::{BufRead, BufReader};
fn main() {
let file = File::open("numbs").unwrap();
let v: Vec<i32> = BufReader::new(file)
.lines()
.flatten() // gets rid of Err from lines
.flat_map(|line| line.parse::<i32>()) // ignores Err variant from Result of str.parse
.collect();
}

Resources