mirror of
https://gitlab.steamos.cloud/holo/steamos-manager.git
synced 2025-07-07 07:00:27 -04:00
Add initial dbus interface xml descripiton.
Much of this will change, but needed something to start with. Based on notes at https://gitlab.steamos.cloud/jupiter/tasks/-/issues/894 Use objectserver to get introspection done for us. Change to session bus for now since system bus will need a config file to allow us to take the name. Will add later. Implement one quick say_hello dbus method to start. Add copyright headers.
This commit is contained in:
parent
302fe3c063
commit
701ff23987
5 changed files with 347 additions and 31 deletions
|
@ -9,3 +9,4 @@ edition = "2021"
|
||||||
async-std = { version = "1.12.0", features = ["attributes"] }
|
async-std = { version = "1.12.0", features = ["attributes"] }
|
||||||
serde = { version = "1.0.188", features = ["derive"] }
|
serde = { version = "1.0.188", features = ["derive"] }
|
||||||
zbus = "3.14.1"
|
zbus = "3.14.1"
|
||||||
|
zbus_macros = "3.14.1"
|
||||||
|
|
226
com.steampowered.SteamOSManager1.xml
Normal file
226
com.steampowered.SteamOSManager1.xml
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
<!DOCTYPE node PUBLIC
|
||||||
|
"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||||
|
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Copyright © 2023 Collabora Ltd.
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included
|
||||||
|
in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
|
||||||
|
<!--
|
||||||
|
com.steampowered.SteamOSManager1
|
||||||
|
@short_description: Interface to control various aspects of SteamOS
|
||||||
|
-->
|
||||||
|
<interface name="com.steampowered.SteamOSManager1">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Version:
|
||||||
|
|
||||||
|
The version of this interface implemented by this object.
|
||||||
|
This file documents version 1 of the interface.
|
||||||
|
-->
|
||||||
|
<property name="Version" type="u" access="read"/>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
FactoryReset:
|
||||||
|
@success: True on success (should reboot next, to finish the reset.). False otherwise.
|
||||||
|
|
||||||
|
Perform factory reset of device.
|
||||||
|
runs steamos-factory-reset script for now.
|
||||||
|
|
||||||
|
Returns true on success, false on failure.
|
||||||
|
-->
|
||||||
|
<method name="FactoryReset">
|
||||||
|
<arg type="b" name="success" direction="out"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
DisableWifiPowerManagement:
|
||||||
|
|
||||||
|
Disable wifi chip's powermanagement.
|
||||||
|
-->
|
||||||
|
<method name="DisableWifiPowerManagement">
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
EnableFanControl:
|
||||||
|
@enable: Whether fan control should be enabled.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<method name="EnableFanControl">
|
||||||
|
<arg type="b" name="enable" direction="in"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
HardwareCheckSupport:
|
||||||
|
@supported: True if all hardware is supported. False otherwise.
|
||||||
|
|
||||||
|
Checks for unsupported hardware.
|
||||||
|
-->
|
||||||
|
<method name="HardwareCheckSupport">
|
||||||
|
<arg type="b" name="supported" direction="out"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ReadALSCalibration:
|
||||||
|
@gain: The gain from the ALS calibration, in floating point.
|
||||||
|
|
||||||
|
Read ALS calibration value.
|
||||||
|
Note: Will give -1.0 on error running script.
|
||||||
|
-->
|
||||||
|
<method name="ReadALSCalibration">
|
||||||
|
<arg type="d" name="gain" direction="out"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
UpdateBIOS:
|
||||||
|
@success: True on bios update success. False otherwise.
|
||||||
|
|
||||||
|
Perform a bios update.
|
||||||
|
-->
|
||||||
|
<method name="UpdateBIOS">
|
||||||
|
<arg type="b" name="success" direction="out"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
UpdateDock:
|
||||||
|
@success: True on dock update script success. False otherwise.
|
||||||
|
|
||||||
|
Perform a dock firmware update.
|
||||||
|
-->
|
||||||
|
<method name="UpdateDock">
|
||||||
|
<arg type="b" name="success" direction="out"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
TrimDevices:
|
||||||
|
@success: True on script success. False otherwise.
|
||||||
|
|
||||||
|
Perform fstrim on disk devices.
|
||||||
|
-->
|
||||||
|
<method name="TrimDevices">
|
||||||
|
<arg type="b" name="success" direction="out"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
FormatSDCard:
|
||||||
|
@success: True on script success. False otherwise.
|
||||||
|
|
||||||
|
Format the inserted sd card.
|
||||||
|
-->
|
||||||
|
<method name="FormatSDCard">
|
||||||
|
<arg type="b" name="success" direction="out"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
SetGPUPerformanceLevel:
|
||||||
|
@level: The level to use (TODO: Fill in range here and also check it in code?)
|
||||||
|
@auto: True if we should set back to auto. False to use level above.
|
||||||
|
@success: True on success. False otherwise.
|
||||||
|
|
||||||
|
Set the GPU performance level to the given value.
|
||||||
|
-->
|
||||||
|
<method name="SetGPUPerformanceLevel">
|
||||||
|
<arg type="i" name="level" direction="in"/>
|
||||||
|
<arg type="b" name="auto" direction="in"/>
|
||||||
|
<arg type="b" name="success" direction="out"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
SetGPUClocks:
|
||||||
|
@clocks: The clocks to use.
|
||||||
|
@clear: True if we should clear to go back to defaults. False to use clocks above.
|
||||||
|
@success: True on success. False otherwise.
|
||||||
|
|
||||||
|
Set the GPU clocks to the given value.
|
||||||
|
-->
|
||||||
|
<method name="SetGPUClocks">
|
||||||
|
<arg type="i" name="clocks" direction="in"/>
|
||||||
|
<arg type="b" name="clear" direction="in"/>
|
||||||
|
<arg type="b" name="success" direction="out"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
SetTDPLimit:
|
||||||
|
@limit: The upper limit to use.
|
||||||
|
@success: True on success. False otherwise.
|
||||||
|
|
||||||
|
Set the TDP limit to the given value.
|
||||||
|
-->
|
||||||
|
<method name="SetTDPLimit">
|
||||||
|
<arg type="i" name="limit" direction="in"/>
|
||||||
|
<arg type="b" name="success" direction="out"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
SetBacklight:
|
||||||
|
@value: The backlight brightness value to use.
|
||||||
|
@success: True on success. False otherwise.
|
||||||
|
|
||||||
|
Set the backlight brightness to the given value.
|
||||||
|
-->
|
||||||
|
<method name="SetBacklight">
|
||||||
|
<arg type="i" name="value" direction="in"/>
|
||||||
|
<arg type="b" name="success" direction="out"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
SetALSIntegrationTime:
|
||||||
|
@seconds: The number of seconds to use for ALS integration.
|
||||||
|
@success: True on success. False otherwise.
|
||||||
|
|
||||||
|
Set the ALS integration time to the given value.
|
||||||
|
-->
|
||||||
|
<method name="SetALSIntegrationTime">
|
||||||
|
<arg type="i" name="seconds" direction="in"/>
|
||||||
|
<arg type="b" name="success" direction="out"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
SetLEDBrightness
|
||||||
|
@value: The value to usse for LED brightness.
|
||||||
|
@success: True on success. False otherwise.
|
||||||
|
|
||||||
|
Set the LED brightness to the given value.
|
||||||
|
-->
|
||||||
|
<method name="SetLEDBrightness">
|
||||||
|
<arg type="i" name="value" direction="in"/>
|
||||||
|
<arg type="b" name="success" direction="out"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
SetContentAdaptiveBrightness
|
||||||
|
@value: The value to usse for content adaptive brightness.
|
||||||
|
@success: True on success. False otherwise.
|
||||||
|
|
||||||
|
Set the content adaptive brightness to the given value.
|
||||||
|
-->
|
||||||
|
<method name="SetContentAdaptiveBrightness">
|
||||||
|
<arg type="i" name="value" direction="in"/>
|
||||||
|
<arg type="b" name="success" direction="out"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
</node>
|
58
src/lib.rs
58
src/lib.rs
|
@ -1,19 +1,47 @@
|
||||||
use std::{fs, io};
|
/*
|
||||||
|
* Copyright © 2023 Collabora Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::fs;
|
||||||
use std::{error::Error, future::pending};
|
use std::{error::Error, future::pending};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use zbus::{Connection, ConnectionBuilder, Result, dbus_interface, zvariant::Value};
|
use zbus::{Connection, ConnectionBuilder, Result, dbus_interface, zvariant::{from_slice, to_bytes, EncodingContext, Value}};
|
||||||
|
|
||||||
|
pub mod manager;
|
||||||
|
|
||||||
// We use s(teamos)m(anager) prefix on all types to help avoid conflicts
|
// We use s(teamos)m(anager) prefix on all types to help avoid conflicts
|
||||||
|
|
||||||
// Types of api we support, so far only dbus and script.
|
// Types of api we support, so far only dbus and script.
|
||||||
// For dbus type we call into other dbus apis specified.
|
|
||||||
// For script type we run a script and provide stdout, stderr, and exitcode back.
|
// For script type we run a script and provide stdout, stderr, and exitcode back.
|
||||||
|
// For SysFS type we manipulate sys fs values, on/off or setting a specific value
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub enum SmApiType {
|
pub enum SmApiType {
|
||||||
DBusType = 0,
|
|
||||||
ScriptType = 1,
|
ScriptType = 1,
|
||||||
|
SysFSType = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
// SmDBusApi represents a dbus api to be called
|
// SmDBusApi represents a dbus api to be called
|
||||||
|
@ -31,13 +59,21 @@ pub struct SmDbusApi {
|
||||||
pub struct SmScript {
|
pub struct SmScript {
|
||||||
path: String
|
path: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SmSysfs represents a read/write to a sysfs path or paths
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct SmSysfs {
|
||||||
|
path: String,
|
||||||
|
// value: zbus::zvariant::Value<'a>
|
||||||
|
}
|
||||||
// An SmOperation is what happens when an incoming dbus method is called.
|
// An SmOperation is what happens when an incoming dbus method is called.
|
||||||
// If the SmEntry type is DBusType this should be a DBusApi with the data neede.
|
// If the SmEntry type is DBusType this should be a DBusApi with the data neede.
|
||||||
// Otherwise it should be a script with the path to execute
|
// Otherwise it should be a script with the path to execute
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub enum SmOperation {
|
pub enum SmOperation {
|
||||||
SmScript(String),
|
SmScript(String),
|
||||||
SmDbusApi(String, String, String)
|
SmDbusApi(String, String, String),
|
||||||
|
// SmSysfs(String, zbus::zvariant::Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each api config file contains one or more entries.
|
// Each api config file contains one or more entries.
|
||||||
|
@ -48,12 +84,12 @@ pub struct SmEntry {
|
||||||
outgoing: SmOperation, // TBD: Either the outgoing zbus method or a script to run
|
outgoing: SmOperation, // TBD: Either the outgoing zbus method or a script to run
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initialize_apis(path: String) -> Result<(Vec::<SmEntry>)>
|
pub fn initialize_apis(path: String) -> Result<Vec::<SmEntry>>
|
||||||
{
|
{
|
||||||
let res = Vec::<SmEntry>::new();
|
let res = Vec::<SmEntry>::new();
|
||||||
for file in fs::read_dir(path)? {
|
// for file in fs::read_dir(path)? {
|
||||||
// Deserialize the file and add SmEntry to res
|
// Deserialize the file and add SmEntry to res
|
||||||
}
|
// }
|
||||||
return Ok(res);
|
return Ok(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,10 +97,10 @@ pub fn create_dbus_apis(connection: zbus::Connection, entries: Vec::<SmEntry>) -
|
||||||
{
|
{
|
||||||
// Create each of the given apis as dbus methods that users, etc.
|
// Create each of the given apis as dbus methods that users, etc.
|
||||||
// can use to call into us.
|
// can use to call into us.
|
||||||
for api in entries
|
// for api in entries
|
||||||
{
|
// {
|
||||||
//
|
//
|
||||||
|
|
||||||
}
|
// }
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
53
src/main.rs
53
src/main.rs
|
@ -1,33 +1,48 @@
|
||||||
use std::io::ErrorKind;
|
/*
|
||||||
|
* Copyright © 2023 Collabora Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use zbus::{ConnectionBuilder, Result};
|
||||||
|
|
||||||
use steamos_manager::*;
|
use steamos_manager::*;
|
||||||
|
|
||||||
use zbus::{Connection, Result};
|
|
||||||
|
|
||||||
#[async_std::main]
|
#[async_std::main]
|
||||||
async fn main() -> Result<()>
|
async fn main() -> Result<()>
|
||||||
{
|
{
|
||||||
// This daemon is responsible for creating a dbus api that steam client can use to do various OS
|
// This daemon is responsible for creating a dbus api that steam client can use to do various OS
|
||||||
// level things (change brightness, etc.) In order to do that it reads a folder of dbus api
|
// level things. It implements com.steampowered.SteamOSManager1 interface
|
||||||
// configuration files and exposes each configuration with the api in the config file. In order
|
|
||||||
// to know what to do with each it gets the information from the same config file about whether
|
|
||||||
// to run a script or call some other dbus api.
|
|
||||||
|
|
||||||
let session_connection = Connection::session().await?;
|
let manager = manager::SMManager {};
|
||||||
session_connection.request_name("com.steampowered.SteamOSManager").await?;
|
|
||||||
|
|
||||||
let result = initialize_apis("/usr/share/steamos-manager".to_string());
|
let _system_connection = ConnectionBuilder::session()?
|
||||||
match result {
|
.name("com.steampowered.SteamOSManager1")?
|
||||||
Ok(manager_apis) => {
|
.serve_at("/com/steampowered/SteamOSManager1", manager)?
|
||||||
let worked: bool = create_dbus_apis(session_connection, manager_apis);
|
.build()
|
||||||
}
|
.await?;
|
||||||
Err(error) => {
|
|
||||||
println!("There was an error reading configuration files, doing nothing. {:?}", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loop
|
loop
|
||||||
{
|
{
|
||||||
|
std::future::pending::<()>().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
38
src/manager.rs
Normal file
38
src/manager.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2023 Collabora Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use zbus_macros::dbus_interface;
|
||||||
|
use zbus::{ObjectServer, SignalContext, MessageHeader};
|
||||||
|
pub struct SMManager {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[dbus_interface(name = "com.steampowered.SteamOSManager1")]
|
||||||
|
impl SMManager {
|
||||||
|
async fn say_hello(&self, name: &str) -> String {
|
||||||
|
format!("Hello {}!", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue