Start implementing write
This commit is contained in:
parent
1268d2df99
commit
2ecd31e9fe
3 changed files with 267 additions and 88 deletions
|
@ -45,7 +45,7 @@ pub struct Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
pub fn connect(info: DeviceInfo, token: &str, key: &str) -> Result<Self> {
|
pub async fn connect(info: DeviceInfo, token: &str, key: &str) -> Result<Self> {
|
||||||
let mut socket = Err(Error::msg(""));
|
let mut socket = Err(Error::msg(""));
|
||||||
|
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
|
@ -87,7 +87,7 @@ impl Device {
|
||||||
me.authenticate()?;
|
me.authenticate()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
me.refresh_status()?;
|
me.refresh_status().await?;
|
||||||
|
|
||||||
Ok(me)
|
Ok(me)
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ impl Device {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refresh_status(&mut self) -> Result<()> {
|
pub async fn refresh_status(&mut self) -> Result<()> {
|
||||||
let mut cmds = vec![self.device_backend.build_query()];
|
let mut cmds = vec![self.device_backend.build_query()];
|
||||||
|
|
||||||
if self.sub_type == 0 {
|
if self.sub_type == 0 {
|
||||||
|
@ -130,7 +130,7 @@ impl Device {
|
||||||
let mut buf = [0; 512];
|
let mut buf = [0; 512];
|
||||||
let bytes_read = match self.socket.lock().unwrap().read(&mut buf) {
|
let bytes_read = match self.socket.lock().unwrap().read(&mut buf) {
|
||||||
Ok(b) => b,
|
Ok(b) => b,
|
||||||
Err(_) => break,
|
Err(_) => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
if bytes_read == 0 {
|
if bytes_read == 0 {
|
||||||
|
@ -147,11 +147,17 @@ impl Device {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_update<F>(&mut self, f: F)
|
pub async fn set_attribute(&self, attribute: &str, value: AttributeValue) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_update<F>(mut self, f: F) -> Self
|
||||||
where
|
where
|
||||||
F: Fn(&HashMap<&'static str, AttributeValue>) -> Result<()> + 'static,
|
F: Fn(&HashMap<&'static str, AttributeValue>) -> Result<()> + 'static,
|
||||||
{
|
{
|
||||||
self.updates.push(Box::new(f));
|
self.updates.push(Box::new(f));
|
||||||
|
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_message(&mut self, msg: &[u8]) -> Result<ParseMessage> {
|
fn parse_message(&mut self, msg: &[u8]) -> Result<ParseMessage> {
|
||||||
|
@ -235,7 +241,7 @@ mod test {
|
||||||
use futures::future::try_join;
|
use futures::future::try_join;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
use crate::{device::Device, Cloud, Startup};
|
use crate::{device::Device, devices::AttributeValue, Cloud, Startup};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn verify_hex() -> Result<()> {
|
async fn verify_hex() -> Result<()> {
|
||||||
|
@ -248,7 +254,7 @@ mod test {
|
||||||
let key_hex = b"*[R\x00\xc2\xc0ML\x81\x1d\x05P\xe1\xdc[1CT6\xb9[wM*\x88\xd7\xe4ma\xfd\x96i";
|
let key_hex = b"*[R\x00\xc2\xc0ML\x81\x1d\x05P\xe1\xdc[1CT6\xb9[wM*\x88\xd7\xe4ma\xfd\x96i";
|
||||||
|
|
||||||
for device_info in devices {
|
for device_info in devices {
|
||||||
let device = Device::connect(device_info, PY_TOKEN, PY_KEY)?;
|
let device = Device::connect(device_info, PY_TOKEN, PY_KEY).await?;
|
||||||
|
|
||||||
assert_eq!(&device.token, token_hex);
|
assert_eq!(&device.token, token_hex);
|
||||||
assert_eq!(&device.key, key_hex);
|
assert_eq!(&device.key, key_hex);
|
||||||
|
@ -265,7 +271,7 @@ mod test {
|
||||||
const PY_KEY: &str = "0fc0c56ea8124414a362e6449ee45ba92558a54f159d4937af697e405f2326b9";
|
const PY_KEY: &str = "0fc0c56ea8124414a362e6449ee45ba92558a54f159d4937af697e405f2326b9";
|
||||||
|
|
||||||
for device_info in devices {
|
for device_info in devices {
|
||||||
Device::connect(device_info, PY_TOKEN, PY_KEY)?;
|
Device::connect(device_info, PY_TOKEN, PY_KEY).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -282,6 +288,7 @@ mod test {
|
||||||
let (token, key) = cloud.keys(device_info.id).await?;
|
let (token, key) = cloud.keys(device_info.id).await?;
|
||||||
|
|
||||||
Device::connect(device_info, &token, &key)
|
Device::connect(device_info, &token, &key)
|
||||||
|
.await
|
||||||
.context(format!("\ntoken: {token}\nkey: {key}"))?;
|
.context(format!("\ntoken: {token}\nkey: {key}"))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +310,39 @@ mod test {
|
||||||
addr: SocketAddr::new(std::net::IpAddr::V4(Ipv4Addr::new(192, 168, 178, 94)), 6444),
|
addr: SocketAddr::new(std::net::IpAddr::V4(Ipv4Addr::new(192, 168, 178, 94)), 6444),
|
||||||
};
|
};
|
||||||
|
|
||||||
Device::connect(device_info, token, key)?;
|
Device::connect(device_info, token, key).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn write() -> Result<()> {
|
||||||
|
let token = "dead840607856a4b84c1a9d94ce8f553b50c037b65fa1ca22126de339c367eb765b231b4525d5b8336c48fe5dae38439bbb5e31282ed3790ff98a48049401dca";
|
||||||
|
let key = "50e77947dc63426db3883c7616613410044b02567f5240a5baf789391b2e5a79";
|
||||||
|
|
||||||
|
let device_info = crate::DeviceInfo {
|
||||||
|
id: 152832116426242,
|
||||||
|
model: "760EY015".to_string(),
|
||||||
|
sn: "0000E1541760EY01534091D002581H2R".to_string(),
|
||||||
|
protocol: 3,
|
||||||
|
device_type: 225,
|
||||||
|
addr: SocketAddr::new(std::net::IpAddr::V4(Ipv4Addr::new(192, 168, 178, 94)), 6444),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut device = Device::connect(device_info, token, key)
|
||||||
|
.await?
|
||||||
|
.register_update(|attributes| {
|
||||||
|
println!("{attributes:?}");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
device
|
||||||
|
.set_attribute("attribute", AttributeValue::Bool(false))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
device.refresh_status().await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::command::*;
|
||||||
use super::{AttributeValue, DeviceBackend};
|
use super::{AttributeValue, DeviceBackend};
|
||||||
|
|
||||||
pub struct E1 {
|
pub struct E1 {
|
||||||
modes: HashMap<u32, String>,
|
modes: HashMap<u8, String>,
|
||||||
attributes: HashMap<&'static str, AttributeValue>,
|
attributes: HashMap<&'static str, AttributeValue>,
|
||||||
|
|
||||||
status: [&'static str; 5],
|
status: [&'static str; 5],
|
||||||
|
@ -51,15 +51,15 @@ impl E1 {
|
||||||
|
|
||||||
let attributes = [
|
let attributes = [
|
||||||
("power", AttributeValue::Bool(false)),
|
("power", AttributeValue::Bool(false)),
|
||||||
("status", AttributeValue::Int(0)),
|
("status", AttributeValue::String(None)),
|
||||||
("mode", AttributeValue::Int(0)),
|
("mode", AttributeValue::String(None)),
|
||||||
("additional", AttributeValue::Int(0)),
|
("additional", AttributeValue::Int(0)),
|
||||||
("uv", AttributeValue::Bool(false)),
|
("uv", AttributeValue::Bool(false)),
|
||||||
("dry", AttributeValue::Bool(false)),
|
("dry", AttributeValue::Bool(false)),
|
||||||
("dry_status", AttributeValue::Bool(false)),
|
("dry_status", AttributeValue::Bool(false)),
|
||||||
("door", AttributeValue::Bool(false)),
|
("door_closed", AttributeValue::Bool(false)),
|
||||||
("rinse_aid", AttributeValue::Bool(false)),
|
("rinse_aid_shortage", AttributeValue::Bool(false)),
|
||||||
("salt", AttributeValue::Bool(false)),
|
("salt_shortage", AttributeValue::Bool(false)),
|
||||||
("child_lock", AttributeValue::Bool(false)),
|
("child_lock", AttributeValue::Bool(false)),
|
||||||
("storage", AttributeValue::Bool(false)),
|
("storage", AttributeValue::Bool(false)),
|
||||||
("storage_progress", AttributeValue::Bool(false)),
|
("storage_progress", AttributeValue::Bool(false)),
|
||||||
|
@ -98,7 +98,12 @@ impl DeviceBackend for E1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_message(&mut self, msg: &[u8]) {
|
fn process_message(&mut self, msg: &[u8]) {
|
||||||
CommandE1Response::new(msg).update_attributes(&mut self.attributes);
|
CommandE1Response::new(msg).update_attributes(
|
||||||
|
&mut self.attributes,
|
||||||
|
&self.modes,
|
||||||
|
&self.status,
|
||||||
|
&self.progress,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_attribute(&self, attribute: &str, value: &str) -> () {
|
fn set_attribute(&self, attribute: &str, value: &str) -> () {
|
||||||
|
@ -303,7 +308,13 @@ impl CommandE1Response {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_attributes(&mut self, attributes: &mut HashMap<&'static str, AttributeValue>) {
|
pub fn update_attributes(
|
||||||
|
&mut self,
|
||||||
|
attributes: &mut HashMap<&'static str, AttributeValue>,
|
||||||
|
modes: &HashMap<u8, String>,
|
||||||
|
status: &[&str],
|
||||||
|
progress: &[&str],
|
||||||
|
) {
|
||||||
let message_type = self.command.header().message_type();
|
let message_type = self.command.header().message_type();
|
||||||
let body = self.command.body();
|
let body = self.command.body();
|
||||||
let body_type = self.command.body().body_type();
|
let body_type = self.command.body().body_type();
|
||||||
|
@ -312,93 +323,155 @@ impl CommandE1Response {
|
||||||
|| ((message_type == MessageType::Query || message_type == MessageType::Notify1)
|
|| ((message_type == MessageType::Query || message_type == MessageType::Notify1)
|
||||||
&& body_type == 0)
|
&& body_type == 0)
|
||||||
{
|
{
|
||||||
attributes.get_mut("power").unwrap().set(body[1] > 0);
|
attributes.get_mut("power").unwrap().set(
|
||||||
attributes.get_mut("status").unwrap().set(body[1]);
|
#[cfg(debug_assertions)]
|
||||||
attributes.get_mut("mode").unwrap().set(body[2]);
|
"power",
|
||||||
attributes.get_mut("additional").unwrap().set(body[3]);
|
body[1] > 0,
|
||||||
|
);
|
||||||
|
attributes.get_mut("status").unwrap().set(
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
"status",
|
||||||
|
status[body[1] as usize],
|
||||||
|
);
|
||||||
|
attributes.get_mut("mode").unwrap().set(
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
"mode",
|
||||||
|
&modes[&body[2]],
|
||||||
|
);
|
||||||
|
attributes.get_mut("additional").unwrap().set(
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
"additional",
|
||||||
|
body[3],
|
||||||
|
);
|
||||||
|
|
||||||
// 0 - open, 1 - close
|
// 0 - open, 1 - close
|
||||||
attributes
|
attributes.get_mut("door_closed").unwrap().set(
|
||||||
.get_mut("door")
|
#[cfg(debug_assertions)]
|
||||||
.unwrap()
|
"door_closed",
|
||||||
.set((body[5] & 0x01) == 0);
|
(body[5] & 0x01) == 0,
|
||||||
|
);
|
||||||
|
|
||||||
// 0 - enough, 1 - shortage
|
// 0 - enough, 1 - shortage
|
||||||
attributes
|
attributes.get_mut("rinse_aid_shortage").unwrap().set(
|
||||||
.get_mut("rinse_aid")
|
#[cfg(debug_assertions)]
|
||||||
.unwrap()
|
"rinse_aid_shortage",
|
||||||
.set((body[5] & 0x02) > 0);
|
(body[5] & 0x02) > 0,
|
||||||
|
);
|
||||||
|
|
||||||
// e - enough, 1 - shortage
|
// 0 - enough, 1 - shortage
|
||||||
attributes
|
attributes.get_mut("salt_shortage").unwrap().set(
|
||||||
.get_mut("salt")
|
#[cfg(debug_assertions)]
|
||||||
.unwrap()
|
"salt_shortage",
|
||||||
.set((body[5] & 0x04) > 0);
|
(body[5] & 0x04) > 0,
|
||||||
|
);
|
||||||
|
|
||||||
let start_pause = (body[5] & 0x08) > 0;
|
let start_pause = (body[5] & 0x08) > 0;
|
||||||
|
|
||||||
if start_pause {
|
if start_pause {
|
||||||
self.start = true;
|
self.start = true;
|
||||||
} else if attributes["status"].byte() == 2 || attributes["status"].byte() == 3 {
|
} else if attributes["status"].str() == Some("Delay")
|
||||||
|
|| attributes["status"].str() == Some("Running")
|
||||||
|
{
|
||||||
self.start = false;
|
self.start = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes
|
attributes.get_mut("child_lock").unwrap().set(
|
||||||
.get_mut("child_lock")
|
#[cfg(debug_assertions)]
|
||||||
.unwrap()
|
"child_lock",
|
||||||
.set((body[5] & 0x10) > 0);
|
(body[5] & 0x10) > 0,
|
||||||
attributes.get_mut("uv").unwrap().set((body[4] & 0x02) > 0);
|
);
|
||||||
attributes.get_mut("dry").unwrap().set((body[4] & 0x10) > 0);
|
attributes.get_mut("uv").unwrap().set(
|
||||||
attributes
|
#[cfg(debug_assertions)]
|
||||||
.get_mut("dry_status")
|
"uv",
|
||||||
.unwrap()
|
(body[4] & 0x02) > 0,
|
||||||
.set((body[4] & 0x20) > 0);
|
);
|
||||||
attributes
|
attributes.get_mut("dry").unwrap().set(
|
||||||
.get_mut("storage")
|
#[cfg(debug_assertions)]
|
||||||
.unwrap()
|
"dry",
|
||||||
.set((body[5] & 0x20) > 0);
|
(body[4] & 0x10) > 0,
|
||||||
attributes
|
);
|
||||||
.get_mut("storage_progress")
|
attributes.get_mut("dry_status").unwrap().set(
|
||||||
.unwrap()
|
#[cfg(debug_assertions)]
|
||||||
.set((body[5] & 0x40) > 0);
|
"dry_status",
|
||||||
attributes.get_mut("time_remaining").unwrap().set(body[6]);
|
(body[4] & 0x20) > 0,
|
||||||
attributes.get_mut("progress").unwrap().set(body[9]);
|
);
|
||||||
attributes
|
attributes.get_mut("storage").unwrap().set(
|
||||||
.get_mut("storage_remaining")
|
#[cfg(debug_assertions)]
|
||||||
.unwrap()
|
"storage",
|
||||||
.set(if body.len() > 18 {
|
(body[5] & 0x20) > 0,
|
||||||
|
);
|
||||||
|
attributes.get_mut("storage_progress").unwrap().set(
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
"storage_progress",
|
||||||
|
(body[5] & 0x40) > 0,
|
||||||
|
);
|
||||||
|
attributes.get_mut("time_remaining").unwrap().set(
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
"time_remaining",
|
||||||
|
body[6],
|
||||||
|
);
|
||||||
|
attributes.get_mut("progress").unwrap().set(
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
"progress",
|
||||||
|
progress[body[9] as usize],
|
||||||
|
);
|
||||||
|
attributes.get_mut("storage_remaining").unwrap().set(
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
"storage_remaining",
|
||||||
|
if body.len() > 18 {
|
||||||
body[11]
|
body[11]
|
||||||
} else {
|
} else {
|
||||||
false.into()
|
false.into()
|
||||||
});
|
},
|
||||||
attributes.get_mut("temperature").unwrap().set(body[11]);
|
);
|
||||||
attributes
|
attributes.get_mut("temperature").unwrap().set(
|
||||||
.get_mut("humidity")
|
#[cfg(debug_assertions)]
|
||||||
.unwrap()
|
"temperature",
|
||||||
.set(if body.len() > 33 {
|
body[11],
|
||||||
|
);
|
||||||
|
attributes.get_mut("humidity").unwrap().set(
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
"humidity",
|
||||||
|
if body.len() > 33 {
|
||||||
body[33].into()
|
body[33].into()
|
||||||
} else {
|
} else {
|
||||||
AttributeValue::from(None)
|
AttributeValue::from(None)
|
||||||
});
|
},
|
||||||
attributes
|
);
|
||||||
.get_mut("waterswitch")
|
attributes.get_mut("waterswitch").unwrap().set(
|
||||||
.unwrap()
|
#[cfg(debug_assertions)]
|
||||||
.set((body[4] & 0x04) > 0);
|
"waterswitch",
|
||||||
attributes
|
(body[4] & 0x04) > 0,
|
||||||
.get_mut("water_lack")
|
);
|
||||||
.unwrap()
|
attributes.get_mut("water_lack").unwrap().set(
|
||||||
.set((body[5] & 0x80) > 0);
|
#[cfg(debug_assertions)]
|
||||||
attributes.get_mut("error_code").unwrap().set(body[10]);
|
"water_lack",
|
||||||
attributes.get_mut("softwater").unwrap().set(body[13]);
|
(body[5] & 0x80) > 0,
|
||||||
attributes.get_mut("wrong_operation").unwrap().set(body[16]);
|
);
|
||||||
attributes
|
attributes.get_mut("error_code").unwrap().set(
|
||||||
.get_mut("bright")
|
#[cfg(debug_assertions)]
|
||||||
.unwrap()
|
"error_code",
|
||||||
.set(if body.len() > 24 {
|
body[10],
|
||||||
|
);
|
||||||
|
attributes.get_mut("softwater").unwrap().set(
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
"softwater",
|
||||||
|
body[13],
|
||||||
|
);
|
||||||
|
attributes.get_mut("wrong_operation").unwrap().set(
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
"wrong_operation",
|
||||||
|
body[16],
|
||||||
|
);
|
||||||
|
attributes.get_mut("bright").unwrap().set(
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
"bright",
|
||||||
|
if body.len() > 24 {
|
||||||
body[24].into()
|
body[24].into()
|
||||||
} else {
|
} else {
|
||||||
AttributeValue::from(None)
|
AttributeValue::from(None)
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,13 +12,68 @@ pub enum AttributeValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AttributeValue {
|
impl AttributeValue {
|
||||||
pub fn set(&mut self, value: impl Into<Self>) {
|
pub fn set(&mut self, #[cfg(debug_assertions)] attribute_name: &str, value: impl Into<Self>) {
|
||||||
match (self, value.into()) {
|
match (self, value.into()) {
|
||||||
(Self::String(current), Self::String(new)) => *current = new,
|
(Self::String(current), Self::String(new)) => {
|
||||||
(Self::String(current), Self::Bool(new)) => *current = Some(new.to_string()),
|
#[cfg(debug_assertions)]
|
||||||
(Self::String(current), Self::Int(new)) => *current = Some(new.to_string()),
|
{
|
||||||
(Self::Bool(current), Self::Bool(new)) => *current = new,
|
if *current != new {
|
||||||
(Self::Int(current), Self::Int(new)) => *current = new,
|
println!(
|
||||||
|
"Attribute ({attribute_name}) changed from {current:?} to {new:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*current = new;
|
||||||
|
}
|
||||||
|
(Self::String(current), Self::Bool(new)) => {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
if *current != Some(new.to_string()) {
|
||||||
|
println!(
|
||||||
|
"Attribute ({attribute_name}) changed from {current:?} to {new:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*current = Some(new.to_string());
|
||||||
|
}
|
||||||
|
(Self::String(current), Self::Int(new)) => {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
if *current != Some(new.to_string()) {
|
||||||
|
println!(
|
||||||
|
"Attribute ({attribute_name}) changed from {current:?} to {new:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*current = Some(new.to_string());
|
||||||
|
}
|
||||||
|
(Self::Bool(current), Self::Bool(new)) => {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
if *current != new {
|
||||||
|
println!(
|
||||||
|
"Attribute ({attribute_name}) changed from {current:?} to {new:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*current = new;
|
||||||
|
}
|
||||||
|
(Self::Int(current), Self::Int(new)) => {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
if *current != new {
|
||||||
|
println!(
|
||||||
|
"Attribute ({attribute_name}) changed from {current:?} to {new:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*current = new;
|
||||||
|
}
|
||||||
|
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
|
@ -46,6 +101,18 @@ impl AttributeValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&str> for AttributeValue {
|
||||||
|
fn from(value: &str) -> Self {
|
||||||
|
Self::String(Some(value.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&String> for AttributeValue {
|
||||||
|
fn from(value: &String) -> Self {
|
||||||
|
Self::String(Some(value.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<String> for AttributeValue {
|
impl From<String> for AttributeValue {
|
||||||
fn from(value: String) -> Self {
|
fn from(value: String) -> Self {
|
||||||
Self::String(Some(value))
|
Self::String(Some(value))
|
||||||
|
|
Loading…
Reference in a new issue