pub mod bugs;
pub mod error;
use crate::{
api::{AppInstallArg, AppStatus, AppVersion, CallCycles, InterCall, Management},
types::{CanisterId, ControllerId, ControllerIds, OperationId},
};
use candid::{CandidType, Principal};
use ic_cdk::api::management_canister::main::{
CanisterInfoResponse, InstallCodeArgument, UpdateSettingsArgument,
};
use ic_cdk::api::management_canister::{
main::CreateCanisterArgument, provisional::CanisterSettings,
};
use serde::{Deserialize, Serialize};
use self::error::AppCallError;
use super::RequestJoinArgs;
#[derive(CandidType, Deserialize, Serialize, PartialEq, Clone)]
pub struct AppCall(pub CanisterId);
impl AppCall {
pub fn canister_id(&self) -> CanisterId {
self.0.clone()
}
pub async fn create_with_cycles(
controllers: Vec<ControllerId>,
cycles: u128,
) -> Result<Self, AppCallError> {
let args = CreateCanisterArgument {
settings: Some(CanisterSettings {
controllers: Some(controllers.clone()),
..Default::default()
}),
};
let result = Management::create_canister(args, cycles).await;
match result {
Ok(result) => Ok(Self(result.canister_id)),
Err(err) => Err(AppCallError::CreateCanisterError(err.to_string())),
}
}
pub async fn validate_user(&self, user_id: Principal) -> Result<bool, AppCallError> {
InterCall(self.0)
.call("validate_user", user_id, CallCycles::NoPay)
.await
.map_err(|err| AppCallError::ValidateUserError(err.to_string()))
}
pub async fn version(&self) -> Result<AppVersion, AppCallError> {
InterCall(self.0)
.call("version", (), CallCycles::NoPay)
.await
.map_err(|err| AppCallError::VersionError(err.to_string()))
}
pub async fn status(&self) -> Result<AppStatus, AppCallError> {
InterCall(self.0)
.call("status", (), CallCycles::NoPay)
.await
.map_err(|err| AppCallError::CanisterStatusError(err.to_string()))
}
pub async fn info(
&self,
num_requested_changes: Option<u64>,
) -> Result<CanisterInfoResponse, AppCallError> {
Management::canister_info(self.0, num_requested_changes)
.await
.map_err(|err| AppCallError::CanisterInfoError(err.to_string()))
}
pub async fn module_hash(&self) -> Result<Option<Vec<u8>>, AppCallError> {
self.info(None)
.await
.map(|info| info.module_hash)
.map_err(|err| AppCallError::CanisterInfoError(err.to_string()))
}
pub async fn request_join(&self, args: RequestJoinArgs) -> Result<OperationId, AppCallError> {
InterCall(self.0)
.call("request_join", args, CallCycles::NoPay)
.await
.map_err(|err| AppCallError::RequestJoinError(err.to_string()))
}
pub async fn install_code(&self, args: AppInstallArg) -> Result<(), AppCallError> {
let canister_id = self.canister_id();
let install_args = InstallCodeArgument {
arg: args.arg,
mode: args.mode,
wasm_module: args.wasm_module,
canister_id,
};
Management::install_code(install_args)
.await
.map_err(|err| AppCallError::InstallCodeError(err.to_string()))
}
pub async fn uninstall_code(&self) -> Result<(), AppCallError> {
Management::uninstall_code(self.0)
.await
.map_err(|err| AppCallError::UninstallCodeError(err.to_string()))
}
pub async fn add_controllers(
&self,
mut controllers: ControllerIds,
) -> Result<(), AppCallError> {
let canister_id = self.0;
let canister_status = Management::canister_status(canister_id.clone())
.await
.map_err(|err| AppCallError::CanisterStatusError(err.to_string()))?;
if !controllers.contains(&canister_id) {
controllers.push(canister_id);
canister_status
.settings
.controllers
.iter()
.for_each(|controller| {
if !controllers.contains(controller) {
controllers.push(controller.clone());
}
});
}
let arg = UpdateSettingsArgument {
canister_id,
settings: CanisterSettings {
controllers: Some(controllers),
..Default::default()
},
};
Management::update_settings(arg)
.await
.map_err(|err| AppCallError::UpdateCanisterControllersError(err.to_string()))
}
}