Where do I need to put file to be read by Rust? - rust

When I was reading through the tutorial for Rust here (https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html). I found this block of code:
use std::fs::File;
fn main() {
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => panic!("Problem opening the file: {:?}", error),
};
}
It always displays an error: { code: 2, kind: NotFound, message: "The system cannot find the file specified." } even when I make a hello.txt at the root folder of the src, it fails to read it.
In another example here, I use cargo run to no success. The program still fails to read hello.txt file. I'm aware that the example uses rustc open.rs && ./open. Since I don't understand why is it suddenly use different compile method and what's it even mean... I just kinda skip it and try to use cargo run instead
Where do I need to put my file here so cargo run can read it ?
Also if I run the production code and need the program to read an external file, where do I need to put it ?
Here's my folder structure. Pretty simple since I just start to learn RUST.
Thank you in advance.

A file without a directory component in the name needs to be in the current working directory, i.e. the directory from which you start your executable or cargo run.
If you start cargo from an IDE, it might not be immediately apparent what directory it will use as the current directory. In that case, you can always find the current working directory by printing it explicitly:
fn main() {
println!("{}", std::env::current_dir().unwrap().display())
}

Related

cargo rust build script - print output of command

I am new to rust and cargo, and I am trying to do something very simple!
I have something like this (in build.rs):
use std::process::Command;
fn main() {
Command::new("echo 123");
}
And I want to see the output of the command echo 123. I want 123 to get printed to the build output (this is mostly to debug what I am doing) and wont be part of the final project.
I have tried cargo build --verbose - this does not work.
I can't extrapolate an answer from there posts (and some others like it):
https://github.com/rust-lang/cargo/issues/985
https://github.com/rust-lang/cargo/issues/1106
I feel this must be simple to do - but I have been stuck for hours looking on the web and not finding the answer.
Just building a Command with Command::new does not execute it yet. It just starts a builder pattern. To actually execute it, you have to use the methods spawn, output or status. Example:
Command::new("echo")
.arg("123")
.spawn()
.expect("failed to spawn process");
It's very unfortunate that this doesn't produce a warning. Someone recently tried to add the #[must_use] attribute to Command, which would make your code procude a warning. The PR closed for now but it seems like it will be added eventually.
We can use a macro and it worked form me, but there is a warning, since it uses cargo to display. but that is fine for me.
I found below code from git hub discussion:
Cargo doesn’t display output from a command in build.rs #985
macro_rules! p {
($($tokens: tt)*) => {
println!("cargo:warning={}", format!($($tokens)*))
}
}
fn main() {
p!("BUILD.rs -> Starting ...");
}

How to write a file using LF instead of CRLF on Windows?

We have a script that runs as the preinstall script. It uses fs.writeFile to write a config file which it generates.
writeFile(configFilePath, configFileContents, (e) => {
// ... do some error handling
}
For some reason it uses CRLF line endings on Windows and creating diffs in git although the file has not changed.
I have tried to do use
.replace(/\r\n/gm, "\n");
on configFileContents but it still uses the Windows line endings.
configFileContents gets created by:
const configFileContents = JSON.stringify({
foo: bar,
baz, foo,
// ...
}, null, 2);
Is there a way to tell Node to use the Linux ones?
You can simply do this:
.replace(/\r\n/g, "\n")
Also /\r\n/gm regexp isn't correct as you're already telling the Regexp engine to look for new line by providing the m/multiple lines option... That's why it doesn't allow the expression to work. Just use g if you really wan't to use the RegExp
I used prettier to update the file automatically after the file was created, that worked for me. So i just added prettier command in extention to the file creation npm script.
prettier \"supportedBrowsers.ts\" --write"

How to do file inputs via node.js using emscripten?

I have a C++ project that I have converted into javascript using emscripten. I need help with implementing file input into the program via node. As I understand it the default file system in emscripten uses preloaded data that can only be done on a web page or web worker. I need mine to work with node.js on the command line.
Looking at the documentation I see that there's a way to use NODEFS instead of the default MEMFS which should allow me to do this. However, I'm unsure how I'm supposed to go about this. I don't really understand the test code that's provided.
Here's how the file handling is being done in the original C++ project:
void InputFile(std::string &fileName)
{
std::ifstream in(fileName);
if (in.fail())
{
std::cerr << "ERROR, Could not open " << fileName << std::endl;
exit(1);
}
}
But when I attempt to run the converted program with a file, node project.js -f test.file I get the error message: ERROR, Could not open test.file meaning that opening the file failed. The original C++ project was able to open the file without any issues, so I know there's not problem with the file itself.
I'm not sure what I have to do to make the converted project work with file inputs, any help would very much appreciated.
Explanation
WebAssembly module, built using emscripten, has no information about files in your physical file system. Instead, it uses a virtual file system. All you have to do is to create a link between files on your physical system to the files on the module's virtual system. NODEFS gives you this opportunity.
Quick solution
We will start at modifying your C++ code by adding the aforementioned link between physical and virtual file systems using embedded JS code (with EM_ASM). First (1), we create a directory '/temp' on the virtual file system where all referenced files will be located in. Then (2), we link this new virtual directory with a real physical location (the current working directory '.') where all the referenced files are already.
#include <emscripten.h>
#include <emscripten/bind.h>
#include <iostream>
#include <fstream>
void InputFile(const std::string &fileName)
{
EM_ASM(
FS.mkdir('/temp'); // (1)
FS.mount(NODEFS, {root : '.'}, '/temp');); // (2)
std::ifstream in(std::string("/temp/") + fileName);
if (in.fail())
{
std::cerr << "ERROR, Could not open " << fileName << std::endl;
exit(1);
}
}
EMSCRIPTEN_BINDINGS(Module)
{
emscripten::function("InputFile", &InputFile);
}
Now, because in the WebAssembly module, we are working with the virtual file systems, and not the physical one, each referenced file from the current directory (the root '.') is actually in the virtual directory previously linked ('/temp'). Hence, '/temp' directory precedes the name to the referenced file: std::ifstream in(std::string("/temp/") + fileName);.
Finally, we can compile this file. We force the synchronized compilation (to make sure the require loads the WASM module on time). Moreover, the option -s EXIT_RUNTIME=1 makes sure that the C++ command exit(1); finishes the execution. Also, we need to link Embind (--bind) and NODEFS (-lnodefs.js):
emcc project.cpp -o project.js -s WASM_ASYNC_COMPILATION=0 -s EXIT_RUNTIME=1 --bind -lnodefs.js
Testing
To test the WebAssembly module with the same calling convention as you have mentioned, we can use the following test.js script:
var Module = require('./project.js');
if (process.argv[3] && process.argv[2] === '-f') {
const filename = process.argv[3];
Module.InputFile(filename);
} else {
console.log('Pass the file with -f flag!');
}
To run the file, all you have to do is this: node test.js -f test.file
Comment
This approach works well if the referenced files are in the current working directory. In the case they are not, you could modify the code of the InputFile to extract the directory in which the fileName is, and then, mount the real-to-virtual directory accordingly.

Why isn't my Rust code cding into the said directory?

use std::{
env, io,
path::PathBuf,
process::{self, Command},
};
fn inner_main() -> io::Result<PathBuf> {
let exe = env::current_exe()?;
let dir = exe.parent().expect("Executable must be in some directory");
let dir = dir.join("nvs");
Ok(dir)
}
fn main() {
let path = inner_main().expect("Couldn't get path.");
let path = path.into_os_string().into_string().unwrap();
Command::new("cd")
.arg(&path)
.status()
.expect("Something went wrong.");
process::exit(0);
}
I grab the path that the binary is in, go into the parent directory so the binaries name is no longer in the path and then append "nvs" at the end of the path and then in main() I put the inner_main() function in a let and then redeclare the let as a string so I can cd into the directory.
Whenever it tries CDing into the nvs directory nothing happens and I know that the command runs because if I move the binary somewhere with no nvs file in it's same directory it runs saying it can't find that directory so my question is when it's in a directory with nvs why doesn't it actually cd into the said directory like it should?
You're attempting to run an external command called cd. Depending on your operating system, this either fails because there is no command called cd, or this does nothing other than test whether the directory exists and you have permission to access it. If a cd command exists, it runs in a subprocess of your program, and its change of directory does not affect your process.
To change to a different directory, you need to change the working directory of your own process. Call std::env::set_current_dir.
std::env::set_current_dir(&path).expect("Unable to change into [path to executable]/nvs");
// do stuff in …/nvs

What does `configurations.<config>` allow to iterate over?

In the gradle documentation in the section about groovy basic, we have an example:
configurations.runtime.each { File f -> println f }
What the code was supposed to print? I write the simple build script containig this line:
apply plugin: 'java'
configurations.runtime.each { File f -> println f }
dependencies{
project(':api') //This's just a project declared in the settings.gradle
}
and what the gradle check does is print nothing. What does it mean actually?
In general this will iterate over all files resolved for the specified configuration. In your case this configuration is runtime. As you havn't declared any runtime dependencies, no file is printed. Vahid is right that you should put this kind of statements into a task, otherwise it will be called at gradles configuration time on every build invocation, even when just asking for available tasks via "gradle tasks".
you should overwrite gradle check method:
check << {
configurations.runtime.each { File f -> println f }
}
then run
gradle check

Resources