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