diff --git a/CHANGELOG.md b/CHANGELOG.md index aff0c504..787b13b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## [Unreleased] +### Added +- asus-armoury driver support. WIP, will be adjusted/changed further + ## [v6.1.0-rc1] ### Added diff --git a/asusctl/src/cli_opts.rs b/asusctl/src/cli_opts.rs index 525089bb..1f21195c 100644 --- a/asusctl/src/cli_opts.rs +++ b/asusctl/src/cli_opts.rs @@ -51,6 +51,8 @@ pub enum CliCommand { Scsi(ScsiCommand), #[options(help = "Change bios settings")] Platform(PlatformCommand), + #[options(help = "Change platform settings")] + PlatformNew(PlatformNewCommand), } #[derive(Debug, Clone, Options)] @@ -121,3 +123,11 @@ pub struct PlatformCommand { #[options(no_long, short = "o", help = "get panel overdrive")] pub panel_overdrive_get: bool, } + +#[derive(Options, Debug)] +pub struct PlatformNewCommand { + #[options(help = "print help message")] + pub help: bool, + #[options(free)] + pub free: Vec, +} diff --git a/asusctl/src/main.rs b/asusctl/src/main.rs index eddf5e8d..2f06d4ed 100644 --- a/asusctl/src/main.rs +++ b/asusctl/src/main.rs @@ -13,6 +13,7 @@ use rog_anime::usb::get_anime_type; use rog_anime::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType, Vec2}; use rog_aura::keyboard::{AuraPowerState, LaptopAuraPower}; use rog_aura::{self, AuraDeviceType, AuraEffect, PowerZones}; +use rog_dbus::asus_armoury::AsusArmouryProxyBlocking; use rog_dbus::list_iface_blocking; use rog_dbus::scsi_aura::ScsiAuraProxyBlocking; use rog_dbus::zbus_anime::AnimeProxyBlocking; @@ -140,13 +141,13 @@ where // e.to_owned()).collect(); println!("{}, {:?}", v.0, o); for k in v.1.keys() { if k.as_str() == iface_name { - println!("Found {iface_name} device at {}, {}", v.0, k); + // println!("Found {iface_name} device at {}, {}", v.0, k); paths.push(v.0.clone()); } } } if paths.len() > 1 { - println!("Multiple aura devices found: {paths:?}"); + println!("Multiple asusd interfaces devices found"); } if !paths.is_empty() { let mut ctrl = Vec::new(); @@ -187,6 +188,7 @@ fn do_parsed( Some(CliCommand::Platform(cmd)) => { handle_platform_properties(&conn, supported_properties, cmd)? } + Some(CliCommand::PlatformNew(cmd)) => handle_platform_new_properties(&conn, cmd)?, None => { if (!parsed.show_supported && parsed.kbd_bright.is_none() @@ -1066,3 +1068,53 @@ fn check_systemd_unit_enabled(name: &str) -> bool { } false } + +fn handle_platform_new_properties( + conn: &Connection, + cmd: &PlatformNewCommand, +) -> Result<(), Box> { + { + if cmd.free.is_empty() || cmd.help { + println!("Missing arg or command\n"); + + let usage: Vec = PlatformCommand::usage() + .lines() + .map(|s| s.to_owned()) + .collect(); + } + + if let Ok(attr) = find_iface::("xyz.ljones.AsusArmoury") { + for attr in attr.iter() { + let name = attr.name()?; + let attrs = attr.available_attrs()?; + // dbg!(&name, &attrs); + println!("{name}::"); + if attrs.contains(&"defalt_value".to_string()) { + let v = attr.default_value()?; + println!(" default_value: {v:?}"); + } + if attrs.contains(&"min_value".to_string()) { + let v = attr.min_value()?; + println!(" min_value: {v:?}"); + } + if attrs.contains(&"max_value".to_string()) { + let v = attr.max_value()?; + println!(" max_value: {v}"); + } + if attrs.contains(&"scalar_increment".to_string()) { + let v = attr.scalar_increment()?; + println!(" scalar_increment: {v}"); + } + if attrs.contains(&"possible_values".to_string()) { + let v = attr.possible_values()?; + println!(" possible_values: {v:?}"); + } + if attrs.contains(&"current_value".to_string()) { + let v = attr.current_value()?; + println!(" current_value: {v}"); + } + } + } + } + Ok(()) +} diff --git a/asusd/src/asus_armoury/attr_int.rs b/asusd/src/asus_armoury.rs similarity index 60% rename from asusd/src/asus_armoury/attr_int.rs rename to asusd/src/asus_armoury.rs index 5ee0184d..add0fe71 100644 --- a/asusd/src/asus_armoury/attr_int.rs +++ b/asusd/src/asus_armoury.rs @@ -1,8 +1,11 @@ +use rog_platform::firmware_attributes::FirmwareAttributes; +use zbus::Connection; + use log::error; use rog_platform::firmware_attributes::{AttrValue, Attribute}; use serde::{Deserialize, Serialize}; use zbus::zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Type, Value}; -use zbus::{fdo, interface, Connection}; +use zbus::{fdo, interface}; use crate::error::RogError; use crate::ASUS_ZBUS_PATH; @@ -43,13 +46,43 @@ impl AsusArmouryAttribute { /// If return is `-1` on a property then there is avilable value for that /// property -#[interface(name = "org.asuslinux.AsusArmoury")] +#[interface(name = "xyz.ljones.AsusArmoury")] impl AsusArmouryAttribute { #[zbus(property)] async fn name(&self) -> String { self.0.name().to_string() } + #[zbus(property)] + async fn available_attrs(&self) -> Vec { + let mut attrs = Vec::new(); + if !matches!(self.0.default_value(), AttrValue::None) { + attrs.push("default_value".to_string()); + } + if !matches!(self.0.min_value(), AttrValue::None) { + attrs.push("min_value".to_string()); + } + if !matches!(self.0.max_value(), AttrValue::None) { + attrs.push("max_value".to_string()); + } + if !matches!(self.0.scalar_increment(), AttrValue::None) { + attrs.push("scalar_increment".to_string()); + } + if !matches!(self.0.possible_values(), AttrValue::None) { + attrs.push("possible_values".to_string()); + } + // TODO: Don't unwrap, use error + if let Ok(value) = self.0.current_value().map_err(|e| { + error!("Failed to read: {e:?}"); + e + }) { + if !matches!(value, AttrValue::None) { + attrs.push("current_value".to_string()); + } + } + attrs + } + /// If return is `-1` then there is no default value #[zbus(property)] async fn default_value(&self) -> i32 { @@ -77,7 +110,18 @@ impl AsusArmouryAttribute { #[zbus(property)] async fn scalar_increment(&self) -> i32 { - self.0.scalar_increment().unwrap_or(1) + match self.0.scalar_increment() { + AttrValue::Integer(i) => *i, + _ => -1, + } + } + + #[zbus(property)] + async fn possible_values(&self) -> Vec { + match self.0.possible_values() { + AttrValue::EnumInt(i) => i.clone(), + _ => Vec::default(), + } } #[zbus(property)] @@ -103,3 +147,12 @@ impl AsusArmouryAttribute { })?) } } + +pub async fn start_attributes_zbus(server: &Connection) -> Result<(), RogError> { + for attr in FirmwareAttributes::new().attributes() { + AsusArmouryAttribute::new(attr.clone()) + .start_tasks(server) + .await?; + } + Ok(()) +} diff --git a/asusd/src/asus_armoury/attr_enum_int.rs b/asusd/src/asus_armoury/attr_enum_int.rs deleted file mode 100644 index 4a9c4a8c..00000000 --- a/asusd/src/asus_armoury/attr_enum_int.rs +++ /dev/null @@ -1,89 +0,0 @@ -use log::error; -use rog_platform::firmware_attributes::{AttrValue, Attribute}; -use serde::{Deserialize, Serialize}; -use zbus::zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Type, Value}; -use zbus::{fdo, interface, Connection}; - -use crate::error::RogError; -use crate::ASUS_ZBUS_PATH; - -const MOD_NAME: &str = "asus_armoury"; - -#[derive(Debug, Default, Clone, Deserialize, Serialize, Type, Value, OwnedValue)] -pub struct PossibleValues { - strings: Vec, - nums: Vec, -} - -fn dbus_path_for_attr(attr_name: &str) -> OwnedObjectPath { - ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{attr_name}")).into() -} - -pub struct AsusArmouryAttribute(Attribute); - -impl AsusArmouryAttribute { - pub fn new(attr: Attribute) -> Self { - Self(attr) - } - - pub async fn start_tasks(self, connection: &Connection) -> Result<(), RogError> { - // self.reload() - // .await - // .unwrap_or_else(|err| warn!("Controller error: {}", err)); - let path = dbus_path_for_attr(self.0.name()); - connection - .object_server() - .at(path.clone(), self) - .await - .map_err(|e| error!("Couldn't add server at path: {path}, {e:?}")) - .ok(); - Ok(()) - } -} - -#[interface(name = "org.asuslinux.AsusArmoury")] -impl AsusArmouryAttribute { - #[zbus(property)] - async fn name(&self) -> String { - self.0.name().to_string() - } - - #[zbus(property)] - async fn default_value(&self) -> i32 { - match self.0.default_value() { - AttrValue::Integer(i) => *i, - _ => -1, - } - } - - #[zbus(property)] - async fn possible_values(&self) -> Vec { - match self.0.possible_values() { - AttrValue::EnumInt(i) => i.clone(), - _ => Vec::default(), - } - } - - #[zbus(property)] - async fn current_value(&self) -> fdo::Result { - if let Ok(v) = self.0.current_value() { - if let AttrValue::Integer(i) = v { - return Ok(i); - } - } - Err(fdo::Error::Failed( - "Could not read current value".to_string(), - )) - } - - #[zbus(property)] - async fn set_current_value(&mut self, value: i32) -> fdo::Result<()> { - Ok(self - .0 - .set_current_value(AttrValue::Integer(value)) - .map_err(|e| { - error!("Could not set value: {e:?}"); - e - })?) - } -} diff --git a/asusd/src/asus_armoury/attr_enum_str.rs b/asusd/src/asus_armoury/attr_enum_str.rs deleted file mode 100644 index ed4ba5d5..00000000 --- a/asusd/src/asus_armoury/attr_enum_str.rs +++ /dev/null @@ -1,89 +0,0 @@ -use log::error; -use rog_platform::firmware_attributes::{AttrType, AttrValue, Attribute}; -use serde::{Deserialize, Serialize}; -use zbus::zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Type, Value}; -use zbus::{fdo, interface, Connection}; - -use crate::error::RogError; -use crate::ASUS_ZBUS_PATH; - -const MOD_NAME: &str = "asus_armoury"; - -#[derive(Debug, Default, Clone, Deserialize, Serialize, Type, Value, OwnedValue)] -pub struct PossibleValues { - strings: Vec, - nums: Vec, -} - -fn dbus_path_for_attr(attr_name: &str) -> OwnedObjectPath { - ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{attr_name}")).into() -} - -pub struct AsusArmouryAttribute(Attribute); - -impl AsusArmouryAttribute { - pub fn new(attr: Attribute) -> Self { - Self(attr) - } - - pub async fn start_tasks(self, connection: &Connection) -> Result<(), RogError> { - // self.reload() - // .await - // .unwrap_or_else(|err| warn!("Controller error: {}", err)); - let path = dbus_path_for_attr(self.0.name()); - connection - .object_server() - .at(path.clone(), self) - .await - .map_err(|e| error!("Couldn't add server at path: {path}, {e:?}")) - .ok(); - Ok(()) - } -} - -#[interface(name = "org.asuslinux.AsusArmoury")] -impl AsusArmouryAttribute { - #[zbus(property)] - async fn name(&self) -> String { - self.0.name().to_string() - } - - #[zbus(property)] - async fn default_value(&self) -> String { - match self.0.default_value() { - AttrValue::String(s) => *s, - _ => String::default(), - } - } - - #[zbus(property)] - async fn possible_values(&self) -> Vec { - match self.0.possible_values() { - AttrValue::EnumStr(s) => s.clone(), - _ => Vec::default(), - } - } - - #[zbus(property)] - async fn current_value(&self) -> fdo::Result { - if let Ok(v) = self.0.current_value() { - if let AttrValue::String(s) = v { - return Ok(s); - } - } - Err(fdo::Error::Failed( - "Could not read current value".to_string(), - )) - } - - #[zbus(property)] - async fn set_current_value(&mut self, value: String) -> fdo::Result<()> { - Ok(self - .0 - .set_current_value(AttrValue::String(value)) - .map_err(|e| { - error!("Could not set value: {e:?}"); - e - })?) - } -} diff --git a/asusd/src/asus_armoury/mod.rs b/asusd/src/asus_armoury/mod.rs deleted file mode 100644 index f55cee01..00000000 --- a/asusd/src/asus_armoury/mod.rs +++ /dev/null @@ -1,33 +0,0 @@ -use rog_platform::firmware_attributes::{AttrType, FirmwareAttributes}; -use zbus::Connection; - -use crate::error::RogError; - -pub mod attr_enum_int; -pub mod attr_enum_str; -pub mod attr_int; - -pub async fn start_attributes_zbus(server: &Connection) -> Result<(), RogError> { - for attr in FirmwareAttributes::new().attributes() { - match attr.attribute_type() { - AttrType::MinMax => { - attr_int::AsusArmouryAttribute::new(attr.clone()) - .start_tasks(server) - .await?; - } - AttrType::EnumInt => { - attr_enum_int::AsusArmouryAttribute::new(attr.clone()) - .start_tasks(server) - .await?; - } - AttrType::EnumStr => { - attr_enum_str::AsusArmouryAttribute::new(attr.clone()) - .start_tasks(server) - .await?; - } - - AttrType::Unbounded => {} - } - } - Ok(()) -} diff --git a/data/asusd.service b/data/asusd.service index e948edd7..1e58e8cd 100644 --- a/data/asusd.service +++ b/data/asusd.service @@ -13,6 +13,6 @@ ExecStart=/usr/bin/asusd Restart=on-failure RestartSec=1 Type=dbus -BusName=org.asuslinux.Daemon +BusName=xyz.ljones.Asusd SELinuxContext=system_u:system_r:unconfined_t:s0 #SELinuxContext=system_u:object_r:modules_object_t:s0 diff --git a/design-patterns.md b/design-patterns.md index abe6f135..1557b6de 100644 --- a/design-patterns.md +++ b/design-patterns.md @@ -135,7 +135,7 @@ impl crate::ZbusAdd for CtrlAnimeZbus { } } -#[dbus_interface(name = "org.asuslinux.Daemon")] +#[dbus_interface(name = "xyz.ljones.Asusd")] impl CtrlAnimeZbus { async fn () { let lock = self.inner.lock().await; diff --git a/rog-control-center/translations/en/rog-control-center.po b/rog-control-center/translations/en/rog-control-center.po index ed51f34a..f691529a 100644 --- a/rog-control-center/translations/en/rog-control-center.po +++ b/rog-control-center/translations/en/rog-control-center.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2024-12-26 08:35+0000\n" +"POT-Creation-Date: 2024-12-27 08:21+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/rog-dbus/src/asus_armoury.rs b/rog-dbus/src/asus_armoury.rs new file mode 100644 index 00000000..aacd5dfb --- /dev/null +++ b/rog-dbus/src/asus_armoury.rs @@ -0,0 +1,65 @@ +//! # D-Bus interface proxy for: `xyz.ljones.AsusArmoury` +//! +//! This code was generated by `zbus-xmlgen` `5.0.1` from D-Bus introspection data. +//! Source: `Interface '/xyz/ljones/asus_armoury/nv_temp_target' from service 'xyz.ljones.Asusd' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "xyz.ljones.AsusArmoury", + default_service = "xyz.ljones.Asusd", + default_path = "/xyz/ljones/asus_armoury/nv_temp_target" +)] +pub trait AsusArmoury { + /// A list of the properties this attribute actually uses. Any property + /// not listed will return either an empty array or `-1` + #[zbus(property)] + fn available_attrs(&self) -> zbus::Result>; + + /// CurrentValue property + #[zbus(property)] + fn current_value(&self) -> zbus::Result; + #[zbus(property)] + fn set_current_value(&self, value: i32) -> zbus::Result<()>; + + /// DefaultValue property + #[zbus(property)] + fn default_value(&self) -> zbus::Result; + + /// MaxValue property. Maximum allowed current_value. Returns `-1` if unused or not set. + #[zbus(property)] + fn max_value(&self) -> zbus::Result; + + /// MinValue property. Minimum allowed current_value. Returns `-1` if unused or not set. + #[zbus(property)] + fn min_value(&self) -> zbus::Result; + + /// PossibleValues property. Return the allowed values for `current_value` if used or set, + /// otherwise the array is empty. + #[zbus(property)] + fn possible_values(&self) -> zbus::Result>; + + /// Name property + #[zbus(property)] + fn name(&self) -> zbus::Result; + + /// ScalarIncrement property. The increment steps that `current_value` may take. Returns + /// `-1` if not used or set. + #[zbus(property)] + fn scalar_increment(&self) -> zbus::Result; +} diff --git a/rog-dbus/src/asus_armoury/attr_enum_int.rs b/rog-dbus/src/asus_armoury/attr_enum_int.rs deleted file mode 100644 index 0befc952..00000000 --- a/rog-dbus/src/asus_armoury/attr_enum_int.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Path for iface is such as "/xyz/ljones/asus_armoury/boot_sound" -use zbus::proxy; -#[proxy( - interface = "xyz.ljones.AsusArmoury", - default_service = "xyz.ljones.Asusd" -)] -pub trait AsusArmoury { - /// CurrentValue property - #[zbus(property)] - fn current_value(&self) -> zbus::Result; - #[zbus(property)] - fn set_current_value(&self, value: i32) -> zbus::Result<()>; - - /// DefaultValue property - #[zbus(property)] - fn default_value(&self) -> zbus::Result; - - /// MaxValue property - #[zbus(property)] - fn max_value(&self) -> zbus::Result; - - /// MinValue property - #[zbus(property)] - fn min_value(&self) -> zbus::Result; - - /// Name property - #[zbus(property)] - fn name(&self) -> zbus::Result; - - /// ScalarIncrement property - #[zbus(property)] - fn scalar_increment(&self) -> zbus::Result; -} diff --git a/rog-dbus/src/asus_armoury/attr_enum_str.rs b/rog-dbus/src/asus_armoury/attr_enum_str.rs deleted file mode 100644 index c73ce013..00000000 --- a/rog-dbus/src/asus_armoury/attr_enum_str.rs +++ /dev/null @@ -1,25 +0,0 @@ -//! Path for iface is such as "/xyz/ljones/asus_armoury/boot_sound" -use zbus::proxy; -#[proxy( - interface = "xyz.ljones.AsusArmoury", - default_service = "xyz.ljones.Asusd" -)] -pub trait AsusArmoury { - /// CurrentValue property - #[zbus(property)] - fn current_value(&self) -> zbus::Result; - #[zbus(property)] - fn set_current_value(&self, value: String) -> zbus::Result<()>; - - /// DefaultValue property - #[zbus(property)] - fn default_value(&self) -> zbus::Result; - - /// Name property - #[zbus(property)] - fn name(&self) -> zbus::Result; - - /// PossibleValues property - #[zbus(property)] - fn possible_values(&self) -> zbus::Result>; -} diff --git a/rog-dbus/src/asus_armoury/attr_int.rs b/rog-dbus/src/asus_armoury/attr_int.rs deleted file mode 100644 index c8fa233f..00000000 --- a/rog-dbus/src/asus_armoury/attr_int.rs +++ /dev/null @@ -1,25 +0,0 @@ -use zbus::proxy; -#[proxy( - interface = "xyz.ljones.AsusArmoury", - default_service = "xyz.ljones.Asusd", - default_path = "/xyz/ljones/asus_armoury/boot_sound" -)] -pub trait AsusArmoury { - /// CurrentValue property - #[zbus(property)] - fn current_value(&self) -> zbus::Result; - #[zbus(property)] - fn set_current_value(&self, value: i32) -> zbus::Result<()>; - - /// DefaultValue property - #[zbus(property)] - fn default_value(&self) -> zbus::Result; - - /// Name property - #[zbus(property)] - fn name(&self) -> zbus::Result; - - /// PossibleValues property - #[zbus(property)] - fn possible_values(&self) -> zbus::Result>; -} diff --git a/rog-dbus/src/asus_armoury/mod.rs b/rog-dbus/src/asus_armoury/mod.rs deleted file mode 100644 index d772e4cc..00000000 --- a/rog-dbus/src/asus_armoury/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod attr_enum_int; -pub mod attr_enum_str; -pub mod attr_int; diff --git a/rog-platform/src/firmware_attributes.rs b/rog-platform/src/firmware_attributes.rs index c081b011..36f80dfa 100644 --- a/rog-platform/src/firmware_attributes.rs +++ b/rog-platform/src/firmware_attributes.rs @@ -4,7 +4,7 @@ use std::path::{Path, PathBuf}; use serde::{Deserialize, Serialize}; use typeshare::typeshare; -use zbus::zvariant::{OwnedValue, Type, Value}; +use zbus::zvariant::Type; use crate::error::PlatformError; @@ -31,14 +31,6 @@ fn read_string(path: &Path) -> Result { Ok(buf.trim().to_string()) } -#[derive(Debug, Clone, Deserialize, Serialize, Type, Value, OwnedValue)] -pub enum AttrType { - MinMax = 0, - EnumInt = 1, - EnumStr = 2, - Unbounded = 3, -} - #[derive(Debug, Default, Clone, PartialEq, PartialOrd)] pub enum AttrValue { Integer(i32), @@ -57,7 +49,7 @@ pub struct Attribute { possible_values: AttrValue, min_value: AttrValue, max_value: AttrValue, - scalar_increment: Option, + scalar_increment: AttrValue, base_path: PathBuf, } @@ -70,20 +62,6 @@ impl Attribute { &self.help } - pub fn attribute_type(&self) -> AttrType { - let mut attr_type = AttrType::Unbounded; - match self.max_value { - AttrValue::Integer(_) => attr_type = AttrType::MinMax, - _ => {} - } - match self.possible_values { - AttrValue::EnumInt(_) => attr_type = AttrType::EnumInt, - AttrValue::EnumStr(_) => attr_type = AttrType::EnumStr, - _ => {} - } - attr_type - } - /// Read the `current_value` directly from the attribute path pub fn current_value(&self) -> Result { match read_string(&self.base_path.join("current_value")) { @@ -129,8 +107,8 @@ impl Attribute { &self.max_value } - pub fn scalar_increment(&self) -> Option { - self.scalar_increment + pub fn scalar_increment(&self) -> &AttrValue { + &self.scalar_increment } /// Read all the immutable values to struct data. These should *never* @@ -138,7 +116,7 @@ impl Attribute { /// subject to `firmware_attributes` class changes in kernel. fn read_base_values( base_path: &Path, - ) -> (AttrValue, AttrValue, AttrValue, AttrValue, Option) { + ) -> (AttrValue, AttrValue, AttrValue, AttrValue, AttrValue) { let default_value = match read_string(&base_path.join("default_value")) { Ok(val) => { if let Ok(int) = val.parse::() { @@ -171,7 +149,10 @@ impl Attribute { .ok() .map(AttrValue::Integer) .unwrap_or_default(); - let scalar_increment = read_i32(&base_path.join("scalar_increment")).ok(); + let scalar_increment = read_i32(&base_path.join("scalar_increment")) + .ok() + .map(AttrValue::Integer) + .unwrap_or_default(); ( default_value,