Get file descriptor from `std::io::File::open` - c

I am writing binding for a C library, I'd like to call std::io::File::open as it's got error handling already. I then intend to pass the fd to C function.
I have looked at std::io::fs, but the fd field is nothing like what I would have thought.
After some more digging I found native::io::file::FileDesc, which indeed has fn fd(&self) -> fd_t, but doesn't seem like this is something I can access from an instance of std::io::File.
There appear to be fs_from_raw_fd method, it's the exact opposite to what I need.

use std::os::unix::io::AsRawFd;
let path = Path::new("my.txt");
let display = path.display();
let mut file = match File::open(&path) {
// The `description` method of `io::Error` returns a string that
// describes the error
Err(why) => panic!("couldn't open {}: {}", display,
why.description()),
Ok(file) => file,
};
println!("file descriptor: {}",file.as_raw_fd());

The closest you can get in the current version of Rust is via native::io::file::open.
use native::io::file::open;
use std::rt::rtio::{Open, Read};
let file = match open(&path.to_c_str(), Open, Read) {
Ok(file) => file,
Err(_) => return,
}
let fd = file.fd();

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)
}

How to read file that is either uncompressed or compressed with gzip?

I try to read the contents of a file. The function doing this should be able to open the file regardless if it is a plain text file or a gzip-compressed file.
Below I have a working example, but if the file happens to be a plain text file, I have to read it twice. The function first tries to open the file assuming it is gzip-compressed (using the flate2 crate). If that does not provid a valid header, the file is read once more as a normal text file.
use std::fs::File;
use std::io::{self, Read};
use flate2::read::GzDecoder;
fn read_file( path: &str ) -> () {
let f_obj = File::open( path )
.expect( "Could not read file." );
let mut text = String::new();
let mut gz = GzDecoder::new( io::BufReader::new( &f_obj ) );
match gz.header() {
// `path` points to a file that is gzip-compressed.
Some( _ ) => gz.read_to_string( &mut text ),
// `path` points to a plain text file.
None => {
// Since the `GzDecoder` already moved some bytes out of f_obj
// by trying to decompress it, the file must be read once more.
// TODO There may be a better way instead of reading the same
// file twice.
let mut f_obj = File::open( path )
.expect( "Could not read file." );
f_obj.read_to_string( &mut text )
},
}.unwrap();
for line in text.lines() {
println!( "{:?}", line );
}
}
fn main() {
read_file( "file.txt.gz" )
}
Is there better way to do this?
I am relatively new to Rust and come from a Python background, so maybe I have the wrong perspective solving this problem, but I assume, this is not the best solution.

Trouble formatting a path for the Linphone SDK in Swift

Thanks in advance for the help!
I am trying to record calls using the Linphone SDK in Swift on Mac OS, and am having trouble passing a path into the function:
func linphone_call_params_set_record_file(_ cp: OpaquePointer!, _ path: UnsafePointer<Int8>!)
that works correctly (the SDK is written in C, though I am accessing it using Swift and a bridging header). The Linphone SDK works properly, and I can make and receive calls programmatically, with full audio support.
In trying to invoke the call recorder, I pass this function a path (pathtofile), such as:
let pathtofile = "/Users/Alex/Safety/1.wav"
where I would like to store the recording file.
func SafetyNetAVRecorderInitializer(pathtofile: String) -> Bool {
// Convert pathtofile to UnsafePointer<Int8>.
let cpathtofile = (pathtofile as NSString).utf8String
let path = UnsafeMutablePointer<Int8>(mutating: cpathtofile)
// Actually begin call recording.
if currentcall != nil {
let currentcallparameters = linphone_call_get_current_params(currentcall)
linphone_call_params_set_record_file (currentcallparameters, path)
linphone_call_start_recording(currentcall)
return true
}
return false
}
No runtime errors are encountered on linphone_call_params_set_record_file(), but when I try to invoke linphone_call_start_recording(), the recording does not begin, and an error is printed in the console that reads:
ortp-error-linphone_call_start_recording(): no output file specified. Use linphone_call_params_set_record_file().
How can I correctly pass a valid path to linphone_call_params_set_record_file()? I have tried directly passing a plain Swift String instead of an UnsafePointer<Int8> to no avail. Am I just misunderstanding how paths are formatted in C?
For reference, the SDK method source is:
void linphone_call_params_set_record_file(LinphoneCallParams *cp, const char *path){
if (cp->record_file){
ms_free(cp->record_file);
cp->record_file=NULL;
}
if (path) cp->record_file=ms_strdup(path);
}
Thanks again!
Try this:
let cpathtofile = (pathtofile as NSString).utf8String! // Unwraps!
...
inphone_call_params_set_record_file(currentcallparameters, cpathtofile)

Archiving and unarchiving Arrays in the DocumentDirectory in Swift

I am trying to save a simple array of objects in the persistent memory by executing the following code:
let fileManager=NSFileManager()
let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
if urls.count>0{
let localDocumentsDirectory=urls[0]
let archivePath=localDocumentsDirectory.URLByAppendingPathExtension("meditations.archive")
NSKeyedArchiver.archiveRootObject(self.meditationsArray, toFile: archivePath.path!)
let restored=NSKeyedUnarchiver.unarchiveObjectWithFile(archivePath.path!)
print("restored \(restored)")
}
}
Yet, when I print the restored date as in the code I find nil.
Conversely, if I use the CachesDirectory the array is soon after restored fine,
but when I reopen the app and try to load the data, it is lost. What is correct way to persistently save data?
I think the problem is that your are using URLByAppendingPathExtension, when you should be using URLByAppendingPathComponent. The "path extension" is the file extension, so your archivePath is "~/Documents.meditations.archive". It might be temporarily working with the CachesDirectory, because it's putting the data into a temporary file somewhere, or maybe just reading it back from memory. This should fix it:
let fileManager = NSFileManager()
let documentDirectoryUrls = fileManager.URLsForDirectory(.DocumentDirectory, .UserDomainMask)
if let documentDirectoryUrl = documentDirectoryUrls.first {
let fileUrl = documentDirectoryUrl.URLByAppendingPathComponent("meditations.archive")
// Also, take advantage of archiveRootObject's return value to check if
// the file was saved successfully, and safely unwrap the `path` property
// of the URL. That will help you catch any errors.
if let path = fileUrl.path {
let success = NSKeyedArchiver.archiveRootObject(meditationArray, toFile: path)
if !success {
print("Unable to save array to \(path)")
}
} else {
print("Invalid path")
}
} else {
print("Unable to find DocumentDirectory for the specified domain mask.")
}
I faced the same issue, I was unable to archive and unarchive array of objects using NSKeyedArchiver, I think the issue is that I'm using the below method :
NSKeyedArchiver.archiveRootObject(arrayOfItems, toFile: FileManager.getFileURL("My-File-Name")!)
I think this method is for archiving Objects, not array of Objects.
Anyway, I found a solution to my problem, by wrapping the whole array in an object, check below :
let myArrayItemsContainer = ArrayItemsContainer()
myArrayItemsContainer.allItems = arrayOfItems
NSKeyedArchiver.archiveRootObject(myArrayItemsContainer, toFile: FileManager.getFileURL("My-File-Name")!)
and I used the below code to unarchive my object :
NSKeyedUnarchiver.unarchiveObject(withFile: FileManager.getFileURL("My-File-Name")!) as? ArrayItemsContainer
Also I used this extension for using FileManager.getFileURL
public extension FileManager {
/// Returns the URL of the file given a name
///
/// - Parameter fileName: The file name of the file + extension
/// - Returns: The URL as String
static func getFileURL(_ fileName: String) -> String? {
let fileURL = FileManager().urls(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).first
return (fileURL?.appendingPathComponent(fileName).path)
}
}

Writing to a file or stdout in Rust

I'm learning Rust, and I'm somewhat stumped.
I'm trying to give the user the option of writing output to stdout or to a supplied filename.
I started with the example code that's given for using extra::getopts located here. From there, in the do_work function, I'm trying to do this:
use std::io::stdio::stdout;
use std::io::buffered::BufferedWriter;
fn do_work( input: &str, out: Option<~str> ) {
println!( "Input: {}", input );
println!( "Output: {}", match out {
Some(x) => x,
None => ~"Using stdout"
} );
let out_writer = BufferedWriter::new( match out {
// I know that unwrap is frowned upon,
// but for now I don't want to deal with the Option.
Some(x) => File::create( &Path::new( x ) ).unwrap(),
None => stdout()
} );
out_writer.write( bytes!( "Test output\n" ) );
}
But it outputs the following error:
test.rs:25:43: 28:6 error: match arms have incompatible types: expected `std::io::fs::File` but found `std::io::stdio::StdWriter` (expected struct std::io::fs::File but found struct std::io::stdio::StdWriter)
test.rs:25 let out_writer = BufferedWriter::new( match out {
test.rs:26 Some(x) => File::create( &Path::new( x ) ).unwrap(),
test.rs:27 None => stdout()
test.rs:28 } );
test.rs:25:22: 25:41 error: failed to find an implementation of trait std::io::Writer for [type error]
test.rs:25 let out_writer = BufferedWriter::new( match out {
^~~~~~~~~~~~~~~~~~~
But I don't understand what the issue is because both File and StdWriter implement the Writer Trait. Can someone explain what I'm doing wrong?
Thanks!
A lot has changed in Rust since 2014, so here is an answer that works for me using Rust 1.15.1:
let out_writer = match out {
Some(x) => {
let path = Path::new(x);
Box::new(File::create(&path).unwrap()) as Box<dyn Write>
}
None => Box::new(io::stdout()) as Box<dyn Write>,
};
This is pretty much the same as #Arjan's answer, except that ~ was replaced by Box, and some names have changed. I'm leaving out BufferedWriter, but if you want that, I believe it is now named BufWriter.
Yes, both implement Write, but the problem is BufWriter is expecting a type T that implements Writer, and that T can't be File and Stdout at the same time.
You must cast both to the common type (either Box<dyn Write> or &dyn Write, but since you cannot return references you have to use Box):
fn do_work(input: &str, out: Option<String>) {
let mut out_writer: Box<dyn Write> = BufWriter::new(match out {
Some(ref x) => Box::new(File::create(&Path::new(x)).unwrap()),
None => Box::new(stdout()),
});
out_writer.write(b"Test output\n").unwrap();
}
You should also handle errors properly, not just using unwrap (used in example for simplicity).

Resources