Ran rustfmt
This commit is contained in:
@@ -1,14 +1,18 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use indication::{
|
||||||
use indication::{CodePage, ConnectAttempt, Connection, Erase, FileTransfer, Hello, Model, Oia, Passthru, Popup, Proxy, RunResult, Screen, ScreenMode, Scroll, Setting, Stats, TerminalName, Thumb, Tls, TlsHello, TraceFile, UiError};
|
CodePage, ConnectAttempt, Connection, Erase, FileTransfer, Hello, Model, Oia, Passthru, Popup,
|
||||||
|
Proxy, RunResult, Screen, ScreenMode, Scroll, Setting, Stats, TerminalName, Thumb, Tls,
|
||||||
|
TlsHello, TraceFile, UiError,
|
||||||
|
};
|
||||||
use operation::{Fail, Register, Run, Succeed};
|
use operation::{Fail, Register, Run, Succeed};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub mod operation;
|
|
||||||
pub mod indication;
|
pub mod indication;
|
||||||
|
pub mod operation;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
pub enum Indication {
|
pub enum Indication {
|
||||||
Bell{}, // TODO: make sure this emits/parses {"bell": {}}
|
Bell {}, // TODO: make sure this emits/parses {"bell": {}}
|
||||||
/// Indicates that the host connection has changed state.
|
/// Indicates that the host connection has changed state.
|
||||||
Connection(Connection),
|
Connection(Connection),
|
||||||
/// A new host connection is being attempted
|
/// A new host connection is being attempted
|
||||||
@@ -30,10 +34,10 @@ pub enum Indication {
|
|||||||
state: bool,
|
state: bool,
|
||||||
},
|
},
|
||||||
/// File transfer state change
|
/// File transfer state change
|
||||||
#[serde(rename="ft")]
|
#[serde(rename = "ft")]
|
||||||
FileTransfer(FileTransfer),
|
FileTransfer(FileTransfer),
|
||||||
/// An XTerm escape sequence requested a new icon name
|
/// An XTerm escape sequence requested a new icon name
|
||||||
Icon{
|
Icon {
|
||||||
text: String,
|
text: String,
|
||||||
},
|
},
|
||||||
/// The first message sent
|
/// The first message sent
|
||||||
@@ -68,13 +72,13 @@ pub enum Indication {
|
|||||||
/// Error in b3270's input
|
/// Error in b3270's input
|
||||||
UiError(UiError),
|
UiError(UiError),
|
||||||
/// Xterm escape sequence requested a change to the window title
|
/// Xterm escape sequence requested a change to the window title
|
||||||
WindowTitle{
|
WindowTitle {
|
||||||
text: String,
|
text: String,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
#[serde(rename_all="kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub enum InitializeIndication {
|
pub enum InitializeIndication {
|
||||||
CodePages(Vec<CodePage>),
|
CodePages(Vec<CodePage>),
|
||||||
/// Indicates that the host connection has changed state.
|
/// Indicates that the host connection has changed state.
|
||||||
@@ -88,7 +92,9 @@ pub enum InitializeIndication {
|
|||||||
/// Change in the state of the Operator Information Area
|
/// Change in the state of the Operator Information Area
|
||||||
Oia(Oia),
|
Oia(Oia),
|
||||||
/// Set of supported prefixes
|
/// Set of supported prefixes
|
||||||
Prefixes{value: String},
|
Prefixes {
|
||||||
|
value: String,
|
||||||
|
},
|
||||||
/// List of supported proxies
|
/// List of supported proxies
|
||||||
Proxies(Vec<Proxy>),
|
Proxies(Vec<Proxy>),
|
||||||
/// Screen dimensions/characteristics changed
|
/// Screen dimensions/characteristics changed
|
||||||
@@ -106,7 +112,7 @@ pub enum InitializeIndication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
#[serde(rename_all="kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub enum Operation {
|
pub enum Operation {
|
||||||
/// Run an action
|
/// Run an action
|
||||||
Run(Run),
|
Run(Run),
|
||||||
@@ -117,4 +123,3 @@ pub enum Operation {
|
|||||||
/// Tell b3270 that a passthru action succeeded
|
/// Tell b3270 that a passthru action succeeded
|
||||||
Succeed(Succeed),
|
Succeed(Succeed),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use crate::b3270::types::{Color, GraphicRendition};
|
use crate::b3270::types::{Color, GraphicRendition};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
|
||||||
#[serde(rename_all="kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub enum ActionCause {
|
pub enum ActionCause {
|
||||||
Command,
|
Command,
|
||||||
Default,
|
Default,
|
||||||
@@ -22,27 +22,27 @@ pub enum ActionCause {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
pub struct CodePage{
|
pub struct CodePage {
|
||||||
/// The canonical name of the code page
|
/// The canonical name of the code page
|
||||||
pub name: String,
|
pub name: String,
|
||||||
#[serde(default, skip_serializing_if="Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub aliases: Vec<String>,
|
pub aliases: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
pub struct Connection{
|
pub struct Connection {
|
||||||
/// New connection state
|
/// New connection state
|
||||||
pub state: ConnectionState,
|
pub state: ConnectionState,
|
||||||
/// Host name, if connected
|
/// Host name, if connected
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub host: Option<String>,
|
pub host: Option<String>,
|
||||||
/// Source of the connection
|
/// Source of the connection
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub cause: Option<ActionCause>,
|
pub cause: Option<ActionCause>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all="kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
pub enum ComposeType {
|
pub enum ComposeType {
|
||||||
Std,
|
Std,
|
||||||
@@ -50,7 +50,7 @@ pub enum ComposeType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all="kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
pub enum ConnectionState {
|
pub enum ConnectionState {
|
||||||
NotConnected,
|
NotConnected,
|
||||||
@@ -65,20 +65,20 @@ pub enum ConnectionState {
|
|||||||
ConnectedUnbound,
|
ConnectedUnbound,
|
||||||
ConnectedENvt,
|
ConnectedENvt,
|
||||||
ConnectedESscp,
|
ConnectedESscp,
|
||||||
#[serde(rename="connected-e-tn3270e")]
|
#[serde(rename = "connected-e-tn3270e")]
|
||||||
ConnectedETn3270e,
|
ConnectedETn3270e,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
|
||||||
#[serde(rename_all="kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct Erase {
|
pub struct Erase {
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub logical_rows: Option<u8>,
|
pub logical_rows: Option<u8>,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub logical_cols: Option<u8>,
|
pub logical_cols: Option<u8>,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub fg: Option<Color>,
|
pub fg: Option<Color>,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub bg: Option<Color>,
|
pub bg: Option<Color>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,34 +101,36 @@ pub struct Model {
|
|||||||
pub struct Oia {
|
pub struct Oia {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub field: OiaField,
|
pub field: OiaField,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub lu: Option<String>,
|
pub lu: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all="kebab-case", tag="field")]
|
#[serde(rename_all = "kebab-case", tag = "field")]
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum OiaField {
|
pub enum OiaField {
|
||||||
/// Composite character in progress
|
/// Composite character in progress
|
||||||
Compose{
|
Compose {
|
||||||
value: bool,
|
value: bool,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
char: Option<String>,
|
char: Option<String>,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
type_: Option<ComposeType>,
|
type_: Option<ComposeType>,
|
||||||
},
|
},
|
||||||
/// Insert mode
|
/// Insert mode
|
||||||
Insert{value: bool},
|
Insert {
|
||||||
/// Keyboard is locked
|
value: bool,
|
||||||
Lock{
|
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
|
||||||
value: Option<String>
|
|
||||||
},
|
},
|
||||||
Lu{
|
/// Keyboard is locked
|
||||||
|
Lock {
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
value: Option<String>,
|
||||||
|
},
|
||||||
|
Lu {
|
||||||
/// Host session logical unit name
|
/// Host session logical unit name
|
||||||
value: String,
|
value: String,
|
||||||
/// Printer session LU name
|
/// Printer session LU name
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
lu: Option<String>,
|
lu: Option<String>,
|
||||||
},
|
},
|
||||||
/// Communication pending
|
/// Communication pending
|
||||||
@@ -139,7 +141,7 @@ pub enum OiaField {
|
|||||||
value: bool,
|
value: bool,
|
||||||
/// Printer session LU name
|
/// Printer session LU name
|
||||||
// TODO: determine if this is sent with this message or with Lu
|
// TODO: determine if this is sent with this message or with Lu
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
lu: Option<String>,
|
lu: Option<String>,
|
||||||
},
|
},
|
||||||
/// Reverse input mode
|
/// Reverse input mode
|
||||||
@@ -155,7 +157,7 @@ pub enum OiaField {
|
|||||||
value: String,
|
value: String,
|
||||||
},
|
},
|
||||||
Timing {
|
Timing {
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
value: Option<String>,
|
value: Option<String>,
|
||||||
},
|
},
|
||||||
Typeahead {
|
Typeahead {
|
||||||
@@ -181,17 +183,17 @@ pub enum OiaFieldName {
|
|||||||
impl OiaField {
|
impl OiaField {
|
||||||
pub fn field_name(&self) -> OiaFieldName {
|
pub fn field_name(&self) -> OiaFieldName {
|
||||||
match self {
|
match self {
|
||||||
OiaField::Compose {..} => OiaFieldName::Compose,
|
OiaField::Compose { .. } => OiaFieldName::Compose,
|
||||||
OiaField::Insert {..} => OiaFieldName::Insert,
|
OiaField::Insert { .. } => OiaFieldName::Insert,
|
||||||
OiaField::Lock {..} => OiaFieldName::Lock,
|
OiaField::Lock { .. } => OiaFieldName::Lock,
|
||||||
OiaField::Lu {..} => OiaFieldName::Lu,
|
OiaField::Lu { .. } => OiaFieldName::Lu,
|
||||||
OiaField::NotUndera {..} => OiaFieldName::NotUndera,
|
OiaField::NotUndera { .. } => OiaFieldName::NotUndera,
|
||||||
OiaField::PrinterSession {..} => OiaFieldName::PrinterSession,
|
OiaField::PrinterSession { .. } => OiaFieldName::PrinterSession,
|
||||||
OiaField::ReverseInput {..} => OiaFieldName::ReverseInput,
|
OiaField::ReverseInput { .. } => OiaFieldName::ReverseInput,
|
||||||
OiaField::ScreenTrace {..} => OiaFieldName::ScreenTrace,
|
OiaField::ScreenTrace { .. } => OiaFieldName::ScreenTrace,
|
||||||
OiaField::Script {..} => OiaFieldName::Script,
|
OiaField::Script { .. } => OiaFieldName::Script,
|
||||||
OiaField::Timing {..} => OiaFieldName::Timing,
|
OiaField::Timing { .. } => OiaFieldName::Timing,
|
||||||
OiaField::Typeahead {..} => OiaFieldName::Typeahead,
|
OiaField::Typeahead { .. } => OiaFieldName::Typeahead,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,24 +226,24 @@ pub struct ScreenMode {
|
|||||||
pub struct TlsHello {
|
pub struct TlsHello {
|
||||||
pub supported: bool,
|
pub supported: bool,
|
||||||
pub provider: String, // docs claim this is always set, but I'm not sure.
|
pub provider: String, // docs claim this is always set, but I'm not sure.
|
||||||
#[serde(default, skip_serializing_if="Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub options: Vec<String>,
|
pub options: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
#[serde(rename_all="kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct Tls {
|
pub struct Tls {
|
||||||
pub secure: bool,
|
pub secure: bool,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub verified: Option<bool>,
|
pub verified: Option<bool>,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub session: Option<String>,
|
pub session: Option<String>,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub host_cert: Option<String>,
|
pub host_cert: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
#[serde(rename_all="kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct ConnectAttempt {
|
pub struct ConnectAttempt {
|
||||||
pub host_ip: String,
|
pub host_ip: String,
|
||||||
pub port: String,
|
pub port: String,
|
||||||
@@ -251,9 +253,9 @@ pub struct ConnectAttempt {
|
|||||||
// TODO: change this to an enum
|
// TODO: change this to an enum
|
||||||
pub struct Cursor {
|
pub struct Cursor {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub row: Option<u8>,
|
pub row: Option<u8>,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub column: Option<u8>,
|
pub column: Option<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,15 +267,15 @@ pub struct FileTransfer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
#[serde(rename_all="lowercase", tag="state")]
|
#[serde(rename_all = "lowercase", tag = "state")]
|
||||||
pub enum FileTransferState {
|
pub enum FileTransferState {
|
||||||
Awaiting,
|
Awaiting,
|
||||||
Running{
|
Running {
|
||||||
/// Number of bytes transferred
|
/// Number of bytes transferred
|
||||||
bytes: usize
|
bytes: usize,
|
||||||
},
|
},
|
||||||
Aborting,
|
Aborting,
|
||||||
Complete{
|
Complete {
|
||||||
/// Completion message
|
/// Completion message
|
||||||
text: String,
|
text: String,
|
||||||
/// Transfer succeeded
|
/// Transfer succeeded
|
||||||
@@ -282,27 +284,27 @@ pub enum FileTransferState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
#[serde(rename="kebab-case")]
|
#[serde(rename = "kebab-case")]
|
||||||
pub struct Passthru {
|
pub struct Passthru {
|
||||||
pub p_tag: String,
|
pub p_tag: String,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub parent_r_tag: Option<String>,
|
pub parent_r_tag: Option<String>,
|
||||||
pub action: String,
|
pub action: String,
|
||||||
#[serde(default, skip_serializing_if="Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub args: Vec<String>,
|
pub args: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
pub struct Popup {
|
pub struct Popup {
|
||||||
#[serde(rename="type")]
|
#[serde(rename = "type")]
|
||||||
pub type_: PopupType,
|
pub type_: PopupType,
|
||||||
pub text: String,
|
pub text: String,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub error: Option<bool>,
|
pub error: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
|
||||||
#[serde(rename="kebab-case")]
|
#[serde(rename = "kebab-case")]
|
||||||
pub enum PopupType {
|
pub enum PopupType {
|
||||||
/// Error message from a connection attempt
|
/// Error message from a connection attempt
|
||||||
ConnectError,
|
ConnectError,
|
||||||
@@ -319,15 +321,14 @@ pub enum PopupType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
#[serde(rename="kebab-case")]
|
#[serde(rename = "kebab-case")]
|
||||||
pub struct Row {
|
pub struct Row {
|
||||||
pub row: u8,
|
pub row: u8,
|
||||||
pub changes: Vec<Change>,
|
pub changes: Vec<Change>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
#[serde(rename="kebab-case")]
|
#[serde(rename = "kebab-case")]
|
||||||
pub enum CountOrText {
|
pub enum CountOrText {
|
||||||
Count(usize),
|
Count(usize),
|
||||||
Text(String),
|
Text(String),
|
||||||
@@ -338,48 +339,48 @@ pub struct Change {
|
|||||||
pub column: u8,
|
pub column: u8,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub change: CountOrText,
|
pub change: CountOrText,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub fg: Option<Color>,
|
pub fg: Option<Color>,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub bg: Option<Color>,
|
pub bg: Option<Color>,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
/// Graphic rendition
|
/// Graphic rendition
|
||||||
pub gr: Option<GraphicRendition>,
|
pub gr: Option<GraphicRendition>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
pub struct Screen {
|
pub struct Screen {
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub cursor: Option<Cursor>,
|
pub cursor: Option<Cursor>,
|
||||||
#[serde(default, skip_serializing_if="Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub rows: Vec<Row>,
|
pub rows: Vec<Row>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
#[serde(rename="kebab-case")]
|
#[serde(rename = "kebab-case")]
|
||||||
pub struct RunResult {
|
pub struct RunResult {
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub r_tag: Option<String>,
|
pub r_tag: Option<String>,
|
||||||
pub success: bool,
|
pub success: bool,
|
||||||
#[serde(default, skip_serializing_if="Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub text: Vec<String>,
|
pub text: Vec<String>,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub abort: Option<bool>,
|
pub abort: Option<bool>,
|
||||||
/// Execution time in seconds
|
/// Execution time in seconds
|
||||||
pub time: f32,
|
pub time: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
#[serde(rename="kebab-case")]
|
#[serde(rename = "kebab-case")]
|
||||||
pub struct Scroll {
|
pub struct Scroll {
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub fg: Option<Color>,
|
pub fg: Option<Color>,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub bg: Option<Color>,
|
pub bg: Option<Color>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
#[serde(rename="kebab-case")]
|
#[serde(rename = "kebab-case")]
|
||||||
pub struct Stats {
|
pub struct Stats {
|
||||||
pub bytes_received: usize,
|
pub bytes_received: usize,
|
||||||
pub bytes_sent: usize,
|
pub bytes_sent: usize,
|
||||||
@@ -390,7 +391,7 @@ pub struct Stats {
|
|||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
pub struct TerminalName {
|
pub struct TerminalName {
|
||||||
pub text: String,
|
pub text: String,
|
||||||
#[serde(rename="override")]
|
#[serde(rename = "override")]
|
||||||
pub override_: bool,
|
pub override_: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,7 +411,7 @@ pub struct Thumb {
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
pub struct TraceFile {
|
pub struct TraceFile {
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,13 +419,13 @@ pub struct TraceFile {
|
|||||||
pub struct UiError {
|
pub struct UiError {
|
||||||
pub fatal: bool,
|
pub fatal: bool,
|
||||||
pub text: String,
|
pub text: String,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub operation: Option<String>,
|
pub operation: Option<String>,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub member: Option<String>,
|
pub member: Option<String>,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub line: Option<usize>,
|
pub line: Option<usize>,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub column: Option<usize>,
|
pub column: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,7 +434,13 @@ mod test {
|
|||||||
use super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
pub fn connection_state_serializes_as_expected() {
|
pub fn connection_state_serializes_as_expected() {
|
||||||
assert_eq!(serde_json::to_string(&ConnectionState::ConnectedETn3270e).unwrap(),r#""connected-e-tn3270e""#);
|
assert_eq!(
|
||||||
assert_eq!(serde_json::to_string(&ConnectionState::ConnectedESscp).unwrap(),r#""connected-e-sscp""#);
|
serde_json::to_string(&ConnectionState::ConnectedETn3270e).unwrap(),
|
||||||
|
r#""connected-e-tn3270e""#
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
serde_json::to_string(&ConnectionState::ConnectedESscp).unwrap(),
|
||||||
|
r#""connected-e-sscp""#
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,45 +4,45 @@ use serde::{Deserialize, Serialize};
|
|||||||
// {"run":{"actions":"Key(a)"}}
|
// {"run":{"actions":"Key(a)"}}
|
||||||
// Operations
|
// Operations
|
||||||
#[derive(Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Clone)]
|
||||||
#[serde(rename_all="kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct Run {
|
pub struct Run {
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub r_tag: Option<String>,
|
pub r_tag: Option<String>,
|
||||||
#[serde(rename="type", default, skip_serializing_if="Option::is_none")]
|
#[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
|
||||||
pub type_: Option<String>,
|
pub type_: Option<String>,
|
||||||
pub actions: Vec<Action>,
|
pub actions: Vec<Action>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Clone)]
|
||||||
#[serde(rename_all="kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct Action {
|
pub struct Action {
|
||||||
pub action: String,
|
pub action: String,
|
||||||
#[serde(default, skip_serializing_if="Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub args: Vec<String>,
|
pub args: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Clone)]
|
||||||
#[serde(rename_all="kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct Register {
|
pub struct Register {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub help_text: Option<String>,
|
pub help_text: Option<String>,
|
||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub help_params: Option<String>,
|
pub help_params: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Clone)]
|
||||||
/// Completes a passthru action unsuccessfully
|
/// Completes a passthru action unsuccessfully
|
||||||
#[serde(rename_all="kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct Fail {
|
pub struct Fail {
|
||||||
pub p_tag: String,
|
pub p_tag: String,
|
||||||
pub text: Vec<String>,
|
pub text: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Clone)]
|
||||||
#[serde(rename_all="kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct Succeed {
|
pub struct Succeed {
|
||||||
pub p_tag: String,
|
pub p_tag: String,
|
||||||
#[serde(default, skip_serializing_if="Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub text: Vec<String>,
|
pub text: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
use bitflags::bitflags;
|
||||||
|
use serde::de::{Error, Visitor};
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use std::fmt::{Display, Formatter, Write};
|
use std::fmt::{Display, Formatter, Write};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use bitflags::bitflags;
|
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
|
||||||
use serde::de::{Error, Visitor};
|
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
#[derive(Clone,Copy,Debug,PartialEq, Eq, Hash)]
|
#[derive(Clone,Copy,Debug,PartialEq, Eq, Hash)]
|
||||||
@@ -36,7 +36,8 @@ static FLAG_NAMES: &'static [(GraphicRendition, &'static str)] = &[
|
|||||||
|
|
||||||
impl Display for GraphicRendition {
|
impl Display for GraphicRendition {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
let flag_names = FLAG_NAMES.iter()
|
let flag_names = FLAG_NAMES
|
||||||
|
.iter()
|
||||||
.filter_map(|(val, name)| self.contains(*val).then_some(*name));
|
.filter_map(|(val, name)| self.contains(*val).then_some(*name));
|
||||||
for (n, name) in flag_names.enumerate() {
|
for (n, name) in flag_names.enumerate() {
|
||||||
if n != 0 {
|
if n != 0 {
|
||||||
@@ -49,7 +50,10 @@ impl Display for GraphicRendition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for GraphicRendition {
|
impl Serialize for GraphicRendition {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
if serializer.is_human_readable() {
|
if serializer.is_human_readable() {
|
||||||
serializer.serialize_str(&self.to_string())
|
serializer.serialize_str(&self.to_string())
|
||||||
} else {
|
} else {
|
||||||
@@ -60,7 +64,6 @@ impl Serialize for GraphicRendition {
|
|||||||
|
|
||||||
struct GrVisitor;
|
struct GrVisitor;
|
||||||
|
|
||||||
|
|
||||||
impl Visitor<'_> for GrVisitor {
|
impl Visitor<'_> for GrVisitor {
|
||||||
type Value = GraphicRendition;
|
type Value = GraphicRendition;
|
||||||
|
|
||||||
@@ -68,15 +71,24 @@ impl Visitor<'_> for GrVisitor {
|
|||||||
write!(formatter, "graphic rendition string or binary value")
|
write!(formatter, "graphic rendition string or binary value")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E> where E: Error {
|
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: Error,
|
||||||
|
{
|
||||||
self.visit_u64((v & 0xFFFF) as u64)
|
self.visit_u64((v & 0xFFFF) as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> where E: Error {
|
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: Error,
|
||||||
|
{
|
||||||
Ok(GraphicRendition::from_bits_truncate((v & 0xFFFF) as u16))
|
Ok(GraphicRendition::from_bits_truncate((v & 0xFFFF) as u16))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where E: Error {
|
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: Error,
|
||||||
|
{
|
||||||
GraphicRendition::from_str(v).map_err(E::custom)
|
GraphicRendition::from_str(v).map_err(E::custom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,7 +99,8 @@ impl FromStr for GraphicRendition {
|
|||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
s.split(",")
|
s.split(",")
|
||||||
.map(|attr| {
|
.map(|attr| {
|
||||||
FLAG_NAMES.iter()
|
FLAG_NAMES
|
||||||
|
.iter()
|
||||||
.find(|(_, name)| *name == attr)
|
.find(|(_, name)| *name == attr)
|
||||||
.map(|x| x.0)
|
.map(|x| x.0)
|
||||||
.ok_or_else(|| format!("Invalid attr name {attr}"))
|
.ok_or_else(|| format!("Invalid attr name {attr}"))
|
||||||
@@ -97,7 +110,10 @@ impl FromStr for GraphicRendition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for GraphicRendition {
|
impl<'de> Deserialize<'de> for GraphicRendition {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
if deserializer.is_human_readable() {
|
if deserializer.is_human_readable() {
|
||||||
deserializer.deserialize_str(GrVisitor)
|
deserializer.deserialize_str(GrVisitor)
|
||||||
} else {
|
} else {
|
||||||
@@ -108,16 +124,19 @@ impl<'de> Deserialize<'de> for GraphicRendition {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::str::FromStr;
|
|
||||||
use super::GraphicRendition;
|
use super::GraphicRendition;
|
||||||
|
use std::str::FromStr;
|
||||||
#[test]
|
#[test]
|
||||||
fn from_str_1() {
|
fn from_str_1() {
|
||||||
assert_eq!(GraphicRendition::from_str("underline,blink"), Ok(GraphicRendition::BLINK | GraphicRendition::UNDERLINE))
|
assert_eq!(
|
||||||
|
GraphicRendition::from_str("underline,blink"),
|
||||||
|
Ok(GraphicRendition::BLINK | GraphicRendition::UNDERLINE)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
|
||||||
#[serde(rename="camelCase")]
|
#[serde(rename = "camelCase")]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Color {
|
pub enum Color {
|
||||||
NeutralBlack,
|
NeutralBlack,
|
||||||
@@ -142,22 +161,22 @@ impl From<Color> for u8 {
|
|||||||
fn from(value: Color) -> Self {
|
fn from(value: Color) -> Self {
|
||||||
use Color::*;
|
use Color::*;
|
||||||
match value {
|
match value {
|
||||||
NeutralBlack => 0,
|
NeutralBlack => 0,
|
||||||
Blue => 1,
|
Blue => 1,
|
||||||
Red => 2,
|
Red => 2,
|
||||||
Pink => 3,
|
Pink => 3,
|
||||||
Green => 4,
|
Green => 4,
|
||||||
Turquoise => 5,
|
Turquoise => 5,
|
||||||
Yellow => 6,
|
Yellow => 6,
|
||||||
NeutralWhite => 7,
|
NeutralWhite => 7,
|
||||||
Black => 8,
|
Black => 8,
|
||||||
DeepBlue => 9,
|
DeepBlue => 9,
|
||||||
Orange => 10,
|
Orange => 10,
|
||||||
Purple => 11,
|
Purple => 11,
|
||||||
PaleGreen => 12,
|
PaleGreen => 12,
|
||||||
PaleTurquoise => 13,
|
PaleTurquoise => 13,
|
||||||
Gray => 14,
|
Gray => 14,
|
||||||
White => 15,
|
White => 15,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,16 +185,16 @@ impl From<u8> for Color {
|
|||||||
fn from(value: u8) -> Self {
|
fn from(value: u8) -> Self {
|
||||||
use Color::*;
|
use Color::*;
|
||||||
match value & 0xF {
|
match value & 0xF {
|
||||||
0 => NeutralBlack,
|
0 => NeutralBlack,
|
||||||
1 => Blue,
|
1 => Blue,
|
||||||
2 => Red,
|
2 => Red,
|
||||||
3 => Pink,
|
3 => Pink,
|
||||||
4 => Green,
|
4 => Green,
|
||||||
5 => Turquoise,
|
5 => Turquoise,
|
||||||
6 => Yellow,
|
6 => Yellow,
|
||||||
7 => NeutralWhite,
|
7 => NeutralWhite,
|
||||||
8 => Black,
|
8 => Black,
|
||||||
9 => DeepBlue,
|
9 => DeepBlue,
|
||||||
10 => Orange,
|
10 => Orange,
|
||||||
11 => Purple,
|
11 => Purple,
|
||||||
12 => PaleGreen,
|
12 => PaleGreen,
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
pub mod b3270;
|
pub mod b3270;
|
||||||
pub mod tracker;
|
pub mod tracker;
|
||||||
|
|
||||||
pub mod executor;
|
pub mod executor;
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
use std::collections::HashMap;
|
use crate::b3270::indication::{
|
||||||
use crate::b3270::indication::{Change, Connection, ConnectionState, CountOrText, Cursor, Erase, Oia, OiaFieldName, Row, RunResult, Screen, ScreenMode, Scroll, Setting, TerminalName, Thumb, Tls, TraceFile};
|
Change, Connection, ConnectionState, CountOrText, Cursor, Erase, Oia, OiaFieldName, Row,
|
||||||
use crate::b3270::{Indication, InitializeIndication};
|
RunResult, Screen, ScreenMode, Scroll, Setting, TerminalName, Thumb, Tls, TraceFile,
|
||||||
|
};
|
||||||
use crate::b3270::types::{Color, GraphicRendition, PackedAttr};
|
use crate::b3270::types::{Color, GraphicRendition, PackedAttr};
|
||||||
|
use crate::b3270::{Indication, InitializeIndication};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
struct CharCell {
|
struct CharCell {
|
||||||
@@ -48,9 +51,7 @@ impl Tracker {
|
|||||||
| Indication::Icon { .. }
|
| Indication::Icon { .. }
|
||||||
| Indication::Popup(_)
|
| Indication::Popup(_)
|
||||||
| Indication::Stats(_)
|
| Indication::Stats(_)
|
||||||
| Indication::WindowTitle { .. }
|
| Indication::WindowTitle { .. } => (),
|
||||||
|
|
||||||
=> (),
|
|
||||||
Indication::Connection(conn) => {
|
Indication::Connection(conn) => {
|
||||||
self.connection = conn.clone();
|
self.connection = conn.clone();
|
||||||
}
|
}
|
||||||
@@ -64,43 +65,47 @@ impl Tracker {
|
|||||||
let cols = self.erase.logical_cols.unwrap_or(self.screen_mode.cols) as usize;
|
let cols = self.erase.logical_cols.unwrap_or(self.screen_mode.cols) as usize;
|
||||||
|
|
||||||
self.screen = vec![
|
self.screen = vec![
|
||||||
vec![CharCell{
|
vec![
|
||||||
attr: u32::c_pack(
|
CharCell {
|
||||||
erase.fg.unwrap_or(Color::NeutralBlack),
|
attr: u32::c_pack(
|
||||||
erase.bg.unwrap_or(Color::Blue),
|
erase.fg.unwrap_or(Color::NeutralBlack),
|
||||||
GraphicRendition::empty(),
|
erase.bg.unwrap_or(Color::Blue),
|
||||||
),
|
GraphicRendition::empty(),
|
||||||
ch: ' ',
|
),
|
||||||
};cols]
|
ch: ' ',
|
||||||
; rows
|
};
|
||||||
|
cols
|
||||||
|
];
|
||||||
|
rows
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Indication::Formatted { state } => {self.formatted = *state; }
|
Indication::Formatted { state } => {
|
||||||
|
self.formatted = *state;
|
||||||
|
}
|
||||||
|
|
||||||
Indication::Initialize(init) => {
|
Indication::Initialize(init) => {
|
||||||
let mut static_init = Vec::with_capacity(init.len());
|
let mut static_init = Vec::with_capacity(init.len());
|
||||||
for indicator in init.clone() {
|
for indicator in init.clone() {
|
||||||
match indicator {
|
match indicator {
|
||||||
InitializeIndication::CodePages(_) |
|
InitializeIndication::CodePages(_)
|
||||||
InitializeIndication::Hello(_) |
|
| InitializeIndication::Hello(_)
|
||||||
InitializeIndication::Models(_) |
|
| InitializeIndication::Models(_)
|
||||||
InitializeIndication::Prefixes { .. } |
|
| InitializeIndication::Prefixes { .. }
|
||||||
InitializeIndication::Proxies(_) |
|
| InitializeIndication::Proxies(_)
|
||||||
InitializeIndication::TlsHello(_) |
|
| InitializeIndication::TlsHello(_)
|
||||||
InitializeIndication::Tls(_) |
|
| InitializeIndication::Tls(_)
|
||||||
InitializeIndication::TraceFile(_) =>
|
| InitializeIndication::TraceFile(_) => static_init.push(indicator),
|
||||||
static_init.push(indicator),
|
|
||||||
|
|
||||||
// The rest are passed through to normal processing.
|
// The rest are passed through to normal processing.
|
||||||
InitializeIndication::Thumb(thumb) => {
|
InitializeIndication::Thumb(thumb) => {
|
||||||
self.handle_indication(&mut Indication::Thumb(thumb));
|
self.handle_indication(&mut Indication::Thumb(thumb));
|
||||||
},
|
}
|
||||||
InitializeIndication::Setting(setting) => {
|
InitializeIndication::Setting(setting) => {
|
||||||
self.handle_indication(&mut Indication::Setting(setting));
|
self.handle_indication(&mut Indication::Setting(setting));
|
||||||
}
|
}
|
||||||
InitializeIndication::ScreenMode(mode) => {
|
InitializeIndication::ScreenMode(mode) => {
|
||||||
self.handle_indication(&mut Indication::ScreenMode(mode));
|
self.handle_indication(&mut Indication::ScreenMode(mode));
|
||||||
},
|
}
|
||||||
InitializeIndication::Oia(oia) => {
|
InitializeIndication::Oia(oia) => {
|
||||||
self.handle_indication(&mut Indication::Oia(oia));
|
self.handle_indication(&mut Indication::Oia(oia));
|
||||||
}
|
}
|
||||||
@@ -127,21 +132,19 @@ impl Tracker {
|
|||||||
// update screen contents
|
// update screen contents
|
||||||
let cols = self.screen[row_idx].iter_mut().skip(col_idx);
|
let cols = self.screen[row_idx].iter_mut().skip(col_idx);
|
||||||
match change.change {
|
match change.change {
|
||||||
CountOrText::Count(n) => {
|
CountOrText::Count(n) => cols.take(n).for_each(|cell| {
|
||||||
cols.take(n).for_each(|cell| {
|
let mut attr = cell.attr;
|
||||||
let mut attr = cell.attr;
|
if let Some(fg) = change.fg {
|
||||||
if let Some(fg) = change.fg {
|
attr = attr.c_setfg(fg);
|
||||||
attr = attr.c_setfg(fg);
|
}
|
||||||
}
|
if let Some(bg) = change.bg {
|
||||||
if let Some(bg) = change.bg {
|
attr = attr.c_setbg(bg);
|
||||||
attr = attr.c_setbg(bg);
|
}
|
||||||
}
|
if let Some(gr) = change.gr {
|
||||||
if let Some(gr) = change.gr {
|
attr = attr.c_setgr(gr);
|
||||||
attr = attr.c_setgr(gr);
|
}
|
||||||
}
|
cell.attr = attr;
|
||||||
cell.attr = attr;
|
}),
|
||||||
})
|
|
||||||
}
|
|
||||||
CountOrText::Text(ref text) => {
|
CountOrText::Text(ref text) => {
|
||||||
cols.zip(text.chars()).for_each(|(cell, ch)| {
|
cols.zip(text.chars()).for_each(|(cell, ch)| {
|
||||||
let mut attr = cell.attr;
|
let mut attr = cell.attr;
|
||||||
@@ -164,18 +167,18 @@ impl Tracker {
|
|||||||
}
|
}
|
||||||
Indication::ScreenMode(mode) => {
|
Indication::ScreenMode(mode) => {
|
||||||
self.screen_mode = *mode;
|
self.screen_mode = *mode;
|
||||||
self.handle_indication(&mut Indication::Erase(Erase{
|
self.handle_indication(&mut Indication::Erase(Erase {
|
||||||
logical_rows: Some(self.screen_mode.rows),
|
logical_rows: Some(self.screen_mode.rows),
|
||||||
logical_cols: Some(self.screen_mode.cols),
|
logical_cols: Some(self.screen_mode.cols),
|
||||||
fg: None,
|
fg: None,
|
||||||
bg: None,
|
bg: None,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
Indication::Scroll(Scroll{ fg, bg }) => {
|
Indication::Scroll(Scroll { fg, bg }) => {
|
||||||
let fg = fg.or(self.erase.fg).unwrap_or(Color::Blue);
|
let fg = fg.or(self.erase.fg).unwrap_or(Color::Blue);
|
||||||
let bg = bg.or(self.erase.bg).unwrap_or(Color::NeutralBlack);
|
let bg = bg.or(self.erase.bg).unwrap_or(Color::NeutralBlack);
|
||||||
let mut row = self.screen.remove(0);
|
let mut row = self.screen.remove(0);
|
||||||
row.fill(CharCell{
|
row.fill(CharCell {
|
||||||
attr: u32::c_pack(fg, bg, GraphicRendition::empty()),
|
attr: u32::c_pack(fg, bg, GraphicRendition::empty()),
|
||||||
ch: ' ',
|
ch: ' ',
|
||||||
});
|
});
|
||||||
@@ -190,7 +193,7 @@ impl Tracker {
|
|||||||
Indication::Thumb(thumb) => {
|
Indication::Thumb(thumb) => {
|
||||||
self.thumb = thumb.clone();
|
self.thumb = thumb.clone();
|
||||||
}
|
}
|
||||||
Indication::TraceFile(TraceFile{name}) => {
|
Indication::TraceFile(TraceFile { name }) => {
|
||||||
self.trace_file = name.clone();
|
self.trace_file = name.clone();
|
||||||
}
|
}
|
||||||
Indication::Tls(tls) => {
|
Indication::Tls(tls) => {
|
||||||
@@ -201,16 +204,15 @@ impl Tracker {
|
|||||||
Indication::UiError(_) => {} // we can assume that this came from the last sent command
|
Indication::UiError(_) => {} // we can assume that this came from the last sent command
|
||||||
Indication::Passthru(_) => {} // dunno how to handle this one
|
Indication::Passthru(_) => {} // dunno how to handle this one
|
||||||
Indication::FileTransfer(_) => {}
|
Indication::FileTransfer(_) => {}
|
||||||
Indication::RunResult(RunResult{r_tag, ..}) => {
|
Indication::RunResult(RunResult { r_tag, .. }) => {
|
||||||
if let Some(dest) = r_tag {
|
if let Some(dest) = r_tag {
|
||||||
return Disposition::Direct(dest.clone());
|
return Disposition::Direct(dest.clone());
|
||||||
} else {
|
} else {
|
||||||
return Disposition::Drop;
|
return Disposition::Drop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return Disposition::Broadcast
|
return Disposition::Broadcast;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_init_indication(&self) -> Vec<Indication> {
|
pub fn get_init_indication(&self) -> Vec<Indication> {
|
||||||
@@ -219,12 +221,13 @@ impl Tracker {
|
|||||||
contents.push(InitializeIndication::Erase(self.erase));
|
contents.push(InitializeIndication::Erase(self.erase));
|
||||||
contents.push(InitializeIndication::Thumb(self.thumb));
|
contents.push(InitializeIndication::Thumb(self.thumb));
|
||||||
|
|
||||||
contents.extend(self.oia.values()
|
contents.extend(self.oia.values().cloned().map(InitializeIndication::Oia));
|
||||||
.cloned()
|
contents.extend(
|
||||||
.map(InitializeIndication::Oia));
|
self.settings
|
||||||
contents.extend(self.settings.values()
|
.values()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(InitializeIndication::Setting));
|
.map(InitializeIndication::Setting),
|
||||||
|
);
|
||||||
contents.extend(self.tls.clone().map(InitializeIndication::Tls));
|
contents.extend(self.tls.clone().map(InitializeIndication::Tls));
|
||||||
|
|
||||||
// Construct a screen snapshot
|
// Construct a screen snapshot
|
||||||
@@ -232,7 +235,9 @@ impl Tracker {
|
|||||||
Indication::Initialize(contents),
|
Indication::Initialize(contents),
|
||||||
Indication::Connection(self.connection.clone()),
|
Indication::Connection(self.connection.clone()),
|
||||||
Indication::Screen(self.screen_snapshot()),
|
Indication::Screen(self.screen_snapshot()),
|
||||||
Indication::Formatted {state: self.formatted},
|
Indication::Formatted {
|
||||||
|
state: self.formatted,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
if let Some(terminal_name) = self.terminal_name.clone() {
|
if let Some(terminal_name) = self.terminal_name.clone() {
|
||||||
result.push(Indication::TerminalName(terminal_name));
|
result.push(Indication::TerminalName(terminal_name));
|
||||||
@@ -255,7 +260,7 @@ impl Tracker {
|
|||||||
let (first, rest) = row.split_at(split_pt);
|
let (first, rest) = row.split_at(split_pt);
|
||||||
row = rest;
|
row = rest;
|
||||||
let content = first.iter().map(|cell| cell.ch).collect();
|
let content = first.iter().map(|cell| cell.ch).collect();
|
||||||
result.push(Change{
|
result.push(Change {
|
||||||
column,
|
column,
|
||||||
fg: Some(cur_gr.c_fg()),
|
fg: Some(cur_gr.c_fg()),
|
||||||
bg: Some(cur_gr.c_bg()),
|
bg: Some(cur_gr.c_bg()),
|
||||||
@@ -269,16 +274,19 @@ impl Tracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn screen_snapshot(&self) -> Screen {
|
fn screen_snapshot(&self) -> Screen {
|
||||||
Screen{
|
Screen {
|
||||||
cursor: Some(self.cursor),
|
cursor: Some(self.cursor),
|
||||||
rows: self.screen.iter()
|
rows: self
|
||||||
.map(Vec::as_slice).map(Self::format_row)
|
.screen
|
||||||
|
.iter()
|
||||||
|
.map(Vec::as_slice)
|
||||||
|
.map(Self::format_row)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(row_id, changes)| Row{
|
.map(|(row_id, changes)| Row {
|
||||||
row: row_id as u8 - 1,
|
row: row_id as u8 - 1,
|
||||||
changes,
|
changes,
|
||||||
})
|
})
|
||||||
.collect()
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -313,7 +321,7 @@ impl Default for Tracker {
|
|||||||
cursor: Cursor {
|
cursor: Cursor {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
row: None,
|
row: None,
|
||||||
column: None
|
column: None,
|
||||||
},
|
},
|
||||||
connection: Connection {
|
connection: Connection {
|
||||||
state: ConnectionState::NotConnected,
|
state: ConnectionState::NotConnected,
|
||||||
@@ -328,4 +336,4 @@ impl Default for Tracker {
|
|||||||
};
|
};
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
|
use anyhow::anyhow;
|
||||||
|
use base64::engine::general_purpose::STANDARD as B64_STANDARD;
|
||||||
|
use base64::Engine;
|
||||||
|
use bytes::Buf;
|
||||||
|
use d3270_common::b3270::indication::RunResult;
|
||||||
|
use d3270_common::b3270::operation::Action;
|
||||||
|
use d3270_common::b3270::{operation, Indication, Operation};
|
||||||
|
use d3270_common::tracker::{Disposition, Tracker};
|
||||||
|
use futures::future::BoxFuture;
|
||||||
|
use futures::{FutureExt, Stream, StreamExt, TryFutureExt};
|
||||||
|
use rand::RngCore;
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
use anyhow::anyhow;
|
use tokio::io::{AsyncBufReadExt, AsyncWrite, BufReader, Lines};
|
||||||
use base64::Engine;
|
|
||||||
use base64::engine::general_purpose::STANDARD as B64_STANDARD;
|
|
||||||
use bytes::Buf;
|
|
||||||
use futures::{FutureExt, Stream, StreamExt, TryFutureExt};
|
|
||||||
use futures::future::BoxFuture;
|
|
||||||
use rand::RngCore;
|
|
||||||
use tokio::io::{BufReader, AsyncBufReadExt, Lines, AsyncWrite};
|
|
||||||
use tokio::process::{Child, ChildStdout};
|
use tokio::process::{Child, ChildStdout};
|
||||||
use tokio::sync::{mpsc, oneshot, broadcast};
|
use tokio::sync::{broadcast, mpsc, oneshot};
|
||||||
use tokio_stream::wrappers::{BroadcastStream, errors::BroadcastStreamRecvError};
|
use tokio_stream::wrappers::{errors::BroadcastStreamRecvError, BroadcastStream};
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
use d3270_common::b3270::{Indication, Operation, operation};
|
|
||||||
use d3270_common::b3270::indication::RunResult;
|
|
||||||
use d3270_common::b3270::operation::Action;
|
|
||||||
use d3270_common::tracker::{Disposition, Tracker};
|
|
||||||
|
|
||||||
pub struct B3270 {
|
pub struct B3270 {
|
||||||
tracker: Tracker, //
|
tracker: Tracker, //
|
||||||
child: Child, //
|
child: Child, //
|
||||||
comm: mpsc::Receiver<B3270Request>, //
|
comm: mpsc::Receiver<B3270Request>, //
|
||||||
ind_chan: broadcast::Sender<Indication>, //
|
ind_chan: broadcast::Sender<Indication>, //
|
||||||
child_reader: Lines<BufReader<ChildStdout>>, //
|
child_reader: Lines<BufReader<ChildStdout>>, //
|
||||||
|
|
||||||
write_buf: VecDeque<u8>,
|
write_buf: VecDeque<u8>,
|
||||||
@@ -38,8 +38,14 @@ pub enum B3270Request {
|
|||||||
enum HandleReceiveState {
|
enum HandleReceiveState {
|
||||||
Steady(BroadcastStream<Indication>),
|
Steady(BroadcastStream<Indication>),
|
||||||
Wait(oneshot::Receiver<(Vec<Indication>, broadcast::Receiver<Indication>)>),
|
Wait(oneshot::Receiver<(Vec<Indication>, broadcast::Receiver<Indication>)>),
|
||||||
Resume(std::vec::IntoIter<Indication>, broadcast::Receiver<Indication>),
|
Resume(
|
||||||
TryRestart(BoxFuture<'static, Result<(), ()>>, oneshot::Receiver<(Vec<Indication>, broadcast::Receiver<Indication>)>),
|
std::vec::IntoIter<Indication>,
|
||||||
|
broadcast::Receiver<Indication>,
|
||||||
|
),
|
||||||
|
TryRestart(
|
||||||
|
BoxFuture<'static, Result<(), ()>>,
|
||||||
|
oneshot::Receiver<(Vec<Indication>, broadcast::Receiver<Indication>)>,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
pub struct Handle {
|
pub struct Handle {
|
||||||
sender: mpsc::Sender<B3270Request>,
|
sender: mpsc::Sender<B3270Request>,
|
||||||
@@ -66,11 +72,12 @@ impl Stream for Handle {
|
|||||||
match rcvr.poll_unpin(cx) {
|
match rcvr.poll_unpin(cx) {
|
||||||
Poll::Ready(Ok((inds, rcvr))) => {
|
Poll::Ready(Ok((inds, rcvr))) => {
|
||||||
// reverse the indicators so that they can be popped.
|
// reverse the indicators so that they can be popped.
|
||||||
self.receiver = Some(HandleReceiveState::Resume(inds.into_iter(), rcvr));
|
self.receiver =
|
||||||
|
Some(HandleReceiveState::Resume(inds.into_iter(), rcvr));
|
||||||
}
|
}
|
||||||
Poll::Ready(Err(error)) => {
|
Poll::Ready(Err(error)) => {
|
||||||
warn!(%error, "unable to reconnect to b3270 server");
|
warn!(%error, "unable to reconnect to b3270 server");
|
||||||
return Poll::Ready(None)
|
return Poll::Ready(None);
|
||||||
}
|
}
|
||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
self.receiver = Some(HandleReceiveState::Wait(rcvr));
|
self.receiver = Some(HandleReceiveState::Wait(rcvr));
|
||||||
@@ -78,41 +85,41 @@ impl Stream for Handle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(HandleReceiveState::Resume(mut inds, rcvr)) => {
|
Some(HandleReceiveState::Resume(mut inds, rcvr)) => match inds.next() {
|
||||||
match inds.next() {
|
Some(next) => {
|
||||||
Some(next) => {
|
self.receiver = Some(HandleReceiveState::Resume(inds, rcvr));
|
||||||
self.receiver = Some(HandleReceiveState::Resume(inds, rcvr));
|
return Poll::Ready(Some(next));
|
||||||
return Poll::Ready(Some(next));
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
self.receiver = Some(HandleReceiveState::Steady(BroadcastStream::new(rcvr)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
None => {
|
||||||
Some(HandleReceiveState::Steady(mut rcvr)) => {
|
self.receiver =
|
||||||
match rcvr.poll_next_unpin(cx) {
|
Some(HandleReceiveState::Steady(BroadcastStream::new(rcvr)));
|
||||||
Poll::Ready(Some(Ok(msg))) => {
|
|
||||||
self.receiver = Some(HandleReceiveState::Steady(rcvr));
|
|
||||||
return Poll::Ready(Some(msg))
|
|
||||||
}
|
|
||||||
Poll::Ready(Some(Err(BroadcastStreamRecvError::Lagged(_)))) => {
|
|
||||||
warn!("Dropped messages from b3270 server; starting resync");
|
|
||||||
let (os_snd, os_rcv) = oneshot::channel();
|
|
||||||
let fut = self.sender.clone().reserve_owned()
|
|
||||||
.map_ok(move |permit| {
|
|
||||||
permit.send(B3270Request::Resync(os_snd));
|
|
||||||
})
|
|
||||||
.map_err(|_| ())
|
|
||||||
.boxed();
|
|
||||||
self.receiver = Some(HandleReceiveState::TryRestart(fut, os_rcv));
|
|
||||||
}
|
|
||||||
Poll::Ready(None) => {
|
|
||||||
warn!("Failed to receive from b3270 server");
|
|
||||||
return Poll::Ready(None)
|
|
||||||
},
|
|
||||||
Poll::Pending => return Poll::Pending
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
Some(HandleReceiveState::Steady(mut rcvr)) => match rcvr.poll_next_unpin(cx) {
|
||||||
|
Poll::Ready(Some(Ok(msg))) => {
|
||||||
|
self.receiver = Some(HandleReceiveState::Steady(rcvr));
|
||||||
|
return Poll::Ready(Some(msg));
|
||||||
|
}
|
||||||
|
Poll::Ready(Some(Err(BroadcastStreamRecvError::Lagged(_)))) => {
|
||||||
|
warn!("Dropped messages from b3270 server; starting resync");
|
||||||
|
let (os_snd, os_rcv) = oneshot::channel();
|
||||||
|
let fut = self
|
||||||
|
.sender
|
||||||
|
.clone()
|
||||||
|
.reserve_owned()
|
||||||
|
.map_ok(move |permit| {
|
||||||
|
permit.send(B3270Request::Resync(os_snd));
|
||||||
|
})
|
||||||
|
.map_err(|_| ())
|
||||||
|
.boxed();
|
||||||
|
self.receiver = Some(HandleReceiveState::TryRestart(fut, os_rcv));
|
||||||
|
}
|
||||||
|
Poll::Ready(None) => {
|
||||||
|
warn!("Failed to receive from b3270 server");
|
||||||
|
return Poll::Ready(None);
|
||||||
|
}
|
||||||
|
Poll::Pending => return Poll::Pending,
|
||||||
|
},
|
||||||
|
|
||||||
None => {
|
None => {
|
||||||
return Poll::Ready(None);
|
return Poll::Ready(None);
|
||||||
@@ -122,11 +129,18 @@ impl Stream for Handle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl B3270 {
|
impl B3270 {
|
||||||
pub fn spawn(mut child: Child) -> (tokio::task::JoinHandle<anyhow::Error>, mpsc::Sender<B3270Request>) {
|
pub fn spawn(
|
||||||
|
mut child: Child,
|
||||||
|
) -> (
|
||||||
|
tokio::task::JoinHandle<anyhow::Error>,
|
||||||
|
mpsc::Sender<B3270Request>,
|
||||||
|
) {
|
||||||
let (subproc_snd, subproc_rcv) = mpsc::channel(10);
|
let (subproc_snd, subproc_rcv) = mpsc::channel(10);
|
||||||
let child_reader = child.stdout.take().expect("Should always be given a child that has stdout captured");
|
let child_reader = child
|
||||||
|
.stdout
|
||||||
|
.take()
|
||||||
|
.expect("Should always be given a child that has stdout captured");
|
||||||
let child_reader = BufReader::new(child_reader).lines();
|
let child_reader = BufReader::new(child_reader).lines();
|
||||||
// A single connect can result in a flurry of messages, so we need a big buffer
|
// A single connect can result in a flurry of messages, so we need a big buffer
|
||||||
let (ind_chan, _) = broadcast::channel(100);
|
let (ind_chan, _) = broadcast::channel(100);
|
||||||
@@ -153,9 +167,7 @@ impl Future for B3270 {
|
|||||||
while let Poll::Ready(buf) = Pin::new(&mut self.child_reader).poll_next_line(cx) {
|
while let Poll::Ready(buf) = Pin::new(&mut self.child_reader).poll_next_line(cx) {
|
||||||
match buf {
|
match buf {
|
||||||
Ok(Some(line)) => match serde_json::from_str(&line) {
|
Ok(Some(line)) => match serde_json::from_str(&line) {
|
||||||
Ok(ind) => {
|
Ok(ind) => indications.push(ind),
|
||||||
indications.push(ind)
|
|
||||||
},
|
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
warn!(%error, msg=line, "Failed to parse indication");
|
warn!(%error, msg=line, "Failed to parse indication");
|
||||||
}
|
}
|
||||||
@@ -204,13 +216,15 @@ impl Future for B3270 {
|
|||||||
let mut sync_state = None;
|
let mut sync_state = None;
|
||||||
while let Poll::Ready(cmd) = self.comm.poll_recv(cx) {
|
while let Poll::Ready(cmd) = self.comm.poll_recv(cx) {
|
||||||
match cmd {
|
match cmd {
|
||||||
None => {},
|
None => {}
|
||||||
Some(B3270Request::Resync(sender)) => {
|
Some(B3270Request::Resync(sender)) => {
|
||||||
if sync_state.is_none() {
|
if sync_state.is_none() {
|
||||||
sync_state = Some(self.tracker.get_init_indication());
|
sync_state = Some(self.tracker.get_init_indication());
|
||||||
}
|
}
|
||||||
// it's OK for this to fail; we just don't get a new client
|
// it's OK for this to fail; we just don't get a new client
|
||||||
sender.send((sync_state.clone().unwrap(), self.ind_chan.subscribe())).ok();
|
sender
|
||||||
|
.send((sync_state.clone().unwrap(), self.ind_chan.subscribe()))
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
Some(B3270Request::Action(actions, response_chan)) => {
|
Some(B3270Request::Action(actions, response_chan)) => {
|
||||||
let tag = 'find_tag: loop {
|
let tag = 'find_tag: loop {
|
||||||
@@ -225,15 +239,12 @@ impl Future for B3270 {
|
|||||||
type_: Some("keymap".to_owned()),
|
type_: Some("keymap".to_owned()),
|
||||||
actions,
|
actions,
|
||||||
});
|
});
|
||||||
let result = serde_json::to_writer(
|
let result = serde_json::to_writer(&mut self.write_buf, &op);
|
||||||
&mut self.write_buf,
|
|
||||||
&op
|
|
||||||
);
|
|
||||||
match result {
|
match result {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
self.write_buf.push_back(b'\n');
|
self.write_buf.push_back(b'\n');
|
||||||
self.action_response_map.insert(tag, response_chan);
|
self.action_response_map.insert(tag, response_chan);
|
||||||
},
|
}
|
||||||
Err(error) => error!(?op, %error, "Failed to serialize op"),
|
Err(error) => error!(?op, %error, "Failed to serialize op"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -244,7 +255,13 @@ impl Future for B3270 {
|
|||||||
'write: while !self.write_buf.is_empty() {
|
'write: while !self.write_buf.is_empty() {
|
||||||
let myself = &mut *self;
|
let myself = &mut *self;
|
||||||
let chunk = myself.write_buf.chunk();
|
let chunk = myself.write_buf.chunk();
|
||||||
let stdin = Pin::new(myself.child.stdin.as_mut().expect("Should always have child stdin"));
|
let stdin = Pin::new(
|
||||||
|
myself
|
||||||
|
.child
|
||||||
|
.stdin
|
||||||
|
.as_mut()
|
||||||
|
.expect("Should always have child stdin"),
|
||||||
|
);
|
||||||
match stdin.poll_write(cx, chunk) {
|
match stdin.poll_write(cx, chunk) {
|
||||||
Poll::Pending | Poll::Ready(Ok(0)) => {
|
Poll::Pending | Poll::Ready(Ok(0)) => {
|
||||||
break 'write;
|
break 'write;
|
||||||
|
|||||||
@@ -1,28 +1,29 @@
|
|||||||
|
use anyhow::anyhow;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use anyhow::anyhow;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let mut subprocess_args = vec![
|
let mut subprocess_args = vec![OsString::from_str("-json").unwrap()];
|
||||||
OsString::from_str("-json").unwrap(),
|
|
||||||
];
|
|
||||||
let mut args_iter = std::env::args_os().peekable();
|
let mut args_iter = std::env::args_os().peekable();
|
||||||
let mut connect_str = None;
|
let mut connect_str = None;
|
||||||
while let Some(arg) = args_iter.next() {
|
while let Some(arg) = args_iter.next() {
|
||||||
// we default to one of the ignored args
|
// we default to one of the ignored args
|
||||||
match arg.to_str().unwrap_or("-json") {
|
match arg.to_str().unwrap_or("-json") {
|
||||||
"-json" | "-xml" | "-indent" | "--" |
|
"-json" | "-xml" | "-indent" | "--" | "-scriptportonce" | "-nowrapperdoc"
|
||||||
"-scriptportonce" | "-nowrapperdoc" |
|
| "-socket" | "-v" | "--version" => {}
|
||||||
"-socket" | "-v" | "--version" => {}
|
|
||||||
"-scriptport" | "-httpd" => {
|
"-scriptport" | "-httpd" => {
|
||||||
args_iter.next();
|
args_iter.next();
|
||||||
}
|
}
|
||||||
"-connect" => {
|
"-connect" => {
|
||||||
connect_str = args_iter.next()
|
connect_str = args_iter
|
||||||
|
.next()
|
||||||
.ok_or_else(|| anyhow!("Arg required for -connect"))
|
.ok_or_else(|| anyhow!("Arg required for -connect"))
|
||||||
.and_then(|arg| arg.into_string().map_err(|_| anyhow!("Invalid connect string")))
|
.and_then(|arg| {
|
||||||
|
arg.into_string()
|
||||||
|
.map_err(|_| anyhow!("Invalid connect string"))
|
||||||
|
})
|
||||||
.map(Some)?;
|
.map(Some)?;
|
||||||
}
|
}
|
||||||
"-e" => {
|
"-e" => {
|
||||||
@@ -37,7 +38,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _connect_str = connect_str.ok_or_else(||anyhow!("No connect string given"))?;
|
let _connect_str = connect_str.ok_or_else(|| anyhow!("No connect string given"))?;
|
||||||
|
|
||||||
let subproc = tokio::process::Command::new("b3270")
|
let subproc = tokio::process::Command::new("b3270")
|
||||||
.args(&subprocess_args)
|
.args(&subprocess_args)
|
||||||
@@ -51,5 +52,4 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod connection;
|
||||||
pub mod connection;
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
eprintln!("Hello from d3270c");
|
eprintln!("Hello from d3270c");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user