pop_common/
lib.rs

1// SPDX-License-Identifier: GPL-3.0
2
3#![doc = include_str!("../README.md")]
4
5pub use account_id::{parse_account, parse_h160_account};
6#[cfg(feature = "integration-tests")]
7#[allow(deprecated)]
8use assert_cmd::cargo::cargo_bin;
9pub use build::Profile;
10pub use errors::Error;
11pub use git::{Git, GitHub, Release};
12pub use helpers::{get_project_name_from_path, get_relative_or_absolute_path, replace_in_file};
13pub use metadata::format_type;
14pub use signer::create_signer;
15pub use sourcing::set_executable_permission;
16use std::{cmp::Ordering, net::TcpListener, ops::Deref};
17#[cfg(feature = "integration-tests")]
18use std::{ffi::OsStr, path::Path};
19pub use subxt::{Config, PolkadotConfig as DefaultConfig};
20pub use subxt_signer::sr25519::Keypair;
21pub use templates::{
22	extractor::extract_template_files,
23	frontend::{FrontendTemplate, FrontendType},
24};
25pub use test::test_project;
26
27/// Module for parsing and handling account IDs.
28pub mod account_id;
29/// Provides functionality for accessing rate-limited APIs.
30pub(crate) mod api;
31/// Provides build profiles for usage when building Rust projects.
32pub mod build;
33/// Represents the various errors that can occur in the crate.
34pub mod errors;
35/// Provides functionality for interacting with Git, GitHub, repositories and releases.
36pub mod git;
37/// Provides general purpose file and path helpers.
38pub mod helpers;
39/// Provides functionality for resolving and managing Cargo manifests.
40pub mod manifest;
41/// Provides functionality for formatting and resolving metadata types.
42pub mod metadata;
43/// Provides parsers for determining Polkadot SDK versions.
44pub mod polkadot_sdk;
45/// Provides functionality for creating a signer from a secret URI.
46pub mod signer;
47/// Provides functionality for sourcing binaries from a variety of different sources.
48pub mod sourcing;
49/// Provides traits and functions used for templates and template extraction.
50pub mod templates;
51/// Module for testing utilities and functionality.
52pub mod test;
53/// Contains utilities for setting up a local test environment.
54pub mod test_env;
55
56static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
57
58/// Trait for observing status updates.
59pub trait Status {
60	/// Update the observer with the provided `status`.
61	fn update(&self, status: &str);
62}
63
64impl Status for () {
65	// no-op: status updates are ignored
66	fn update(&self, _: &str) {}
67}
68
69/// Determines the target triple based on the current platform.
70pub fn target() -> Result<&'static str, Error> {
71	use std::env::consts::*;
72
73	if OS == "windows" {
74		return Err(Error::UnsupportedPlatform { arch: ARCH, os: OS });
75	}
76
77	match ARCH {
78		"aarch64" => {
79			return match OS {
80				"macos" => Ok("aarch64-apple-darwin"),
81				_ => Ok("aarch64-unknown-linux-gnu"),
82			};
83		},
84		"x86_64" | "x86" => {
85			return match OS {
86				"macos" => Ok("x86_64-apple-darwin"),
87				_ => Ok("x86_64-unknown-linux-gnu"),
88			};
89		},
90		&_ => {},
91	}
92	Err(Error::UnsupportedPlatform { arch: ARCH, os: OS })
93}
94
95/// Creates a new Command instance for running the `pop` binary in integration tests.
96///
97/// # Arguments
98///
99/// * `dir` - The working directory where the command will be executed.
100/// * `args` - An iterator of arguments to pass to the command.
101///
102/// # Returns
103///
104/// A new Command instance configured to run the pop binary with the specified arguments
105#[cfg(feature = "integration-tests")]
106pub fn pop(
107	dir: &Path,
108	args: impl IntoIterator<Item = impl AsRef<OsStr>>,
109) -> tokio::process::Command {
110	#[allow(deprecated)]
111	let mut command = tokio::process::Command::new(cargo_bin("pop"));
112	command.current_dir(dir).args(args);
113	println!("{command:?}");
114	command
115}
116
117/// Checks if preferred port is available, otherwise returns a random available port.
118pub fn find_free_port(preferred_port: Option<u16>) -> u16 {
119	// Try to bind to preferred port if provided.
120	if let Some(port) = preferred_port &&
121		TcpListener::bind(format!("127.0.0.1:{}", port)).is_ok()
122	{
123		return port;
124	}
125
126	// Else, fallback to a random available port
127	TcpListener::bind("127.0.0.1:0")
128		.expect("Failed to bind to an available port")
129		.local_addr()
130		.expect("Failed to retrieve local address. This should never occur.")
131		.port()
132}
133
134/// A slice of `T` items which have been sorted.
135pub struct SortedSlice<'a, T>(&'a mut [T]);
136impl<'a, T> SortedSlice<'a, T> {
137	/// Sorts a slice with a comparison function, preserving the initial order of equal elements.
138	///
139	/// # Arguments
140	/// * `slice`: A mutable slice of `T` items.
141	/// * `f`: A comparison function which returns an [Ordering].
142	pub fn by(slice: &'a mut [T], f: impl FnMut(&T, &T) -> Ordering) -> Self {
143		slice.sort_by(f);
144		Self(slice)
145	}
146
147	/// Sorts a slice with a key extraction function, preserving the initial order of equal
148	/// elements.
149	///
150	/// # Arguments
151	/// * `slice`: A mutable slice of `T` items.
152	/// * `f`: A comparison function which returns a key.
153	pub fn by_key<K: Ord>(slice: &'a mut [T], f: impl FnMut(&T) -> K) -> Self {
154		slice.sort_by_key(f);
155		Self(slice)
156	}
157}
158
159impl<T> Deref for SortedSlice<'_, T> {
160	type Target = [T];
161
162	fn deref(&self) -> &Self::Target {
163		&self.0[..]
164	}
165}
166
167/// Provides functionality for making calls to parachains or smart contracts.
168pub mod call {
169	// Note: cargo contract logic is used for parsing events after calling a chain. This could be
170	// refactored in the future so that we don't have to use cargo contract code in
171	// `pop-chains`.
172	pub use contract_build::Verbosity;
173	pub use contract_extrinsics::{DisplayEvents, TokenMetadata};
174	pub use ink_env::DefaultEnvironment;
175}
176
177#[cfg(test)]
178mod tests {
179	use super::*;
180	use anyhow::Result;
181
182	#[test]
183	fn target_works() -> Result<()> {
184		use std::{process::Command, str};
185		let output = Command::new("rustc").arg("-vV").output()?;
186		let output = str::from_utf8(&output.stdout)?;
187		let target_expected = output
188			.lines()
189			.find(|l| l.starts_with("host: "))
190			.map(|l| &l[6..])
191			.unwrap()
192			.to_string();
193		assert_eq!(target()?, target_expected);
194		Ok(())
195	}
196
197	#[test]
198	fn find_free_port_works() -> Result<()> {
199		let port = find_free_port(None);
200		let addr = format!("127.0.0.1:{}", port);
201		// Constructs the TcpListener from the above port
202		let listener = TcpListener::bind(&addr);
203		assert!(listener.is_ok());
204		Ok(())
205	}
206
207	#[test]
208	fn sorted_slice_sorts_by_function() {
209		let mut values = ["one", "two", "three"];
210		let sorted = SortedSlice::by(values.as_mut_slice(), |a, b| a.cmp(b));
211		assert_eq!(*sorted, ["one", "three", "two"]);
212	}
213
214	#[test]
215	fn sorted_slice_sorts_by_key() {
216		let mut values = ['c', 'b', 'a'];
217		let sorted = SortedSlice::by_key(values.as_mut_slice(), |v| *v as u8);
218		assert_eq!(*sorted, ['a', 'b', 'c']);
219	}
220}