I have text file templates that I need to read, modify, then save to the user's directory in my cli app. The problem is that I don't know how to read these template files from my Rust app directory.
File Structure
- src
- templates
- foo.txt
- bar.txt
- main.rs
So in my main.rs, I would like to read the contents of foo.txt from the templates directory, modify the contents as a string, and then write it to the directory that the user is running the cli app from.
I've tried to read the files using:
std::fs::read_to_string("./templates/foo.txt").unwrap();
but since it's relative to where the user is running the cli app from, it doesn't exist there.
I've read about std::env::current_exe but it has a few warnings and looks like it might not be consistent so I'm not sure if this is the best way to do it.
Really depends on what exactly you want to do. It's important to first differentiate between including a file at compile time and reading a file at runtime. Rust is a compiled language[1], which makes this distinction much more important than in an interpreted language[1] like Javascript or Python.
This means that the contents of your template files will be read by the compiler. This file does not need to be shipped to the user's machine, because it's already coded into the binary. Conceptually, it's the same as writing a long string literal in your source code. There is no "reading file from disk" happening on your user's machine (other than the initial loading of the binary). From what I understand about your question, this is likely what you want.
In Rust, you would use the include_str! macro. Since this "include" action happens at compile time, this macro expects a string literal.
Note that include_str! expects a path relative to the current .rs file; if you wish to use a path relative to your cargo project's root directory, you can take a look here.
This means that your compiled binary will only know of a path, and will try to read from that path when running on the user's system. This of course, means that your template files will need to be installed onto the user's system.[2] This is usually the duty of the software packager, although if you distribute an installer for your application for example, you are effectively your own packager.
In Rust, you would use fs::read_to_string (as you are doing now), or fs::File if you want more granular control. Since this reading is happening at runtime, the path passed in can be any expression computed at runtime.
[1]: Strictly speaking, there is no such thing as a "compiled language" or "interpreted language", since it's possible to build a compiler and/or interpreter for any language. So more accurately I should say, in the vast majority of cases, Rust code is compiled then run instead of directly interpreted.
[2]: In this case, these templates are not really considered "source files" in the conventional sense, precisely because they do not contribute towards compiling the binary. You will often find them referred to as "support files" or even "config files" instead.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With