Creating a Shared Library

For multiple reasons, our different components may need to share some code. Luckily this is very simple. Let's start by initializing a new library package:

cd paekli-rs
cargo new --lib paekli-core

This new library can be used in any of our other packages by adding it as a dependency first. Note that we need the --path flag for a local library, as opposed to ones from crates.io.

cd paekli-rs/paekli-cli
cargo add --path ../paekli-core

Lastly, you can confirm it works by trying to import the generated add function. Note how the dash in paekli-core changes to an underscore in Rust code. This is automatic, because Rust identifiers cannot contain dashes.

#![allow(unused)]
fn main() {
// paekli-cli/src/main.rs
use paekli_core::add;
}

Now you can add some functionality to paekli-core and use it in your other packages.

Target-incompatible dependencies

As you implement more components, your shared library will accumulate its own dependencies. Some of these dependencies are not compatible with the web app. This can lead to difficult to understand error messages. Essentially, the web app will fail to compile because a bunch of stuff was "not found". This is because it doesn't exist in the browser, where our app will run. The next section will explain how to deal with this. If you have no intention of implementing the web app, you can safely ignore it.

Target-specific code and dependencies

Some functionality in paekli-core will only be available for components that run in a normal operating system. This is notably not the case for the web app.

So, we need to be able to include or exclude some functionality of paekli-core based on which platform it's being compiled for.

Compiling code only for specific targets

Let's assume your library is organized in two modules, wasm_compatible and wasm_incompatible. In that case, you can skip compiling the incompatible module for wasm-builds like so:

#![allow(unused)]
fn main() {
pub mod wasm_compatible;

#[cfg(not(target_family = "wasm"))]
pub mod wasm_incompatible;
}

Depending on how you organize your code, you may have to add multiple of these annotations.

See also the cfg page of Rust By Example and the Rust Reference.

Declaring target-specific dependencies

Even if you already excluded your own target-incompatible code as explained above, dependencies declared in Cargo.toml will still (fail to) compile. We need to tell cargo to completely exclude these libraries from the build, based on the target platform.

Take a look at the following example. The dependency serde is compiled for every target, while tokio is not compiled for the web app.

# paekli-core/Cargo.toml
[dependencies]
serde = { version = "1.0.197", features = ["derive"] }

[target.'cfg(not(target_family = "wasm"))'.dependencies]
tokio = { version = "1.36.0", features = ["full"] }

Note how the cfg syntax is identical to the one used in the source code attribute. The documentation for this is in the cargo book.