Minor test of platform attributes

This commit is contained in:
Luke D. Jones
2024-12-27 21:21:32 +13:00
parent a1a9c7077a
commit fd3384decc
16 changed files with 200 additions and 333 deletions

View File

@@ -2,6 +2,9 @@
## [Unreleased] ## [Unreleased]
### Added
- asus-armoury driver support. WIP, will be adjusted/changed further
## [v6.1.0-rc1] ## [v6.1.0-rc1]
### Added ### Added

View File

@@ -51,6 +51,8 @@ pub enum CliCommand {
Scsi(ScsiCommand), Scsi(ScsiCommand),
#[options(help = "Change bios settings")] #[options(help = "Change bios settings")]
Platform(PlatformCommand), Platform(PlatformCommand),
#[options(help = "Change platform settings")]
PlatformNew(PlatformNewCommand),
} }
#[derive(Debug, Clone, Options)] #[derive(Debug, Clone, Options)]
@@ -121,3 +123,11 @@ pub struct PlatformCommand {
#[options(no_long, short = "o", help = "get panel overdrive")] #[options(no_long, short = "o", help = "get panel overdrive")]
pub panel_overdrive_get: bool, pub panel_overdrive_get: bool,
} }
#[derive(Options, Debug)]
pub struct PlatformNewCommand {
#[options(help = "print help message")]
pub help: bool,
#[options(free)]
pub free: Vec<String>,
}

View File

@@ -13,6 +13,7 @@ use rog_anime::usb::get_anime_type;
use rog_anime::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType, Vec2}; use rog_anime::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType, Vec2};
use rog_aura::keyboard::{AuraPowerState, LaptopAuraPower}; use rog_aura::keyboard::{AuraPowerState, LaptopAuraPower};
use rog_aura::{self, AuraDeviceType, AuraEffect, PowerZones}; use rog_aura::{self, AuraDeviceType, AuraEffect, PowerZones};
use rog_dbus::asus_armoury::AsusArmouryProxyBlocking;
use rog_dbus::list_iface_blocking; use rog_dbus::list_iface_blocking;
use rog_dbus::scsi_aura::ScsiAuraProxyBlocking; use rog_dbus::scsi_aura::ScsiAuraProxyBlocking;
use rog_dbus::zbus_anime::AnimeProxyBlocking; use rog_dbus::zbus_anime::AnimeProxyBlocking;
@@ -140,13 +141,13 @@ where
// e.to_owned()).collect(); println!("{}, {:?}", v.0, o); // e.to_owned()).collect(); println!("{}, {:?}", v.0, o);
for k in v.1.keys() { for k in v.1.keys() {
if k.as_str() == iface_name { 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()); paths.push(v.0.clone());
} }
} }
} }
if paths.len() > 1 { if paths.len() > 1 {
println!("Multiple aura devices found: {paths:?}"); println!("Multiple asusd interfaces devices found");
} }
if !paths.is_empty() { if !paths.is_empty() {
let mut ctrl = Vec::new(); let mut ctrl = Vec::new();
@@ -187,6 +188,7 @@ fn do_parsed(
Some(CliCommand::Platform(cmd)) => { Some(CliCommand::Platform(cmd)) => {
handle_platform_properties(&conn, supported_properties, cmd)? handle_platform_properties(&conn, supported_properties, cmd)?
} }
Some(CliCommand::PlatformNew(cmd)) => handle_platform_new_properties(&conn, cmd)?,
None => { None => {
if (!parsed.show_supported if (!parsed.show_supported
&& parsed.kbd_bright.is_none() && parsed.kbd_bright.is_none()
@@ -1066,3 +1068,53 @@ fn check_systemd_unit_enabled(name: &str) -> bool {
} }
false false
} }
fn handle_platform_new_properties(
conn: &Connection,
cmd: &PlatformNewCommand,
) -> Result<(), Box<dyn std::error::Error>> {
{
if cmd.free.is_empty() || cmd.help {
println!("Missing arg or command\n");
let usage: Vec<String> = PlatformCommand::usage()
.lines()
.map(|s| s.to_owned())
.collect();
}
if let Ok(attr) = find_iface::<AsusArmouryProxyBlocking>("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(())
}

View File

@@ -1,8 +1,11 @@
use rog_platform::firmware_attributes::FirmwareAttributes;
use zbus::Connection;
use log::error; use log::error;
use rog_platform::firmware_attributes::{AttrValue, Attribute}; use rog_platform::firmware_attributes::{AttrValue, Attribute};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use zbus::zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Type, Value}; use zbus::zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Type, Value};
use zbus::{fdo, interface, Connection}; use zbus::{fdo, interface};
use crate::error::RogError; use crate::error::RogError;
use crate::ASUS_ZBUS_PATH; 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 /// If return is `-1` on a property then there is avilable value for that
/// property /// property
#[interface(name = "org.asuslinux.AsusArmoury")] #[interface(name = "xyz.ljones.AsusArmoury")]
impl AsusArmouryAttribute { impl AsusArmouryAttribute {
#[zbus(property)] #[zbus(property)]
async fn name(&self) -> String { async fn name(&self) -> String {
self.0.name().to_string() self.0.name().to_string()
} }
#[zbus(property)]
async fn available_attrs(&self) -> Vec<String> {
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 /// If return is `-1` then there is no default value
#[zbus(property)] #[zbus(property)]
async fn default_value(&self) -> i32 { async fn default_value(&self) -> i32 {
@@ -77,7 +110,18 @@ impl AsusArmouryAttribute {
#[zbus(property)] #[zbus(property)]
async fn scalar_increment(&self) -> i32 { 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<i32> {
match self.0.possible_values() {
AttrValue::EnumInt(i) => i.clone(),
_ => Vec::default(),
}
} }
#[zbus(property)] #[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(())
}

View File

@@ -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<String>,
nums: Vec<i32>,
}
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<i32> {
match self.0.possible_values() {
AttrValue::EnumInt(i) => i.clone(),
_ => Vec::default(),
}
}
#[zbus(property)]
async fn current_value(&self) -> fdo::Result<i32> {
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
})?)
}
}

View File

@@ -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<String>,
nums: Vec<i32>,
}
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<String> {
match self.0.possible_values() {
AttrValue::EnumStr(s) => s.clone(),
_ => Vec::default(),
}
}
#[zbus(property)]
async fn current_value(&self) -> fdo::Result<String> {
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
})?)
}
}

View File

@@ -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(())
}

View File

@@ -13,6 +13,6 @@ ExecStart=/usr/bin/asusd
Restart=on-failure Restart=on-failure
RestartSec=1 RestartSec=1
Type=dbus Type=dbus
BusName=org.asuslinux.Daemon BusName=xyz.ljones.Asusd
SELinuxContext=system_u:system_r:unconfined_t:s0 SELinuxContext=system_u:system_r:unconfined_t:s0
#SELinuxContext=system_u:object_r:modules_object_t:s0 #SELinuxContext=system_u:object_r:modules_object_t:s0

View File

@@ -135,7 +135,7 @@ impl crate::ZbusAdd for CtrlAnimeZbus {
} }
} }
#[dbus_interface(name = "org.asuslinux.Daemon")] #[dbus_interface(name = "xyz.ljones.Asusd")]
impl CtrlAnimeZbus { impl CtrlAnimeZbus {
async fn <zbus method>() { async fn <zbus method>() {
let lock = self.inner.lock().await; let lock = self.inner.lock().await;

View File

@@ -2,7 +2,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"

View File

@@ -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<Vec<String>>;
/// CurrentValue property
#[zbus(property)]
fn current_value(&self) -> zbus::Result<i32>;
#[zbus(property)]
fn set_current_value(&self, value: i32) -> zbus::Result<()>;
/// DefaultValue property
#[zbus(property)]
fn default_value(&self) -> zbus::Result<i32>;
/// MaxValue property. Maximum allowed current_value. Returns `-1` if unused or not set.
#[zbus(property)]
fn max_value(&self) -> zbus::Result<i32>;
/// MinValue property. Minimum allowed current_value. Returns `-1` if unused or not set.
#[zbus(property)]
fn min_value(&self) -> zbus::Result<i32>;
/// 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<Vec<i32>>;
/// Name property
#[zbus(property)]
fn name(&self) -> zbus::Result<String>;
/// 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<i32>;
}

View File

@@ -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<i32>;
#[zbus(property)]
fn set_current_value(&self, value: i32) -> zbus::Result<()>;
/// DefaultValue property
#[zbus(property)]
fn default_value(&self) -> zbus::Result<i32>;
/// MaxValue property
#[zbus(property)]
fn max_value(&self) -> zbus::Result<i32>;
/// MinValue property
#[zbus(property)]
fn min_value(&self) -> zbus::Result<i32>;
/// Name property
#[zbus(property)]
fn name(&self) -> zbus::Result<String>;
/// ScalarIncrement property
#[zbus(property)]
fn scalar_increment(&self) -> zbus::Result<i32>;
}

View File

@@ -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<String>;
#[zbus(property)]
fn set_current_value(&self, value: String) -> zbus::Result<()>;
/// DefaultValue property
#[zbus(property)]
fn default_value(&self) -> zbus::Result<String>;
/// Name property
#[zbus(property)]
fn name(&self) -> zbus::Result<String>;
/// PossibleValues property
#[zbus(property)]
fn possible_values(&self) -> zbus::Result<Vec<String>>;
}

View File

@@ -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<i32>;
#[zbus(property)]
fn set_current_value(&self, value: i32) -> zbus::Result<()>;
/// DefaultValue property
#[zbus(property)]
fn default_value(&self) -> zbus::Result<i32>;
/// Name property
#[zbus(property)]
fn name(&self) -> zbus::Result<String>;
/// PossibleValues property
#[zbus(property)]
fn possible_values(&self) -> zbus::Result<Vec<i32>>;
}

View File

@@ -1,3 +0,0 @@
pub mod attr_enum_int;
pub mod attr_enum_str;
pub mod attr_int;

View File

@@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use typeshare::typeshare; use typeshare::typeshare;
use zbus::zvariant::{OwnedValue, Type, Value}; use zbus::zvariant::Type;
use crate::error::PlatformError; use crate::error::PlatformError;
@@ -31,14 +31,6 @@ fn read_string(path: &Path) -> Result<String, PlatformError> {
Ok(buf.trim().to_string()) 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)] #[derive(Debug, Default, Clone, PartialEq, PartialOrd)]
pub enum AttrValue { pub enum AttrValue {
Integer(i32), Integer(i32),
@@ -57,7 +49,7 @@ pub struct Attribute {
possible_values: AttrValue, possible_values: AttrValue,
min_value: AttrValue, min_value: AttrValue,
max_value: AttrValue, max_value: AttrValue,
scalar_increment: Option<i32>, scalar_increment: AttrValue,
base_path: PathBuf, base_path: PathBuf,
} }
@@ -70,20 +62,6 @@ impl Attribute {
&self.help &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 /// Read the `current_value` directly from the attribute path
pub fn current_value(&self) -> Result<AttrValue, PlatformError> { pub fn current_value(&self) -> Result<AttrValue, PlatformError> {
match read_string(&self.base_path.join("current_value")) { match read_string(&self.base_path.join("current_value")) {
@@ -129,8 +107,8 @@ impl Attribute {
&self.max_value &self.max_value
} }
pub fn scalar_increment(&self) -> Option<i32> { pub fn scalar_increment(&self) -> &AttrValue {
self.scalar_increment &self.scalar_increment
} }
/// Read all the immutable values to struct data. These should *never* /// 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. /// subject to `firmware_attributes` class changes in kernel.
fn read_base_values( fn read_base_values(
base_path: &Path, base_path: &Path,
) -> (AttrValue, AttrValue, AttrValue, AttrValue, Option<i32>) { ) -> (AttrValue, AttrValue, AttrValue, AttrValue, AttrValue) {
let default_value = match read_string(&base_path.join("default_value")) { let default_value = match read_string(&base_path.join("default_value")) {
Ok(val) => { Ok(val) => {
if let Ok(int) = val.parse::<i32>() { if let Ok(int) = val.parse::<i32>() {
@@ -171,7 +149,10 @@ impl Attribute {
.ok() .ok()
.map(AttrValue::Integer) .map(AttrValue::Integer)
.unwrap_or_default(); .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, default_value,