From 54d83eb3ef587fb853f8d88f6857211de464ce80 Mon Sep 17 00:00:00 2001 From: Alex Lyn Date: Fri, 24 May 2024 10:42:59 +0800 Subject: [PATCH 1/2] Add get_mut for fields of spec/runtime Fixes #165 Signed-off-by: Alex Lyn --- src/runtime/hooks.rs | 25 +++++++---- src/runtime/linux.rs | 81 +++++++++++++++++++++++------------- src/runtime/miscellaneous.rs | 16 +++++-- src/runtime/mod.rs | 8 ++-- src/runtime/process.rs | 31 +++++++++----- 5 files changed, 110 insertions(+), 51 deletions(-) diff --git a/src/runtime/hooks.rs b/src/runtime/hooks.rs index c3eafdbb6d..c45cecf22e 100644 --- a/src/runtime/hooks.rs +++ b/src/runtime/hooks.rs @@ -1,11 +1,21 @@ use crate::error::OciSpecError; use derive_builder::Builder; -use getset::{CopyGetters, Getters, Setters}; +use getset::{CopyGetters, Getters, MutGetters, Setters}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; #[derive( - Builder, Clone, Debug, Default, Deserialize, Eq, Getters, Setters, PartialEq, Serialize, + Builder, + Clone, + Debug, + Default, + Deserialize, + Eq, + MutGetters, + Getters, + Setters, + PartialEq, + Serialize, )] #[serde(rename_all = "camelCase")] #[builder( @@ -14,7 +24,7 @@ use std::path::PathBuf; setter(into, strip_option), build_fn(error = "OciSpecError") )] -#[getset(get = "pub", set = "pub")] +#[getset(get_mut = "pub", get = "pub", set = "pub")] /// Hooks specifies a command that is run in the container at a particular /// event in the lifecycle (setup and teardown) of a container. pub struct Hooks { @@ -75,6 +85,7 @@ pub struct Hooks { Deserialize, Eq, Getters, + MutGetters, Setters, PartialEq, Serialize, @@ -88,7 +99,7 @@ pub struct Hooks { /// Hook specifies a command that is run at a particular event in the /// lifecycle of a container. pub struct Hook { - #[getset(get = "pub", set = "pub")] + #[getset(get_mut = "pub", get = "pub", set = "pub")] /// Path to the binary to be executed. Following similar semantics to /// [IEEE Std 1003.1-2008 `execv`'s path](https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html). This /// specification extends the IEEE standard in that path MUST be @@ -96,20 +107,20 @@ pub struct Hook { path: PathBuf, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get = "pub", set = "pub")] + #[getset(get_mut = "pub", get = "pub", set = "pub")] /// Arguments used for the binary, including the binary name itself. /// Following the same semantics as [IEEE Std 1003.1-2008 /// `execv`'s argv](https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html). args: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get = "pub", set = "pub")] + #[getset(get_mut = "pub", get = "pub", set = "pub")] /// Additional `key=value` environment variables. Following the same /// semantics as [IEEE Std 1003.1-2008's `environ`](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_01). env: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get_copy = "pub", set = "pub")] + #[getset(get_mut = "pub", get_copy = "pub", set = "pub")] /// Timeout is the number of seconds before aborting the hook. If set, /// timeout MUST be greater than zero. timeout: Option, diff --git a/src/runtime/linux.rs b/src/runtime/linux.rs index fdb37fc2c3..7ebe27a4dc 100644 --- a/src/runtime/linux.rs +++ b/src/runtime/linux.rs @@ -1,11 +1,13 @@ use crate::error::{oci_error, OciSpecError}; use derive_builder::Builder; -use getset::{CopyGetters, Getters, Setters}; +use getset::{CopyGetters, Getters, MutGetters, Setters}; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, path::PathBuf, vec}; -#[derive(Builder, Clone, Debug, Deserialize, Eq, Getters, Setters, PartialEq, Serialize)] +#[derive( + Builder, Clone, Debug, Deserialize, Eq, Getters, MutGetters, Setters, PartialEq, Serialize, +)] #[serde(rename_all = "camelCase")] #[builder( default, @@ -13,7 +15,7 @@ use std::{collections::HashMap, path::PathBuf, vec}; setter(into, strip_option), build_fn(error = "OciSpecError") )] -#[getset(get = "pub", set = "pub")] +#[getset(get_mut = "pub", get = "pub", set = "pub")] /// Linux contains platform-specific configuration for Linux based /// containers. pub struct Linux { @@ -240,6 +242,7 @@ impl LinuxDeviceType { Deserialize, Eq, Getters, + MutGetters, Setters, PartialEq, Serialize, @@ -254,28 +257,28 @@ impl LinuxDeviceType { /// controller pub struct LinuxDeviceCgroup { #[serde(default)] - #[getset(get_copy = "pub", set = "pub")] + #[getset(get_mut = "pub", get_copy = "pub", set = "pub")] /// Allow or deny allow: bool, #[serde(default, rename = "type", skip_serializing_if = "Option::is_none")] - #[getset(get_copy = "pub", set = "pub")] + #[getset(get_mut = "pub", get_copy = "pub", set = "pub")] /// Device type, block, char, etc. typ: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get_copy = "pub", set = "pub")] + #[getset(get_mut = "pub", get_copy = "pub", set = "pub")] /// Device's major number major: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get_copy = "pub", set = "pub")] + #[getset(get_mut = "pub", get_copy = "pub", set = "pub")] /// Device's minor number minor: Option, /// Cgroup access premissions format, rwm. #[serde(default)] - #[getset(get = "pub", set = "pub")] + #[getset(get_mut = "pub", get = "pub", set = "pub")] access: Option, } @@ -688,6 +691,7 @@ pub struct LinuxNetwork { Deserialize, Eq, Getters, + MutGetters, Setters, PartialEq, Serialize, @@ -702,55 +706,65 @@ pub struct LinuxNetwork { /// Resource constraints for container pub struct LinuxResources { #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get = "pub", set = "pub")] + #[getset(get_mut = "pub", get = "pub", set = "pub")] /// Devices configures the device allowlist. devices: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get = "pub", set = "pub")] + #[getset(get_mut = "pub", get = "pub", set = "pub")] /// Memory restriction configuration. memory: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get = "pub", set = "pub")] + #[getset(get_mut = "pub", get = "pub", set = "pub")] /// CPU resource restriction configuration. cpu: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get = "pub", set = "pub")] + #[getset(get_mut = "pub", get = "pub", set = "pub")] /// Task resource restrictions pids: Option, #[serde(default, skip_serializing_if = "Option::is_none", rename = "blockIO")] - #[getset(get = "pub", set = "pub")] + #[getset(get_mut = "pub", get = "pub", set = "pub")] /// BlockIO restriction configuration. block_io: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get = "pub", set = "pub")] + #[getset(get_mut = "pub", get = "pub", set = "pub")] /// Hugetlb limit (in bytes). hugepage_limits: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get = "pub", set = "pub")] + #[getset(get_mut = "pub", get = "pub", set = "pub")] /// Network restriction configuration. network: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get = "pub", set = "pub")] + #[getset(get_mut = "pub", get = "pub", set = "pub")] /// Rdma resource restriction configuration. Limits are a set of key /// value pairs that define RDMA resource limits, where the key /// is device name and value is resource limits. rdma: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get = "pub", set = "pub")] + #[getset(get_mut = "pub", get = "pub", set = "pub")] /// Unified resources. unified: Option>, } #[derive( - Builder, Clone, Copy, CopyGetters, Debug, Default, Deserialize, Eq, PartialEq, Serialize, + Builder, + Clone, + Copy, + CopyGetters, + Debug, + Default, + Deserialize, + Eq, + MutGetters, + PartialEq, + Serialize, )] #[serde(rename_all = "camelCase")] #[builder( @@ -759,7 +773,7 @@ pub struct LinuxResources { setter(into, strip_option), build_fn(error = "OciSpecError") )] -#[getset(get_copy = "pub", set = "pub")] +#[getset(get_mut = "pub", get_copy = "pub", set = "pub")] /// LinuxRdma for Linux cgroup 'rdma' resource management (Linux 4.11). pub struct LinuxRdma { #[serde(skip_serializing_if = "Option::is_none")] @@ -900,6 +914,7 @@ pub fn get_default_namespaces() -> Vec { Deserialize, Eq, Getters, + MutGetters, Setters, PartialEq, Serialize, @@ -915,37 +930,37 @@ pub fn get_default_namespaces() -> Vec { /// file. pub struct LinuxDevice { #[serde(default)] - #[getset(get = "pub", set = "pub")] + #[getset(get_mut = "pub", get = "pub", set = "pub")] /// Path to the device. path: PathBuf, #[serde(rename = "type")] - #[getset(get_copy = "pub", set = "pub")] + #[getset(get_mut = "pub", get_copy = "pub", set = "pub")] /// Device type, block, char, etc.. typ: LinuxDeviceType, #[serde(default)] - #[getset(get_copy = "pub", set = "pub")] + #[getset(get_mut = "pub", get_copy = "pub", set = "pub")] /// Major is the device's major number. major: i64, #[serde(default)] - #[getset(get_copy = "pub", set = "pub")] + #[getset(get_mut = "pub", get_copy = "pub", set = "pub")] /// Minor is the device's minor number. minor: i64, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get_copy = "pub", set = "pub")] + #[getset(get_mut = "pub", get_copy = "pub", set = "pub")] /// FileMode permission bits for the device. file_mode: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get_copy = "pub", set = "pub")] + #[getset(get_mut = "pub", get_copy = "pub", set = "pub")] /// UID of the device. uid: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get_copy = "pub", set = "pub")] + #[getset(get_mut = "pub", get_copy = "pub", set = "pub")] /// Gid of the device. gid: Option, } @@ -1271,7 +1286,17 @@ pub fn get_default_readonly_paths() -> Vec { } #[derive( - Builder, Clone, Debug, Default, Deserialize, Eq, Getters, Setters, PartialEq, Serialize, + Builder, + Clone, + Debug, + Default, + Deserialize, + Eq, + Getters, + MutGetters, + Setters, + PartialEq, + Serialize, )] #[serde(rename_all = "camelCase")] #[builder( @@ -1280,7 +1305,7 @@ pub fn get_default_readonly_paths() -> Vec { setter(into, strip_option), build_fn(error = "OciSpecError") )] -#[getset(get = "pub", set = "pub")] +#[getset(get_mut = "pub", get = "pub", set = "pub")] /// LinuxIntelRdt has container runtime resource constraints for Intel RDT CAT and MBA /// features and flags enabling Intel RDT CMT and MBM features. /// Intel RDT features are available in Linux 4.14 and newer kernel versions. diff --git a/src/runtime/miscellaneous.rs b/src/runtime/miscellaneous.rs index 5c2415ccc1..73a9a7f399 100644 --- a/src/runtime/miscellaneous.rs +++ b/src/runtime/miscellaneous.rs @@ -1,6 +1,6 @@ use crate::error::OciSpecError; use derive_builder::Builder; -use getset::{CopyGetters, Getters, Setters}; +use getset::{CopyGetters, Getters, MutGetters, Setters}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; @@ -40,7 +40,17 @@ impl Default for Root { } #[derive( - Builder, Clone, Debug, Default, Deserialize, Eq, Getters, Setters, PartialEq, Serialize, + Builder, + Clone, + Debug, + Default, + Deserialize, + Eq, + Getters, + MutGetters, + Setters, + PartialEq, + Serialize, )] #[builder( default, @@ -48,7 +58,7 @@ impl Default for Root { setter(into, strip_option), build_fn(error = "OciSpecError") )] -#[getset(get = "pub", set = "pub")] +#[getset(get_mut = "pub", get = "pub", set = "pub")] /// Mount specifies a mount for a container. pub struct Mount { /// Destination is the absolute path where the mount will be placed in diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index a681c81e80..5dbed9357f 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -1,7 +1,7 @@ //! [OCI runtime spec](https://github.com/opencontainers/runtime-spec) types and definitions. use derive_builder::Builder; -use getset::{Getters, Setters}; +use getset::{Getters, MutGetters, Setters}; use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, @@ -35,7 +35,9 @@ pub use vm::*; pub use windows::*; /// Base configuration for the container. -#[derive(Builder, Clone, Debug, Deserialize, Getters, Setters, PartialEq, Eq, Serialize)] +#[derive( + Builder, Clone, Debug, Deserialize, Getters, MutGetters, Setters, PartialEq, Eq, Serialize, +)] #[serde(rename_all = "camelCase")] #[builder( default, @@ -43,7 +45,7 @@ pub use windows::*; setter(into, strip_option), build_fn(error = "OciSpecError") )] -#[getset(get = "pub", set = "pub")] +#[getset(get_mut = "pub", get = "pub", set = "pub")] pub struct Spec { #[serde(default, rename = "ociVersion")] /// MUST be in SemVer v2.0.0 format and specifies the version of the diff --git a/src/runtime/process.rs b/src/runtime/process.rs index 69a1f02216..b6cc005e95 100644 --- a/src/runtime/process.rs +++ b/src/runtime/process.rs @@ -3,12 +3,22 @@ use crate::{ runtime::{Capabilities, Capability}, }; use derive_builder::Builder; -use getset::{CopyGetters, Getters, Setters}; +use getset::{CopyGetters, Getters, MutGetters, Setters}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; #[derive( - Builder, Clone, CopyGetters, Debug, Deserialize, Getters, Setters, Eq, PartialEq, Serialize, + Builder, + Clone, + CopyGetters, + Debug, + Deserialize, + Getters, + MutGetters, + Setters, + Eq, + PartialEq, + Serialize, )] #[serde(rename_all = "camelCase")] #[builder( @@ -30,7 +40,7 @@ pub struct Process { /// ConsoleSize specifies the size of the console. console_size: Option, - #[getset(get = "pub", set = "pub")] + #[getset(get_mut = "pub", get = "pub", set = "pub")] /// User specifies user information for the process. user: User, @@ -41,13 +51,13 @@ pub struct Process { args: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get = "pub", set = "pub")] + #[getset(get_mut = "pub", get = "pub", set = "pub")] /// CommandLine specifies the full command line for the application to /// execute on Windows. command_line: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get = "pub", set = "pub")] + #[getset(get_mut = "pub", get = "pub", set = "pub")] /// Env populates the process environment for the process. env: Option>, @@ -265,6 +275,7 @@ pub struct LinuxRlimit { Default, Deserialize, Getters, + MutGetters, Setters, Eq, PartialEq, @@ -280,28 +291,28 @@ pub struct LinuxRlimit { /// User id (uid) and group id (gid) tracks file permssions. pub struct User { #[serde(default)] - #[getset(get_copy = "pub", set = "pub")] + #[getset(get_mut = "pub", get_copy = "pub", set = "pub")] /// UID is the user id. uid: u32, #[serde(default)] - #[getset(get_copy = "pub", set = "pub")] + #[getset(get_mut = "pub", get_copy = "pub", set = "pub")] /// GID is the group id. gid: u32, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get_copy = "pub", set = "pub")] + #[getset(get_mut = "pub", get_copy = "pub", set = "pub")] /// Specifies the umask of the user. umask: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get = "pub", set = "pub")] + #[getset(get_mut = "pub", get = "pub", set = "pub")] /// AdditionalGids are additional group ids set for the container's /// process. additional_gids: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] - #[getset(get = "pub", set = "pub")] + #[getset(get_mut = "pub", get = "pub", set = "pub")] /// Username is the user name. username: Option, } From e90458f106b4d1d11e82b180f3d93566ef42695c Mon Sep 17 00:00:00 2001 From: Alex Lyn Date: Mon, 27 May 2024 10:21:20 +0800 Subject: [PATCH 2/2] Implement Display Trait instead of ToString Trait MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This ToString trait is automatically implemented for any type which implements the Display trait. As such, ToString shouldn’t be implemented directly: Display should be implemented instead, and you get the ToString implementation for free. With such fixing, It will make CI with lint-clippy happy. Fixes #165 Signed-off-by: Alex Lyn --- .rustfmt.toml | 2 +- src/image/config.rs | 18 ++++++++++++------ src/image/index.rs | 18 ++++++++++++------ src/image/manifest.rs | 18 ++++++++++++------ src/runtime/linux.rs | 23 ++++++++++++++++------- 5 files changed, 53 insertions(+), 26 deletions(-) diff --git a/.rustfmt.toml b/.rustfmt.toml index d4cfb80ff8..bf633e4ed1 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -14,7 +14,7 @@ reorder_imports = true reorder_modules = true remove_nested_parens = true match_arm_leading_pipes = "Never" -fn_args_layout = "Tall" +fn_params_layout = "Tall" edition = "2018" merge_derives = true use_try_shorthand = false diff --git a/src/image/config.rs b/src/image/config.rs index cb42d28885..19889c3477 100644 --- a/src/image/config.rs +++ b/src/image/config.rs @@ -10,6 +10,7 @@ use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer} use std::collections::BTreeMap; use std::{ collections::HashMap, + fmt::Display, io::{Read, Write}, path::Path, }; @@ -250,14 +251,19 @@ impl ImageConfiguration { } } -/// Implement `ToString` directly since we cannot avoid twice memory allocation -/// when using auto-implementaion through `Display`. -impl ToString for ImageConfiguration { - fn to_string(&self) -> String { +/// This ToString trait is automatically implemented for any type which implements the Display trait. +/// As such, ToString shouldn’t be implemented directly: Display should be implemented instead, +/// and you get the ToString implementation for free. +impl Display for ImageConfiguration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // Serde seralization never fails since this is // a combination of String and enums. - self.to_string_pretty() - .expect("ImageConfiguration JSON convertion failed") + write!( + f, + "{}", + self.to_string_pretty() + .expect("ImageConfiguration JSON convertion failed") + ) } } diff --git a/src/image/index.rs b/src/image/index.rs index e09adc345f..20441eb551 100644 --- a/src/image/index.rs +++ b/src/image/index.rs @@ -8,6 +8,7 @@ use getset::{CopyGetters, Getters, Setters}; use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, + fmt::Display, io::{Read, Write}, path::Path, }; @@ -210,14 +211,19 @@ impl Default for ImageIndex { } } -/// Implement `ToString` directly since we cannot avoid twice memory allocation -/// when using auto-implementaion through `Display`. -impl ToString for ImageIndex { - fn to_string(&self) -> String { +/// This ToString trait is automatically implemented for any type which implements the Display trait. +/// As such, ToString shouldn’t be implemented directly: Display should be implemented instead, +/// and you get the ToString implementation for free. +impl Display for ImageIndex { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // Serde seralization never fails since this is // a combination of String and enums. - self.to_string_pretty() - .expect("ImageIndex to JSON convertion failed") + write!( + f, + "{}", + self.to_string_pretty() + .expect("ImageIndex to JSON convertion failed") + ) } } diff --git a/src/image/manifest.rs b/src/image/manifest.rs index c0135429f2..814c634d00 100644 --- a/src/image/manifest.rs +++ b/src/image/manifest.rs @@ -8,6 +8,7 @@ use getset::{CopyGetters, Getters, MutGetters, Setters}; use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, + fmt::Display, io::{Read, Write}, path::Path, }; @@ -221,14 +222,19 @@ impl ImageManifest { } } -/// Implement `ToString` directly since we cannot avoid twice memory allocation -/// when using auto-implementaion through `Display`. -impl ToString for ImageManifest { - fn to_string(&self) -> String { +/// This ToString trait is automatically implemented for any type which implements the Display trait. +/// As such, ToString shouldn’t be implemented directly: Display should be implemented instead, +/// and you get the ToString implementation for free. +impl Display for ImageManifest { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // Serde seralization never fails since this is // a combination of String and enums. - self.to_string_pretty() - .expect("ImageManifest to JSON convertion failed") + write!( + f, + "{}", + self.to_string_pretty() + .expect("ImageManifest to JSON convertion failed") + ) } } diff --git a/src/runtime/linux.rs b/src/runtime/linux.rs index 7ebe27a4dc..0d11040359 100644 --- a/src/runtime/linux.rs +++ b/src/runtime/linux.rs @@ -3,7 +3,7 @@ use crate::error::{oci_error, OciSpecError}; use derive_builder::Builder; use getset::{CopyGetters, Getters, MutGetters, Setters}; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, path::PathBuf, vec}; +use std::{collections::HashMap, fmt::Display, path::PathBuf, vec}; #[derive( Builder, Clone, Debug, Deserialize, Eq, Getters, MutGetters, Setters, PartialEq, Serialize, @@ -282,8 +282,11 @@ pub struct LinuxDeviceCgroup { access: Option, } -impl ToString for LinuxDeviceCgroup { - fn to_string(&self) -> String { +/// This ToString trait is automatically implemented for any type which implements the Display trait. +/// As such, ToString shouldn’t be implemented directly: Display should be implemented instead, +/// and you get the ToString implementation for free. +impl Display for LinuxDeviceCgroup { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let major = self .major .map(|mj| mj.to_string()) @@ -293,7 +296,8 @@ impl ToString for LinuxDeviceCgroup { .map(|mi| mi.to_string()) .unwrap_or_else(|| "*".to_string()); let access = self.access.as_deref().unwrap_or(""); - format!( + write!( + f, "{} {}:{} {}", &self.typ.unwrap_or_default().as_str(), &major, @@ -644,9 +648,14 @@ pub struct LinuxInterfacePriority { priority: u32, } -impl ToString for LinuxInterfacePriority { - fn to_string(&self) -> String { - format!("{} {}\n", self.name, self.priority) +/// This ToString trait is automatically implemented for any type which implements the Display trait. +/// As such, ToString shouldn’t be implemented directly: Display should be implemented instead, +/// and you get the ToString implementation for free. +impl Display for LinuxInterfacePriority { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // Serde seralization never fails since this is + // a combination of String and enums. + writeln!(f, "{} {}", self.name, self.priority) } }