resonite 0.4.0

Resonite's API in rust
Documentation
#[cfg(not(feature = "http_client"))]
fn main() {
	println!("http_client feature required");
	std::process::exit(2);
}

#[cfg(feature = "http_client")]
use std::{fs::File, io, process::exit};

#[cfg(feature = "http_client")]
const USER_AGENT: &str = concat!(
	env!("CARGO_PKG_NAME"),
	"-AuthHelper/",
	env!("CARGO_PKG_VERSION"),
	" (",
	env!("CARGO_PKG_REPOSITORY"),
	")",
);

#[cfg(feature = "http_client")]
fn main() {
	// We really don't need to care about multithreading for this simple tool

	use std::io::Write;

	let rt = tokio::runtime::Builder::new_current_thread()
		.enable_all()
		.build()
		.expect("Creating tokio runtime to work");

	let user_session_serde_file = File::create("local/user-session.json")
		.expect("Creating local/user-session.json file to work");

	#[cfg(feature = "borsh")]
	let mut user_session_bin_file = File::create("local/user-session.bin")
		.expect("Creating local/user-session.bin file to work");

	println!(
		"This is a very simple helper tool to generate a `user-session.json` file for running the integration tests."
	);
	println!(
		"Be aware that it does not censor what you type (including the password)."
	);

	let stdin = io::stdin();

	let username = {
		let input = &mut String::new();
		println!("Username?");
		input.clear();
		stdin.read_line(input).expect("Reading input to work");
		input.trim().to_owned()
	};

	if username.is_empty() {
		println!("Username cannot be empty!");
		exit(1);
	}

	let password = {
		let input = &mut String::new();
		println!("Password?");
		input.clear();
		stdin.read_line(input).expect("Reading input to work");
		let input = input.trim_end_matches('\n').to_owned();

		if input.is_empty() {
			println!("Username cannot be empty!");
			exit(1);
		}

		input
	};

	let unique_machine_identifier = {
		let input = &mut String::new();
		#[cfg(not(feature = "rand_util"))]
		println!("UID?");
		#[cfg(feature = "rand_util")]
		println!("UID? (enter empty to generate)");
		input.clear();
		stdin.read_line(input).expect("Reading input to work");
		let mut input = input.trim().to_owned();

		if input.is_empty() {
			#[cfg(not(feature = "rand_util"))]
			{
				println!("UID cannot be empty!");
				exit(1);
			}
			#[cfg(feature = "rand_util")]
			{
				input = resonite::util::random_ascii_string(32);
			}
		}
		input
	};

	let secret_machine_id = {
		let input = &mut String::new();
		#[cfg(not(feature = "rand_util"))]
		println!("Secret machine ID?");
		#[cfg(feature = "rand_util")]
		println!("Secret machine ID? (enter empty to generate)");
		input.clear();
		stdin.read_line(input).expect("Reading input to work");
		let mut input = input.trim().to_owned();

		if input.is_empty() {
			#[cfg(not(feature = "rand_util"))]
			{
				println!("UID cannot be empty!");
				exit(1);
			}
			#[cfg(feature = "rand_util")]
			{
				input = resonite::util::random_ascii_string(32);
			}
		}
		input
	};

	let second_factor = {
		let input = &mut String::new();
		println!("TOTP? (enter empty if none)");
		input.clear();
		stdin.read_line(input).expect("Reading input to work");
		let input = input.trim().to_owned();

		if input.is_empty() { None } else { Some(input) }
	};

	let auth_state = resonite::query::Authenticating {
		second_factor,
		unique_machine_identifier: unique_machine_identifier.clone(),
	};

	let queryable = resonite::query::UserSession {
		remember_me: true,
		secret_machine_id,
		authentication: resonite::query::UserSessionAuthentication::Password(
			resonite::query::UserSessionPasswordAuthentication {
				password,
				recovery_code: None,
			},
		),
		identifier: resonite::query::LoginCredentialsIdentifier::Username(username),
	};

	// Execute the future, blocking the current thread until completion
	let user_session = rt
		.block_on(request_session(auth_state, queryable))
		.expect("login should succeed");
	drop(rt);

	println!("Login successful");
	serde_json::to_writer_pretty(
		user_session_serde_file,
		&user_session.user_session,
	)
	.expect("Writing user session json to work");

	#[cfg(feature = "borsh")]
	{
		use borsh::BorshSerialize;
		let mut s = Vec::new();
		user_session
			.user_session
			.serialize(&mut s)
			.expect("serializing user session to bin to work");
		user_session_bin_file
			.write_all(&s)
			.expect("Writing user session bin to work");
	}

	println!(
		"Dumping config files not implemented, received: {:?}",
		user_session.config_files
	);
}

#[cfg(feature = "http_client")]
async fn request_session(
	auth_state: resonite::query::Authenticating,
	queryable: resonite::query::UserSession,
) -> Result<resonite::model::UserSessionResult, racal::reqwest::ApiError> {
	use resonite::api_client::ApiClient;
	let client =
		resonite::api_client::UnauthenticatedResonite::new(USER_AGENT.to_owned())
			.expect("Creating API client to work");

	let client =
		resonite::api_client::AuthenticatingResonite::from((client, auth_state));
	client.query(queryable).await
}