I'm working on a custom test-runner for a custom target, that doesn't have libtest available. However I would like to use the TestDescAndFn struct, which is defined in libtest, in my own test-runner. TestDescAndFn provides information about the tests such as if it should panic or not, the name of the test and others, so having this information would be really useful compared to just using #[test_case].
Since the #[test] annotation is resolved at compile-time and simply generates a test harness that calls a (custom) test-runner I can define with #![test_runner(my_test_runner)], I don't think I really need libtest aside from TestDescAndFnand the enums it contains. Is there any "good" way to use the definitions from libtest for TestDescAndFn, TestDesc etc., without actually building libtest?
The test crate provides TestDescAndFn and is provided by the compiler. So, your custom framework crate might do something like this:
#![feature(test)]
// provided by compiler, no need to specify dependency in Cargo.toml
extern crate test;
use ::test::TestDescAndFn;
pub fn run_tests(tests: &[&TestDescAndFn]) {
// ...
}
and then your crate under test would do this:
#![feature(custom_test_frameworks)]
#![test_runner(::my_framework::run_tests)]
// crate with #[test] as normal
Related
Is it possible to somehow detect in a crate that it's used in proc_macro and include some items based on that?
Let's say I have a token_parser crate with a Token enum:
pub struct Group;
pub struct Ident;
pub struct Punct;
pub struct Literal;
pub enum Token {
Group(Group),
Ident(Ident),
Punct(Punct),
Literal(Literal)
}
It's intended to be a wrapper for a proc_macro::TokenTree, so I want to be able to use it inside a proc_macro crates. I don't want to manualy implement a converting logic in every crate, so I want something like this:
#[cfg(proc_macro)]
impl From<proc_macro::TokenTree> for Token {
fn from(token: proc_macro::TokenTree) -> Self
{
todo!()
}
}
while still having an option to use token_parser crate in normal crates.
P.S I'm aware of proc_macro2 crate. I will probably use it (or even syn) in the end, for now I'm just trying to understand how to do something like that myself properly.
There are primarily two ways I would reach for based on how I want to enable such functionality.
feature for project-based enabling
If you want it only available when opted for, that's a fantastic case to use a cargo feature. Crates that want to use such functionality can then choose to enable this feature while other crates can choose to not enable the feature. Whether this feature should be default typically depends on how the library author believes it should/is used most of the time.
cfg for platform-based enabling
As you may have seen from reading the proc_macro2 source, they conditionally enable a cfg in their build script1. On the wasm32 platform target it is disabled, otherwise it is enabled. This way is very useful in cases where enabling functionality mostly depends on the platform.
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use self::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
hosting::add_to_waitlist();
hosting::add_to_waitlist();
}
The code is from Rust Book. In this case, if using the relative path, why bother to use self? I find that use front_of_house::hosting works well here.
So, any necessary reason to introduce self here?
The outcome of use front_of_house::hosting depends on the context. If the crate root contains extern crate front_of_house or front_of_house is a dependency in Cargo.toml, this will refer to hosting in that crate. In your case, however, there is the local module front_of_house, which takes priority over external crates. Conveniently, it's located just before the code that uses it, so it's obvious that it's the actual module being used. In larger files, however, there is a chance that the source of a module is ambiguous, i.e. if there is a dependency named the same as a local module. In that case, the extra self adds clarity to the code, helping you differentiate between dependencies and local modules. If, in that case, you wanted to use the dependency, you'd use ::front_of_house::hosting, which unconditionally refers to the dependency and fails if there isn't one named like that.
I'm writing a Rust library (generated from cargo) with unit tests.
I'd like to use the extern crate maplit in my unit tests to be able to use JavaScript-like hashmap literals. I don't want to use maplit in my library code.
maplit provides a macro that apparently must be activated using #[macro_use]. The only way I've been able to get this all working is to place this at the top of lib.rs:
#[cfg(test)] #[macro_use] extern crate maplit;
// my crate-specific stuff
At this point I realized I don't know what exactly #[cfg(test)] does. I'm using it in my tests. These are included with library code, as per the convention, like so:
// some library code
#[cfg(test)]
mod test {
use super::*;
// tests here
}
I had thought that the line #[cfg(test)] was marking what follows until the end of the file (or block?) as only applicable to the test configuration.
If so, then putting this directive at the top of lib.rs seems like a problem. Won't my entire library be dropped when I compile a distribution?
I've tried to find documentation on what exactly #[cfg(test)] does, but to no avail.
#[....]
The above is a Rust Attribute which is like an annotation in other languages. For example; in Java we have #Annotation(....) for methods and classes. Unlike annotation the rust attribute can be an expression that follows the attribute syntax.
#[cfg(....)]
The above is a compiler configuration attribute. The cfg() is one of many built-in attributes.
#[cfg(test)]
The above tells the Rust compiler that the following code should only be compiled when the test configuration is active. You can have other configuration attributes like debug, windows or features.
#[cfg(test)] #[macro_use] extern crate maplit;
Is the same as
#[cfg(test)]
#[macro_use]
extern crate maplit;
Which tells the Rust compiler to only compile the next line if the test configuration is active, and the next line tells Rust to only use macros from the following crate.
If so, then putting this directive at the top of lib.rs seems like a problem. Won't my entire library be dropped when I compile a distribution?
The #[cfg(...)] attribute only applies the compiler condition upon the thing it is attached to.
When you place the attribute at the top of the file followed by a space. The attribute is attached to the current module or crate.
As shown here from the documentation example, the crate_type is applied to the entire file:
// General metadata applied to the enclosing module or crate.
#![crate_type = "lib"]
// A function marked as a unit test
#[test]
fn test_foo() {
/* ... */
}
// A conditionally-compiled module
#[cfg(target_os = "linux")]
mod bar {
/* ... */
}
// A lint attribute used to suppress a warning/error
#[allow(non_camel_case_types)]
type int8_t = i8;
// Inner attribute applies to the entire function.
fn some_unused_variables() {
#![allow(unused_variables)]
let x = ();
let y = ();
let z = ();
}
I would like to use the nix crate in a project.
However, this project also has an acceptable alternative implementation for OSX and Windows, where I would like to use a different crate.
What is the current way of expressing that I only want nix in Linux platforms?
There's two steps you need to make a dependency completely target-specific.
First, you need to specify this in your Cargo.toml, like so:
[target.'cfg(target_os = "linux")'.dependencies]
nix = "0.5"
This will make Cargo only include the dependency when that configuration is active. However, this means that on non-Linux OS, you'll get a compile error for every spot you use nix in your code. To remedy this, annotate those usages with a cfg attribute, like so:
#[cfg(target_os = "linux")]
use nix::foo;
Of course that has rippling effects as now other code using those items fails to compile as the import, function, module or whatever doesn't exist on non-Linux. One common way to deal with that is to put all usages of nix into one function and use a no-op function on all other OSes. For example:
#[cfg(target_os = "linux")]
fn do_stuff() {
nix::do_something();
}
#[cfg(not(target_os = "linux"))]
fn do_stuff() {}
fn main() {
do_stuff();
}
With this, on all platforms, the function do_stuff exists and can be called. Of course, you have to decide for yourself what the function should do on non Linux.
Is it possible to reduce the boilerplate in using a library (as the library author) from:
#![feature(plugin)]
#![plugin(myplugin)]
#[macro_use]
extern crate myplugin_derive;
#[derive(MyPlugin)]
// ...
to something more like:
#![feature(plugin)]
#![plugin(myplugin)]
#[derive(MyPlugin)]
// ...
That is, since I know anyone using myplugin wants to use #[derive(MyPlugin)], is there a way of bringing MyPlugin into scope with #![plugin(myplugin)] only?