Use stream implementations of the tiles

This commit is contained in:
Skye Jensen 2020-06-04 21:04:06 -04:00
parent 68f2d4e3f1
commit 6b9a5e1609
8 changed files with 77 additions and 327 deletions

View file

@ -1,5 +1,6 @@
use crate::tile::TileModule;
use crate::tile::Block;
use crate::tiles;
use futures::stream::BoxStream;
use serde::Deserialize;
use smart_default::SmartDefault;
use std::env::var;
@ -8,6 +9,7 @@ use std::path::PathBuf;
use structopt::StructOpt;
use tokio::fs::File;
use tokio::prelude::*;
use tokio::time;
#[derive(Deserialize, Clone, Debug, Default)]
#[serde(default)]
@ -82,11 +84,14 @@ pub async fn read_config() -> Result<Config, Box<dyn std::error::Error>> {
Ok(toml::from_slice(&config_contents)?)
}
pub fn process_tile(tile: &TileConfig) -> Box<dyn TileModule> {
pub fn process_tile(
tile: &TileConfig,
) -> BoxStream<'static, Result<Block, Box<dyn std::error::Error + Send + Sync>>> {
let five_secs = time::Duration::from_secs(5);
match tile {
TileConfig::Load => Box::new(tiles::Hostname::new()),
TileConfig::Memory => Box::new(tiles::Memory::new()),
TileConfig::Hostname => Box::new(tiles::Hostname::new()),
TileConfig::Time(c) => Box::new(tiles::Time::from_config(c)),
TileConfig::Load => Box::pin(tiles::load_stream(time::interval(five_secs))),
TileConfig::Memory => Box::pin(tiles::memory_stream(time::interval(five_secs))),
TileConfig::Hostname => Box::pin(tiles::hostname_stream()),
TileConfig::Time(c) => Box::pin(tiles::time_stream(c.clone())),
}
}

View file

@ -1,11 +1,11 @@
pub mod config;
pub mod output;
pub mod tile;
pub mod tiles;
mod config;
mod output;
mod tile;
mod tiles;
use dbus_tokio::connection::new_session_sync;
use futures::channel::mpsc::{channel, Sender};
use futures::{Stream, StreamExt};
use futures::{stream::BoxStream, StreamExt};
use std::fmt::Debug;
use std::sync::Arc;
use uuid::Uuid;
@ -25,33 +25,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (sender, receiver) = channel(1024);
let mut index = 0usize;
let wrap = |module| {
let tile = tile::Tile::new(
index,
sender.clone(),
Uuid::new_v4().to_string().into(),
module,
);
index += 1;
tile
};
let tiles: Vec<tile::Tile> = config
let tiles: Vec<_> = config
.tile
.iter()
.map(config::process_tile)
.map(wrap)
.enumerate()
.map(|(index, stream)| spawn_stream(index, stream, sender.clone()))
.collect();
let num_tiles = tiles.len();
for tile in tiles.into_iter() {
tile.spawn();
}
// let format = "%Y-%m-%d %H:%M:%S".into();
// let short_format = "%H:%M:%S".into();
// let stream = tiles::time::time_stream(format, short_format);
// spawn_stream(4, stream, sender.clone());
drop(sender);
@ -59,9 +41,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
}
#[allow(unused)]
fn spawn_stream<S, E>(index: usize, stream: S, sender: Sender<tile::TileData>)
where
S: Stream<Item = Result<tile::Block, E>> + Send + 'static,
fn spawn_stream<E: 'static>(
index: usize,
stream: BoxStream<'static, Result<tile::Block, E>>,
sender: Sender<tile::TileData>,
) where
E: Debug,
{
tokio::spawn(async move {

View file

@ -1,13 +1,10 @@
use async_trait::async_trait;
use futures::channel::mpsc::{SendError, Sender};
use futures::SinkExt;
use serde::{ser::Serializer, Serialize};
use smart_default::SmartDefault;
use std::fmt::Debug;
use std::sync::Arc;
//use tokio::sync::mpsc::{error::SendError, Sender};
use tokio::task::JoinHandle;
#[allow(unused)]
#[derive(Copy, Clone, Debug, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Alignment {
@ -22,6 +19,7 @@ impl Default for Alignment {
}
}
#[allow(unused)]
#[derive(Copy, Clone, Debug, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Markup {
@ -84,56 +82,3 @@ pub struct TileData {
pub sender_id: usize,
pub block: Block,
}
#[async_trait]
pub trait TileModule: Send + std::fmt::Debug {
async fn run(
&mut self,
sender: &mut BlockSender,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
}
#[derive(Debug)]
pub struct BlockSender {
sender_id: usize,
sender: Sender<TileData>,
instance: Arc<str>,
}
impl BlockSender {
pub async fn send(&mut self, mut block: Block) -> Result<(), SendError> {
block.instance = self.instance.clone();
let data = TileData {
block,
sender_id: self.sender_id,
};
self.sender.send(data).await
}
}
#[derive(Debug)]
pub struct Tile {
sender: BlockSender,
module: Box<dyn TileModule>,
}
impl Tile {
pub fn new(
sender_id: usize,
sender: Sender<TileData>,
instance: Arc<str>,
module: Box<dyn TileModule>,
) -> Self {
Tile {
sender: BlockSender {
sender_id,
sender,
instance,
},
module,
}
}
pub fn spawn(mut self) -> JoinHandle<Result<(), Box<dyn std::error::Error + Send + Sync>>> {
tokio::spawn(async move { self.module.run(&mut self.sender).await })
}
}

View file

@ -1,44 +1,11 @@
use crate::tile::{Block, BlockSender, TileModule};
use async_trait::async_trait;
use crate::tile::Block;
use futures::stream;
use futures::Stream;
use tokio::fs::File;
use tokio::prelude::*;
#[derive(Debug, Default)]
pub struct Hostname;
impl Hostname {
pub fn new() -> Hostname {
Hostname
}
}
#[async_trait]
impl TileModule for Hostname {
async fn run(
&mut self,
sender: &mut BlockSender,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut raw = String::new();
File::open("/proc/sys/kernel/hostname")
.await?
.read_to_string(&mut raw)
.await?;
let block = Block {
full_text: raw.trim_end_matches('\n').into(),
name: "hostname".into(),
..Default::default()
};
sender.send(block).await?;
// What's the hostname gonna do? Change?
Ok(())
}
}
#[allow(unused)]
fn hostname_stream() -> impl Stream<Item = Result<Block, Box<dyn std::error::Error + Send + Sync>>>
{
pub fn hostname_stream(
) -> impl Stream<Item = Result<Block, Box<dyn std::error::Error + Send + Sync>>> {
stream::once(async {
let mut raw = String::new();
File::open("/proc/sys/kernel/hostname")

View file

@ -1,48 +1,10 @@
use crate::tile::{Block, BlockSender, TileModule};
use async_trait::async_trait;
use crate::tile::Block;
use futures::stream::StreamExt;
use std::time::Duration;
use tokio::fs::File;
use tokio::prelude::*;
use tokio::stream::Stream;
use tokio::time::interval;
#[derive(Debug, Default)]
pub struct Load;
impl Load {
pub fn new() -> Self {
Load
}
}
#[async_trait]
impl TileModule for Load {
async fn run(
&mut self,
sender: &mut BlockSender,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut timer = interval(Duration::from_secs(5));
loop {
timer.tick().await;
let mut raw = String::new();
File::open("/proc/loadavg")
.await?
.read_to_string(&mut raw)
.await?;
let (load, _rest) = raw.split_at(raw.find(' ').unwrap_or(0));
let block = Block {
full_text: load.into(),
name: "load".into(),
..Default::default()
};
sender.send(block).await?;
}
}
}
#[allow(unused)]
fn load_stream<T>(
pub fn load_stream<T>(
clock: T,
) -> impl Stream<Item = Result<Block, Box<dyn std::error::Error + Send + Sync>>>
where

View file

@ -1,102 +1,47 @@
use crate::tile::{Block, BlockSender, TileModule};
use async_trait::async_trait;
use crate::tile::Block;
use futures::{Stream, StreamExt};
use std::io;
use std::str;
use std::time::Duration;
use tokio::fs::File;
use tokio::prelude::*;
use tokio::time::interval;
#[derive(Debug, Default)]
pub struct Memory;
impl Memory {
pub fn new() -> Memory {
Memory
fn prettify_kib(kib: u64) -> Box<str> {
if kib > u64::MAX / 1024 {
panic!("Too much memory");
}
fn prettify_kib(kib: u64) -> Box<str> {
if kib > u64::MAX / 1024 {
panic!("Too much memory");
let mut mem = kib;
let mut stages = 0u8;
while mem >= 1024 {
stages += 1;
mem /= 1024;
}
format!(
"{} {}iB",
mem,
match stages {
0 => 'k',
1 => 'M',
2 => 'G',
3 => 'T',
4 => 'P',
5 => 'E',
6 => 'Z',
_ => panic!("Too much memory, for real this time"),
}
let mut mem = kib;
let mut stages = 0u8;
while mem >= 1024 {
stages += 1;
mem /= 1024;
}
format!(
"{} {}iB",
mem,
match stages {
0 => 'k',
1 => 'M',
2 => 'G',
3 => 'T',
4 => 'P',
5 => 'E',
6 => 'Z',
_ => panic!("Too much memory, for real this time"),
}
)
.into_boxed_str()
}
fn extract_value(line: &str) -> Result<u64, Box<dyn std::error::Error + Send + Sync>> {
let mut parts = line.split_whitespace();
parts.next();
Ok(parts
.next()
.ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData))?
.parse()?)
}
)
.into_boxed_str()
}
#[async_trait]
impl TileModule for Memory {
async fn run(
&mut self,
sender: &mut BlockSender,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut timer = interval(Duration::from_secs(5));
let mut raw = [0u8; 256];
loop {
timer.tick().await;
File::open("/proc/meminfo")
.await?
.read_exact(&mut raw)
.await?;
let string_data = str::from_utf8(&raw)?;
let mut lines = string_data.split('\n');
let mem_total = Memory::prettify_kib(Memory::extract_value(
lines
.next()
.ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData))?,
)?);
lines.next();
let mem_avail = Memory::prettify_kib(Memory::extract_value(
lines
.next()
.ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData))?,
)?);
let full_text = format!("{} avail / {}", mem_avail, mem_total).into_boxed_str();
let short_text = format!("{} / {}", mem_avail, mem_total).into_boxed_str();
let block = Block {
full_text,
short_text: Some(short_text),
name: "memory".into(),
..Default::default()
};
sender.send(block).await?;
}
}
fn extract_value(line: &str) -> Result<u64, Box<dyn std::error::Error + Send + Sync>> {
let mut parts = line.split_whitespace();
parts.next();
Ok(parts
.next()
.ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData))?
.parse()?)
}
#[allow(unused)]
fn memory_stream<T>(
pub fn memory_stream<T>(
clock: T,
) -> impl Stream<Item = Result<Block, Box<dyn std::error::Error + Send + Sync>>>
where
@ -110,13 +55,13 @@ where
.await?;
let string_data = str::from_utf8(&raw)?;
let mut lines = string_data.split('\n');
let mem_total = Memory::prettify_kib(Memory::extract_value(
let mem_total = prettify_kib(extract_value(
lines
.next()
.ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData))?,
)?);
lines.next();
let mem_avail = Memory::prettify_kib(Memory::extract_value(
let mem_avail = prettify_kib(extract_value(
lines
.next()
.ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData))?,

View file

@ -2,7 +2,7 @@ pub mod hostname;
pub mod load;
pub mod memory;
pub mod time;
pub use hostname::Hostname;
pub use load::Load;
pub use memory::Memory;
pub use time::Time;
pub use hostname::hostname_stream;
pub use load::load_stream;
pub use memory::memory_stream;
pub use time::time_stream;

View file

@ -1,86 +1,28 @@
use crate::config::TimeConfig;
use crate::tile::{Block, BlockSender, TileModule};
use async_trait::async_trait;
use crate::tile::Block;
use chrono::prelude::*;
use chrono::DateTime;
use futures::future::Future;
use futures::stream::Stream;
use futures_util::ready;
use pin_project::pin_project;
use std::error::Error;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
use tokio::time::{delay_for, delay_until, Delay, Instant};
#[derive(Debug)]
pub struct Time {
format: Box<str>,
short_format: Box<str>,
}
impl Time {
pub fn new() -> Time {
Default::default()
}
pub fn from_config(config: &TimeConfig) -> Time {
Time {
format: config.format.clone(),
short_format: config.short_format.clone(),
}
}
fn send_time(&mut self, time: DateTime<Local>) -> Block {
Block {
full_text: time.format(&self.format).to_string().into(),
short_text: Some(time.format(&self.short_format).to_string().into()),
name: "time".into(),
..Default::default()
}
}
}
impl Default for Time {
fn default() -> Self {
Time {
format: "%Y-%m-%d %H:%M:%S".into(),
short_format: "%H:%M:%S".into(),
}
}
}
#[async_trait]
impl TileModule for Time {
async fn run(
&mut self,
sender: &mut BlockSender,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut time = Local::now();
loop {
sender.send(self.send_time(time)).await?;
time = Local::now();
let millis_part = time.naive_local().timestamp_subsec_millis() as u64;
let delay_ms = 1000u64 - millis_part % 1000; // Don't crash if we hit a leap second
delay_for(Duration::from_millis(delay_ms)).await;
}
}
}
pub fn time_stream(
format: Box<str>,
short_format: Box<str>,
config: TimeConfig,
) -> impl Stream<Item = Result<Block, Box<dyn std::error::Error + Send + Sync>>> {
TimeStream {
format,
short_format,
config,
delay: delay_until(Instant::now()),
}
}
#[pin_project]
struct TimeStream {
format: Box<str>,
short_format: Box<str>,
config: TimeConfig,
#[pin]
delay: Delay,
}
@ -88,8 +30,8 @@ struct TimeStream {
impl TimeStream {
fn send_time(&self, time: DateTime<Local>) -> Block {
Block {
full_text: time.format(&self.format).to_string().into(),
short_text: Some(time.format(&self.short_format).to_string().into()),
full_text: time.format(&self.config.format).to_string().into(),
short_text: Some(time.format(&self.config.short_format).to_string().into()),
name: "time".into(),
..Default::default()
}
@ -104,7 +46,7 @@ impl TimeStream {
}
impl Stream for TimeStream {
type Item = Result<Block, Box<dyn std::error::Error + Send + Sync>>;
type Item = Result<Block, Box<dyn Error + Send + Sync>>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let project = Pin::as_mut(&mut self).project();
ready!(Future::poll(project.delay, cx));