use std::fmt::{self, Debug, Display};
use devicemapper::Sectors;
use crate::engine::{
engine::Filesystem,
types::{DevUuid, FilesystemUuid, PoolUuid},
};
pub struct Key;
pub struct Clevis;
pub trait EngineAction {
type Return;
fn is_changed(&self) -> bool;
fn changed(self) -> Option<Self::Return>;
}
#[derive(Debug, PartialEq, Eq)]
pub enum CreateAction<T> {
Identity,
Created(T),
}
impl<T> EngineAction for CreateAction<T> {
type Return = T;
fn is_changed(&self) -> bool {
!matches!(*self, CreateAction::Identity)
}
fn changed(self) -> Option<T> {
match self {
CreateAction::Created(t) => Some(t),
_ => None,
}
}
}
impl Display for CreateAction<PoolUuid> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CreateAction::Created(uuid) => {
write!(f, "Pool with UUID {} was created successfully", uuid)
}
CreateAction::Identity => {
write!(
f,
"The pool requested for creation is already present; no action taken"
)
}
}
}
}
impl Display for CreateAction<Clevis> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CreateAction::Created(Clevis) => {
write!(
f,
"Pool successfully bound to an unlocking mechanism using clevis"
)
}
CreateAction::Identity => {
write!(
f,
"The pool requested for clevis binding is already bound; no action taken"
)
}
}
}
}
impl Display for CreateAction<Key> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CreateAction::Created(Key) => {
write!(
f,
"Pool successfully bound to a passphrase in the kernel keyring"
)
}
CreateAction::Identity => {
write!(
f,
"The pool requested for keyring binding is already bound; no action taken"
)
}
}
}
}
impl<F> Display for CreateAction<(FilesystemUuid, &mut F)>
where
F: Filesystem,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CreateAction::Created((uuid, fs)) => {
write!(
f,
"Snapshot with UUID {} was created successfully from filesystem with path {}",
uuid,
fs.devnode().display()
)
}
CreateAction::Identity => {
write!(
f,
"The snapshot requested for creation is already present; no action taken"
)
}
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum MappingCreateAction<T> {
Created(T),
Identity,
ValueChanged(T),
}
impl Display for MappingCreateAction<Key> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MappingCreateAction::Created(Key) => write!(f, "Set a new key successfully"),
MappingCreateAction::Identity => {
write!(f, "The requested key already exists; no action was taken")
}
MappingCreateAction::ValueChanged(Key) => write!(
f,
"An existing key was updated with a new value successfully"
),
}
}
}
impl<T> EngineAction for MappingCreateAction<T> {
type Return = T;
fn is_changed(&self) -> bool {
matches!(
*self,
MappingCreateAction::Created(_) | MappingCreateAction::ValueChanged(_)
)
}
fn changed(self) -> Option<T> {
match self {
MappingCreateAction::Created(t) | MappingCreateAction::ValueChanged(t) => Some(t),
_ => None,
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum MappingDeleteAction<T> {
Deleted(T),
Identity,
}
impl<T> EngineAction for MappingDeleteAction<T> {
type Return = T;
fn is_changed(&self) -> bool {
matches!(*self, MappingDeleteAction::Deleted(_))
}
fn changed(self) -> Option<T> {
match self {
MappingDeleteAction::Deleted(t) => Some(t),
_ => None,
}
}
}
impl Display for MappingDeleteAction<Key> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MappingDeleteAction::Deleted(_) => {
write!(f, "A key was deleted successfully")
}
MappingDeleteAction::Identity => {
write!(
f,
"The key requested for deletion is already absent; no action taken"
)
}
}
}
}
pub struct SetUnlockAction<T> {
unlocked: Vec<T>,
}
impl<T> SetUnlockAction<T> {
pub fn new(unlocked: Vec<T>) -> SetUnlockAction<T> {
SetUnlockAction { unlocked }
}
pub fn empty() -> SetUnlockAction<T> {
SetUnlockAction {
unlocked: Vec::new(),
}
}
}
impl<T> EngineAction for SetUnlockAction<T> {
type Return = Vec<T>;
fn is_changed(&self) -> bool {
!self.unlocked.is_empty()
}
fn changed(self) -> Option<Vec<T>> {
if self.unlocked.is_empty() {
None
} else {
Some(self.unlocked)
}
}
}
impl Display for SetUnlockAction<DevUuid> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.unlocked.is_empty() {
write!(
f,
"No new devices were able to be unlocked; no action was taken"
)
} else {
write!(
f,
"The devices with UUIDs {} were successfully unlocked",
self.unlocked
.iter()
.map(|uuid| uuid.to_string())
.collect::<Vec<_>>()
.join(", ")
)
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct SetCreateAction<T> {
changed: Vec<T>,
}
impl<T> SetCreateAction<T> {
pub fn new(changed: Vec<T>) -> Self {
SetCreateAction { changed }
}
pub fn empty() -> Self {
SetCreateAction { changed: vec![] }
}
}
impl<T> EngineAction for SetCreateAction<T> {
type Return = Vec<T>;
fn is_changed(&self) -> bool {
!self.changed.is_empty()
}
fn changed(self) -> Option<Vec<T>> {
if self.changed.is_empty() {
None
} else {
Some(self.changed)
}
}
}
impl Display for SetCreateAction<(&str, FilesystemUuid, Sectors)> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.changed.is_empty() {
write!(
f,
"The requested filesystems already exist; no action taken"
)
} else {
write!(
f,
"The following filesystems {} were successfully created",
self.changed
.iter()
.map(|(n, u, s)| format!("name: {}, UUID: {}, size: {}", n, u, s))
.collect::<Vec<_>>()
.join("; ")
)
}
}
}
impl Display for SetCreateAction<DevUuid> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.changed.is_empty() {
write!(
f,
"The requested filesystems already exist; no action taken"
)
} else {
write!(
f,
"The following devices with UUIDs {} were successfully added to a pool",
self.changed
.iter()
.map(|u| u.to_string())
.collect::<Vec<_>>()
.join(", ")
)
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum RenameAction<T> {
Identity,
Renamed(T),
NoSource,
}
impl<T> EngineAction for RenameAction<T> {
type Return = T;
fn is_changed(&self) -> bool {
matches!(*self, RenameAction::Renamed(_))
}
fn changed(self) -> Option<T> {
match self {
RenameAction::Renamed(t) => Some(t),
_ => None,
}
}
}
impl Display for RenameAction<DevUuid> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RenameAction::Identity => {
write!(
f,
"Device is already named the target name; no action taken"
)
}
RenameAction::Renamed(uuid) => {
write!(f, "Device with UUID {} was successfully renamed", uuid)
}
RenameAction::NoSource => {
write!(f, "The device requested to be renamed does not exist")
}
}
}
}
impl Display for RenameAction<FilesystemUuid> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RenameAction::Identity => {
write!(
f,
"Filesystem is already named the target name; no action taken"
)
}
RenameAction::Renamed(uuid) => {
write!(f, "Filesystem with UUID {} was successfully renamed", uuid)
}
RenameAction::NoSource => {
write!(f, "The filesystem requested to be renamed does not exist")
}
}
}
}
impl Display for RenameAction<PoolUuid> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RenameAction::Identity => {
write!(f, "Pool is already named the target name; no action taken")
}
RenameAction::Renamed(uuid) => {
write!(f, "Pool with UUID {} was successfully renamed", uuid)
}
RenameAction::NoSource => {
write!(f, "The pool requested to be renamed does not exist")
}
}
}
}
impl Display for RenameAction<Key> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RenameAction::Identity => {
write!(
f,
"Requested passphrase in change passphrase operation was the same as the original"
)
}
RenameAction::Renamed(_) => {
write!(f, "Passphrase was successfully changed")
}
RenameAction::NoSource => {
write!(
f,
"Could not change the passphrase as no passphrase is currently set"
)
}
}
}
}
impl Display for RenameAction<Clevis> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RenameAction::Identity => {
write!(
f,
"Clevis bindings regeneration resulted in no changes to the metadata",
)
}
RenameAction::Renamed(_) => {
write!(f, "Clevis bindings were successfully regenerated")
}
RenameAction::NoSource => {
write!(
f,
"Could not change the Clevis bindings as this pool is not bound to Clevis"
)
}
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum DeleteAction<T> {
Identity,
Deleted(T),
}
impl<T> EngineAction for DeleteAction<T> {
type Return = T;
fn is_changed(&self) -> bool {
matches!(*self, DeleteAction::Deleted(_))
}
fn changed(self) -> Option<T> {
match self {
DeleteAction::Deleted(t) => Some(t),
_ => None,
}
}
}
impl Display for DeleteAction<PoolUuid> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DeleteAction::Deleted(uuid) => {
write!(f, "Pool with UUID {} was deleted successfully", uuid)
}
DeleteAction::Identity => {
write!(
f,
"The pool requested for deletion is already absent; no action taken"
)
}
}
}
}
impl Display for DeleteAction<Clevis> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DeleteAction::Deleted(_) => {
write!(f, "A clevis binding was successfully removed from a pool")
}
DeleteAction::Identity => {
write!(
f,
"The clevis binding requested for removal is already absent; no action taken"
)
}
}
}
}
impl Display for DeleteAction<Key> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DeleteAction::Deleted(_) => {
write!(
f,
"Bindings to a passphrase in the kernel keyring were removed successfully"
)
}
DeleteAction::Identity => {
write!(
f,
"The keyring bindings requested for removal are already absent; no action taken"
)
}
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct SetDeleteAction<T> {
changed: Vec<T>,
}
impl<T> SetDeleteAction<T> {
pub fn new(changed: Vec<T>) -> Self {
SetDeleteAction { changed }
}
pub fn empty() -> Self {
SetDeleteAction { changed: vec![] }
}
}
impl<T> EngineAction for SetDeleteAction<T> {
type Return = Vec<T>;
fn is_changed(&self) -> bool {
!self.changed.is_empty()
}
fn changed(self) -> Option<Vec<T>> {
if self.changed.is_empty() {
None
} else {
Some(self.changed)
}
}
}
impl Display for SetDeleteAction<FilesystemUuid> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.changed.is_empty() {
write!(
f,
"The requested filesystems are already absent; no action taken"
)
} else {
write!(
f,
"Filesystems with UUIDs {} were successfully deleted",
self.changed
.iter()
.map(|u| u.to_string())
.collect::<Vec<_>>()
.join(", ")
)
}
}
}
pub struct RegenAction;
impl Display for RegenAction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"The Clevis bindings were successfully regenerated using the same configuration that was originally supplied"
)
}
}
pub enum StartAction<T> {
Identity,
Started(T),
}
impl Display for StartAction<PoolUuid> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StartAction::Identity => write!(
f,
"The requested pool is already started; no action was taken"
),
StartAction::Started(uuid) => {
write!(f, "The pool with UUID {} was successfully started", uuid)
}
}
}
}
impl<T> EngineAction for StartAction<T> {
type Return = T;
fn is_changed(&self) -> bool {
matches!(self, StartAction::Started(_))
}
fn changed(self) -> Option<Self::Return> {
match self {
StartAction::Started(t) => Some(t),
_ => None,
}
}
}
pub enum StopAction<T> {
Identity,
Stopped(T),
}
impl Display for StopAction<PoolUuid> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StopAction::Identity => write!(
f,
"The requested pool is already stopped; no action was taken"
),
StopAction::Stopped(uuid) => {
write!(f, "The pool with UUID {} was successfully stopped", uuid)
}
}
}
}
impl<T> EngineAction for StopAction<T> {
type Return = T;
fn is_changed(&self) -> bool {
matches!(self, StopAction::Stopped(_))
}
fn changed(self) -> Option<Self::Return> {
match self {
StopAction::Stopped(t) => Some(t),
_ => None,
}
}
}
pub enum GrowAction<T> {
Identity,
Grown(T),
}
impl Display for GrowAction<(PoolUuid, DevUuid)> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
GrowAction::Identity => write!(
f,
"No changes to block device size were detected; no action taken"
),
GrowAction::Grown((pool_uuid, dev_uuid)) => {
write!(f, "Block device with UUID {} belonging to pool with UUID {} was successfully grown and more space is now available to the pool", dev_uuid, pool_uuid)
}
}
}
}
impl<T> EngineAction for GrowAction<T> {
type Return = T;
fn is_changed(&self) -> bool {
matches!(self, GrowAction::Grown(_))
}
fn changed(self) -> Option<Self::Return> {
match self {
GrowAction::Identity => None,
GrowAction::Grown(t) => Some(t),
}
}
}
pub trait ToDisplay {
type Display: Display;
fn to_display(&self) -> Self::Display;
}
pub enum PropChangeAction<T> {
Identity,
NewValue(T),
}
impl<T> ToDisplay for PropChangeAction<Option<T>>
where
T: Display,
{
type Display = PropChangeAction<String>;
fn to_display(&self) -> PropChangeAction<String> {
match self {
PropChangeAction::Identity => PropChangeAction::Identity,
PropChangeAction::NewValue(Some(v)) => {
PropChangeAction::NewValue(format!("a value of {}", v))
}
PropChangeAction::NewValue(None) => {
PropChangeAction::NewValue("an empty value".to_string())
}
}
}
}
impl<T> Display for PropChangeAction<T>
where
T: Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PropChangeAction::Identity => write!(f, "No change was made to the given property"),
PropChangeAction::NewValue(v) => write!(f, "Property was changed to {}", v),
}
}
}
impl<T> EngineAction for PropChangeAction<T> {
type Return = T;
fn is_changed(&self) -> bool {
matches!(self, PropChangeAction::NewValue(_))
}
fn changed(self) -> Option<Self::Return> {
match self {
PropChangeAction::NewValue(t) => Some(t),
PropChangeAction::Identity => None,
}
}
}