Lots of changes
This commit is contained in:
137
Cargo.lock
generated
137
Cargo.lock
generated
@@ -65,6 +65,15 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ansi_term"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.71"
|
version = "1.0.71"
|
||||||
@@ -294,6 +303,17 @@ version = "1.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3"
|
checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi 0.1.19",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -439,6 +459,21 @@ dependencies = [
|
|||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "2.34.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||||
|
dependencies = [
|
||||||
|
"ansi_term",
|
||||||
|
"atty",
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"strsim",
|
||||||
|
"textwrap",
|
||||||
|
"unicode-width",
|
||||||
|
"vec_map",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codespan-reporting"
|
name = "codespan-reporting"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
@@ -609,6 +644,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_cbor",
|
"serde_cbor",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"structopt",
|
||||||
"tide",
|
"tide",
|
||||||
"tide-websockets",
|
"tide-websockets",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -839,6 +875,24 @@ version = "1.8.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -990,7 +1044,7 @@ version = "1.0.10"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
|
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"hermit-abi 0.3.1",
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
@@ -1019,6 +1073,12 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.143"
|
version = "0.2.143"
|
||||||
@@ -1183,6 +1243,30 @@ version = "0.2.17"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-hack"
|
name = "proc-macro-hack"
|
||||||
version = "0.5.20+deprecated"
|
version = "0.5.20+deprecated"
|
||||||
@@ -1553,6 +1637,36 @@ version = "0.1.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
|
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "structopt"
|
||||||
|
version = "0.3.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"lazy_static",
|
||||||
|
"structopt-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "structopt-derive"
|
||||||
|
version = "0.4.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
@@ -1599,6 +1713,15 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.40"
|
version = "1.0.40"
|
||||||
@@ -1798,6 +1921,12 @@ dependencies = [
|
|||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.10"
|
version = "0.1.10"
|
||||||
@@ -1846,6 +1975,12 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vec_map"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
|
|||||||
@@ -13,4 +13,7 @@ serde = { version = "1.0.162", features = ["derive"]}
|
|||||||
serde_json = "1.0.96"
|
serde_json = "1.0.96"
|
||||||
serde_cbor = "0.11.2"
|
serde_cbor = "0.11.2"
|
||||||
anyhow = "1.0.71"
|
anyhow = "1.0.71"
|
||||||
bitflags = "2.2.1"
|
bitflags = "2.2.1"
|
||||||
|
|
||||||
|
# deps for bins
|
||||||
|
structopt = "0.3.26"
|
||||||
|
|||||||
@@ -87,18 +87,22 @@ pub enum InitializeIndication {
|
|||||||
Models(Vec<Model>),
|
Models(Vec<Model>),
|
||||||
/// Change in the state of the Operator Information Area
|
/// Change in the state of the Operator Information Area
|
||||||
Oia(Oia),
|
Oia(Oia),
|
||||||
/// List of supported proxies
|
|
||||||
Proxies(Vec<Proxy>),
|
|
||||||
/// Set of supported prefixes
|
/// Set of supported prefixes
|
||||||
Prefixes{value: String},
|
Prefixes{value: String},
|
||||||
|
/// List of supported proxies
|
||||||
|
Proxies(Vec<Proxy>),
|
||||||
/// Screen dimensions/characteristics changed
|
/// Screen dimensions/characteristics changed
|
||||||
ScreenMode(ScreenMode),
|
ScreenMode(ScreenMode),
|
||||||
/// Setting changed
|
/// Setting changed
|
||||||
Setting(Setting),
|
Setting(Setting),
|
||||||
|
/// Scroll thumb position
|
||||||
|
Thumb(Thumb),
|
||||||
/// Indicates build-time TLS config
|
/// Indicates build-time TLS config
|
||||||
TlsHello(TlsHello),
|
TlsHello(TlsHello),
|
||||||
/// TLS state changed
|
/// TLS state changed
|
||||||
Tls(Tls),
|
Tls(Tls),
|
||||||
|
/// Trace file
|
||||||
|
TraceFile(TraceFile),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use crate::b3270::types::GraphicRendition;
|
use crate::b3270::types::{Color, GraphicRendition};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
|
||||||
#[serde(rename_all="kebab-case")]
|
#[serde(rename_all="kebab-case")]
|
||||||
@@ -69,7 +69,7 @@ pub enum ConnectionState {
|
|||||||
ConnectedETn3270e,
|
ConnectedETn3270e,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[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")]
|
||||||
@@ -147,18 +147,54 @@ pub enum OiaField {
|
|||||||
value: bool,
|
value: bool,
|
||||||
},
|
},
|
||||||
/// Screen trace count
|
/// Screen trace count
|
||||||
Screentrace {
|
ScreenTrace {
|
||||||
value: String,
|
value: String,
|
||||||
},
|
},
|
||||||
/// Host command timer (minutes:seconds)
|
/// Host command timer (minutes:seconds)
|
||||||
Script {
|
Script {
|
||||||
value: String,
|
value: String,
|
||||||
},
|
},
|
||||||
|
Timing {
|
||||||
|
#[serde(default, skip_serializing_if="Option::is_none")]
|
||||||
|
value: Option<String>,
|
||||||
|
},
|
||||||
Typeahead {
|
Typeahead {
|
||||||
value: bool,
|
value: bool,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, Ord, PartialOrd, PartialEq, Hash)]
|
||||||
|
pub enum OiaFieldName {
|
||||||
|
Compose,
|
||||||
|
Insert,
|
||||||
|
Lock,
|
||||||
|
Lu,
|
||||||
|
NotUndera,
|
||||||
|
PrinterSession,
|
||||||
|
ReverseInput,
|
||||||
|
ScreenTrace,
|
||||||
|
Script,
|
||||||
|
Timing,
|
||||||
|
Typeahead,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OiaField {
|
||||||
|
pub fn field_name(&self) -> OiaFieldName {
|
||||||
|
match self {
|
||||||
|
OiaField::Compose {..} => OiaFieldName::Compose,
|
||||||
|
OiaField::Insert {..} => OiaFieldName::Insert,
|
||||||
|
OiaField::Lock {..} => OiaFieldName::Lock,
|
||||||
|
OiaField::Lu {..} => OiaFieldName::Lu,
|
||||||
|
OiaField::NotUndera {..} => OiaFieldName::NotUndera,
|
||||||
|
OiaField::PrinterSession {..} => OiaFieldName::PrinterSession,
|
||||||
|
OiaField::ReverseInput {..} => OiaFieldName::ReverseInput,
|
||||||
|
OiaField::ScreenTrace {..} => OiaFieldName::ScreenTrace,
|
||||||
|
OiaField::Script {..} => OiaFieldName::Script,
|
||||||
|
OiaField::Timing {..} => OiaFieldName::Timing,
|
||||||
|
OiaField::Typeahead {..} => OiaFieldName::Typeahead,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
pub struct Proxy {
|
pub struct Proxy {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@@ -211,7 +247,7 @@ pub struct ConnectAttempt {
|
|||||||
pub port: String,
|
pub port: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
|
||||||
// TODO: change this to an enum
|
// TODO: change this to an enum
|
||||||
pub struct Cursor {
|
pub struct Cursor {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
@@ -342,27 +378,6 @@ pub struct Scroll {
|
|||||||
pub bg: Option<Color>,
|
pub bg: Option<Color>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
|
|
||||||
#[serde(rename="camelCase")]
|
|
||||||
pub enum Color {
|
|
||||||
NeutralBlack,
|
|
||||||
Blue,
|
|
||||||
Red,
|
|
||||||
Pink,
|
|
||||||
Green,
|
|
||||||
Turquoise,
|
|
||||||
Yellow,
|
|
||||||
NeutralWhite,
|
|
||||||
Black,
|
|
||||||
DeepBlue,
|
|
||||||
Orange,
|
|
||||||
Purple,
|
|
||||||
PaleGreen,
|
|
||||||
PaleTurquoise,
|
|
||||||
Gray,
|
|
||||||
White,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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 {
|
||||||
@@ -412,3 +427,13 @@ pub struct UiError {
|
|||||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
#[serde(default, skip_serializing_if="Option::is_none")]
|
||||||
pub column: Option<usize>,
|
pub column: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
pub fn connection_state_serializes_as_expected() {
|
||||||
|
assert_eq!(serde_json::to_string(&ConnectionState::ConnectedETn3270e).unwrap(),r#""connected-e-tn3270e""#);
|
||||||
|
assert_eq!(serde_json::to_string(&ConnectionState::ConnectedESscp).unwrap(),r#""connected-e-sscp""#);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
// {"run":{"actions":[{"action":"Connect","args":["10.24.74.37:3270"]}]}}
|
||||||
|
|
||||||
// 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")]
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ bitflags! {
|
|||||||
const PRIVATE_USE = 0x080;
|
const PRIVATE_USE = 0x080;
|
||||||
const NO_COPY = 0x100;
|
const NO_COPY = 0x100;
|
||||||
const WRAP = 0x200;
|
const WRAP = 0x200;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,4 +114,115 @@ mod 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)]
|
||||||
|
#[serde(rename="camelCase")]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum Color {
|
||||||
|
NeutralBlack,
|
||||||
|
Blue,
|
||||||
|
Red,
|
||||||
|
Pink,
|
||||||
|
Green,
|
||||||
|
Turquoise,
|
||||||
|
Yellow,
|
||||||
|
NeutralWhite,
|
||||||
|
Black,
|
||||||
|
DeepBlue,
|
||||||
|
Orange,
|
||||||
|
Purple,
|
||||||
|
PaleGreen,
|
||||||
|
PaleTurquoise,
|
||||||
|
Gray,
|
||||||
|
White,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Color> for u8 {
|
||||||
|
fn from(value: Color) -> Self {
|
||||||
|
use Color::*;
|
||||||
|
match value {
|
||||||
|
NeutralBlack => 0,
|
||||||
|
Blue => 1,
|
||||||
|
Red => 2,
|
||||||
|
Pink => 3,
|
||||||
|
Green => 4,
|
||||||
|
Turquoise => 5,
|
||||||
|
Yellow => 6,
|
||||||
|
NeutralWhite => 7,
|
||||||
|
Black => 8,
|
||||||
|
DeepBlue => 9,
|
||||||
|
Orange => 10,
|
||||||
|
Purple => 11,
|
||||||
|
PaleGreen => 12,
|
||||||
|
PaleTurquoise => 13,
|
||||||
|
Gray => 14,
|
||||||
|
White => 15,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u8> for Color {
|
||||||
|
fn from(value: u8) -> Self {
|
||||||
|
use Color::*;
|
||||||
|
match value & 0xF {
|
||||||
|
0 => NeutralBlack,
|
||||||
|
1 => Blue,
|
||||||
|
2 => Red,
|
||||||
|
3 => Pink,
|
||||||
|
4 => Green,
|
||||||
|
5 => Turquoise,
|
||||||
|
6 => Yellow,
|
||||||
|
7 => NeutralWhite,
|
||||||
|
8 => Black,
|
||||||
|
9 => DeepBlue,
|
||||||
|
10 => Orange,
|
||||||
|
11 => Purple,
|
||||||
|
12 => PaleGreen,
|
||||||
|
13 => PaleTurquoise,
|
||||||
|
14 => Gray,
|
||||||
|
15 => White,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PackedAttr {
|
||||||
|
fn c_gr(self) -> GraphicRendition;
|
||||||
|
fn c_fg(self) -> Color;
|
||||||
|
fn c_bg(self) -> Color;
|
||||||
|
fn c_setgr(self, gr: GraphicRendition) -> Self;
|
||||||
|
fn c_setfg(self, fg: Color) -> Self;
|
||||||
|
fn c_setbg(self, bg: Color) -> Self;
|
||||||
|
fn c_pack(fg: Color, bg: Color, gr: GraphicRendition) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackedAttr for u32 {
|
||||||
|
fn c_gr(self) -> GraphicRendition {
|
||||||
|
GraphicRendition::from_bits_truncate((self & 0xFFFF) as u16)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_fg(self) -> Color {
|
||||||
|
((self >> 16 & 0xF) as u8).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_bg(self) -> Color {
|
||||||
|
((self >> 20 & 0xF) as u8).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_setgr(self, gr: GraphicRendition) -> Self {
|
||||||
|
self & !0xFFFF | gr.bits() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_setfg(self, fg: Color) -> Self {
|
||||||
|
self & !0xF0000 | (u8::from(fg) as u32) << 16
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_setbg(self, bg: Color) -> Self {
|
||||||
|
self & !0xF0000 | (u8::from(bg) as u32) << 20
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_pack(fg: Color, bg: Color, gr: GraphicRendition) -> Self {
|
||||||
|
0.c_setfg(fg).c_setbg(bg).c_setgr(gr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
3
src/bin/d3270c/main.rs
Normal file
3
src/bin/d3270c/main.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
eprintln!("Hello from d3270c");
|
||||||
|
}
|
||||||
49
src/bin/d3270d/main.rs
Normal file
49
src/bin/d3270d/main.rs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
use std::ffi::OsString;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use anyhow::{anyhow, bail};
|
||||||
|
use structopt::StructOpt;
|
||||||
|
use d3270::tracker::Tracker;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
fn main() -> anyhow::Result<()> {
|
||||||
|
let mut subprocess_args = vec![
|
||||||
|
OsString::from_str("-json").unwrap(),
|
||||||
|
];
|
||||||
|
let mut args_iter = std::env::args_os().peekable();
|
||||||
|
let mut connect_str = None;
|
||||||
|
while let Some(arg) = args_iter.next() {
|
||||||
|
// we default to one of the ignored args
|
||||||
|
match arg.to_str().unwrap_or("-json") {
|
||||||
|
"-json" | "-xml" | "-indent" | "--" |
|
||||||
|
"-scriptportonce" | "-nowrapperdoc" |
|
||||||
|
"-socket" | "-v" | "--version" => {}
|
||||||
|
"-scriptport" | "-httpd" => {
|
||||||
|
args_iter.next();
|
||||||
|
}
|
||||||
|
"-connect" => {
|
||||||
|
connect_str = args_iter.next()
|
||||||
|
.ok_or_else(anyhow!("Arg required for -connect"))
|
||||||
|
.and_then(|arg| arg.into_string().map_err(|_| anyhow!("Invalid connect string")))
|
||||||
|
.map(Some)?;
|
||||||
|
}
|
||||||
|
"-e" => {
|
||||||
|
'skip: while let Some(arg) = args_iter.peek() {
|
||||||
|
if arg.to_str().unwrap_or("").starts_with("-") {
|
||||||
|
break 'skip;
|
||||||
|
}
|
||||||
|
args_iter.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => subprocess_args.push(arg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let connect_str = connect_str.ok_or_else(||anyhow!("No connect string given"))?;
|
||||||
|
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Subproc {
|
||||||
|
tracker: Tracker,
|
||||||
|
}
|
||||||
0
src/executor.rs
Normal file
0
src/executor.rs
Normal file
@@ -1,2 +1,4 @@
|
|||||||
pub mod b3270;
|
pub mod b3270;
|
||||||
pub mod tracker;
|
pub mod tracker;
|
||||||
|
|
||||||
|
pub mod executor;
|
||||||
322
src/tracker.rs
322
src/tracker.rs
@@ -1,5 +1,325 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use crate::b3270::indication::{Change, Connection, ConnectionState, CountOrText, Cursor, Erase, Oia, OiaFieldName, Row, Screen, ScreenMode, Scroll, Setting, TerminalName, Thumb, Tls, TraceFile};
|
||||||
|
use crate::b3270::{Indication, InitializeIndication};
|
||||||
|
use crate::b3270::types::{Color, GraphicRendition, PackedAttr};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
struct CharCell {
|
||||||
|
pub ch: char,
|
||||||
|
pub attr: u32,
|
||||||
|
}
|
||||||
pub struct Tracker {
|
pub struct Tracker {
|
||||||
|
screen: Vec<Vec<CharCell>>,
|
||||||
|
oia: HashMap<OiaFieldName, Oia>,
|
||||||
|
screen_mode: ScreenMode,
|
||||||
|
erase: Erase,
|
||||||
|
thumb: Thumb,
|
||||||
|
settings: HashMap<String, Setting>,
|
||||||
|
|
||||||
|
// These are not init indications, but need to be tracked anyways
|
||||||
|
cursor: Cursor,
|
||||||
|
connection: Connection,
|
||||||
|
formatted: bool,
|
||||||
|
terminal_name: Option<TerminalName>,
|
||||||
|
trace_file: Option<String>,
|
||||||
|
tls: Option<Tls>,
|
||||||
|
|
||||||
|
// These never change, but need to be represented in an initialize message
|
||||||
|
static_init: Vec<InitializeIndication>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Disposition {
|
||||||
|
// Deliver this indication to every connected client
|
||||||
|
Broadcast,
|
||||||
|
// Ignore this message
|
||||||
|
Drop,
|
||||||
|
// Send this message to one particular client
|
||||||
|
Direct(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tracker {
|
||||||
|
pub fn handle_indication(&mut self, indication: &mut Indication) -> Disposition {
|
||||||
|
match indication {
|
||||||
|
Indication::Bell { .. }
|
||||||
|
| Indication::ConnectAttempt(_)
|
||||||
|
| Indication::Flipped { .. }
|
||||||
|
| Indication::Font { .. }
|
||||||
|
| Indication::Icon { .. }
|
||||||
|
| Indication::Popup(_)
|
||||||
|
| Indication::Stats(_)
|
||||||
|
| Indication::WindowTitle { .. }
|
||||||
|
|
||||||
|
=> (),
|
||||||
|
Indication::Connection(conn) => {
|
||||||
|
self.connection = conn.clone();
|
||||||
|
}
|
||||||
|
Indication::Erase(erase) => {
|
||||||
|
self.erase.logical_cols = erase.logical_cols.or(self.erase.logical_cols);
|
||||||
|
self.erase.logical_rows = erase.logical_rows.or(self.erase.logical_rows);
|
||||||
|
self.erase.fg = erase.fg.or(self.erase.fg);
|
||||||
|
self.erase.bg = erase.bg.or(self.erase.bg);
|
||||||
|
|
||||||
|
let rows = self.erase.logical_rows.unwrap_or(self.screen_mode.rows) as usize;
|
||||||
|
let cols = self.erase.logical_cols.unwrap_or(self.screen_mode.cols) as usize;
|
||||||
|
|
||||||
|
self.screen = vec![
|
||||||
|
vec![CharCell{
|
||||||
|
attr: u32::c_pack(
|
||||||
|
erase.fg.unwrap_or(Color::NeutralBlack),
|
||||||
|
erase.bg.unwrap_or(Color::Blue),
|
||||||
|
GraphicRendition::empty(),
|
||||||
|
),
|
||||||
|
ch: ' ',
|
||||||
|
};cols]
|
||||||
|
; rows
|
||||||
|
]
|
||||||
|
}
|
||||||
|
Indication::Formatted { state } => {self.formatted = *state; }
|
||||||
|
|
||||||
|
Indication::Initialize(init) => {
|
||||||
|
let mut static_init = Vec::with_capacity(init.len());
|
||||||
|
for indicator in init.clone() {
|
||||||
|
match indicator {
|
||||||
|
InitializeIndication::CodePages(_) |
|
||||||
|
InitializeIndication::Hello(_) |
|
||||||
|
InitializeIndication::Models(_) |
|
||||||
|
InitializeIndication::Prefixes { .. } |
|
||||||
|
InitializeIndication::Proxies(_) |
|
||||||
|
InitializeIndication::TlsHello(_) |
|
||||||
|
InitializeIndication::Tls(_) |
|
||||||
|
InitializeIndication::TraceFile(_) =>
|
||||||
|
static_init.push(indicator),
|
||||||
|
|
||||||
|
// The rest are passed through to normal processing.
|
||||||
|
InitializeIndication::Thumb(thumb) => {
|
||||||
|
self.handle_indication(&mut Indication::Thumb(thumb));
|
||||||
|
},
|
||||||
|
InitializeIndication::Setting(setting) => {
|
||||||
|
self.handle_indication(&mut Indication::Setting(setting));
|
||||||
|
}
|
||||||
|
InitializeIndication::ScreenMode(mode) => {
|
||||||
|
self.handle_indication(&mut Indication::ScreenMode(mode));
|
||||||
|
},
|
||||||
|
InitializeIndication::Oia(oia) => {
|
||||||
|
self.handle_indication(&mut Indication::Oia(oia));
|
||||||
|
}
|
||||||
|
InitializeIndication::Erase(erase) => {
|
||||||
|
self.handle_indication(&mut Indication::Erase(erase));
|
||||||
|
}
|
||||||
|
InitializeIndication::Connection(conn) => {
|
||||||
|
self.handle_indication(&mut Indication::Connection(conn));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Indication::Oia(oia) => {
|
||||||
|
self.oia.insert(oia.field.field_name(), oia.clone());
|
||||||
|
}
|
||||||
|
Indication::Screen(screen) => {
|
||||||
|
if let Some(cursor) = screen.cursor {
|
||||||
|
self.cursor = cursor;
|
||||||
|
}
|
||||||
|
for row in screen.rows.iter() {
|
||||||
|
let row_idx = row.row as usize - 1;
|
||||||
|
for change in row.changes.iter() {
|
||||||
|
let col_idx = change.column as usize - 1;
|
||||||
|
// update screen contents
|
||||||
|
let cols = self.screen[row_idx].iter_mut().skip(col_idx);
|
||||||
|
match change.change {
|
||||||
|
CountOrText::Count(n) => {
|
||||||
|
cols.take(n).for_each(|cell| {
|
||||||
|
let mut attr = cell.attr;
|
||||||
|
if let Some(fg) = change.fg {
|
||||||
|
attr = attr.c_setfg(fg);
|
||||||
|
}
|
||||||
|
if let Some(bg) = change.bg {
|
||||||
|
attr = attr.c_setbg(bg);
|
||||||
|
}
|
||||||
|
if let Some(gr) = change.gr {
|
||||||
|
attr = attr.c_setgr(gr);
|
||||||
|
}
|
||||||
|
cell.attr = attr;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
CountOrText::Text(ref text) => {
|
||||||
|
cols.zip(text.chars()).for_each(|(cell, ch)| {
|
||||||
|
let mut attr = cell.attr;
|
||||||
|
if let Some(fg) = change.fg {
|
||||||
|
attr = attr.c_setfg(fg);
|
||||||
|
}
|
||||||
|
if let Some(bg) = change.bg {
|
||||||
|
attr = attr.c_setbg(bg);
|
||||||
|
}
|
||||||
|
if let Some(gr) = change.gr {
|
||||||
|
attr = attr.c_setgr(gr);
|
||||||
|
}
|
||||||
|
cell.attr = attr;
|
||||||
|
cell.ch = ch;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Indication::ScreenMode(mode) => {
|
||||||
|
self.screen_mode = *mode;
|
||||||
|
self.handle_indication(&mut Indication::Erase(Erase{
|
||||||
|
logical_rows: Some(self.screen_mode.rows),
|
||||||
|
logical_cols: Some(self.screen_mode.cols),
|
||||||
|
fg: None,
|
||||||
|
bg: None,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
Indication::Scroll(Scroll{ fg, bg }) => {
|
||||||
|
let fg = fg.or(self.erase.fg).unwrap_or(Color::Blue);
|
||||||
|
let bg = bg.or(self.erase.bg).unwrap_or(Color::NeutralBlack);
|
||||||
|
let mut row = self.screen.remove(0);
|
||||||
|
row.fill(CharCell{
|
||||||
|
attr: u32::c_pack(fg, bg, GraphicRendition::empty()),
|
||||||
|
ch: ' ',
|
||||||
|
});
|
||||||
|
self.screen.push(row);
|
||||||
|
}
|
||||||
|
Indication::Setting(setting) => {
|
||||||
|
self.settings.insert(setting.name.clone(), setting.clone());
|
||||||
|
}
|
||||||
|
Indication::TerminalName(term) => {
|
||||||
|
self.terminal_name = Some(term.clone());
|
||||||
|
}
|
||||||
|
Indication::Thumb(thumb) => {
|
||||||
|
self.thumb = thumb.clone();
|
||||||
|
}
|
||||||
|
Indication::TraceFile(TraceFile{name}) => {
|
||||||
|
self.trace_file = name.clone();
|
||||||
|
}
|
||||||
|
Indication::Tls(tls) => {
|
||||||
|
self.tls = Some(tls.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// These need direction
|
||||||
|
Indication::UiError(_) => {} // we can assume that this came from the last sent command
|
||||||
|
Indication::Passthru(_) => {} // dunno how to handle this one
|
||||||
|
Indication::FileTransfer(_) => {}
|
||||||
|
Indication::RunResult(_) => {}
|
||||||
|
|
||||||
|
}
|
||||||
|
return Disposition::Broadcast
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_init_indication(&self) -> Vec<Indication> {
|
||||||
|
let mut contents = self.static_init.clone();
|
||||||
|
contents.push(InitializeIndication::ScreenMode(self.screen_mode));
|
||||||
|
contents.push(InitializeIndication::Erase(self.erase));
|
||||||
|
contents.push(InitializeIndication::Thumb(self.thumb));
|
||||||
|
|
||||||
|
contents.extend(self.oia.values()
|
||||||
|
.cloned()
|
||||||
|
.map(InitializeIndication::Oia));
|
||||||
|
contents.extend(self.settings.values()
|
||||||
|
.cloned()
|
||||||
|
.map(InitializeIndication::Setting));
|
||||||
|
contents.extend(self.tls.clone().map(InitializeIndication::Tls));
|
||||||
|
|
||||||
|
// Construct a screen snapshot
|
||||||
|
let mut result = vec![
|
||||||
|
Indication::Initialize(contents),
|
||||||
|
Indication::Connection(self.connection.clone()),
|
||||||
|
Indication::Screen(self.screen_snapshot()),
|
||||||
|
Indication::Formatted {state: self.formatted},
|
||||||
|
];
|
||||||
|
if let Some(terminal_name) = self.terminal_name.clone() {
|
||||||
|
result.push(Indication::TerminalName(terminal_name));
|
||||||
|
}
|
||||||
|
if let Some(trace_file) = self.trace_file.clone() {
|
||||||
|
result.push(Indication::TraceFile(TraceFile {
|
||||||
|
name: Some(trace_file),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_row(mut row: &[CharCell]) -> Vec<Change> {
|
||||||
|
let mut result = vec![];
|
||||||
|
let mut column = 1;
|
||||||
|
while !row.is_empty() {
|
||||||
|
let cur_gr = row[0].attr;
|
||||||
|
|
||||||
|
let split_pt = row.iter().take_while(|ch| ch.attr == cur_gr).count();
|
||||||
|
let (first, rest) = row.split_at(split_pt);
|
||||||
|
row = rest;
|
||||||
|
let content = first.iter().map(|cell| cell.ch).collect();
|
||||||
|
result.push(Change{
|
||||||
|
column,
|
||||||
|
fg: Some(cur_gr.c_fg()),
|
||||||
|
bg: Some(cur_gr.c_bg()),
|
||||||
|
gr: Some(cur_gr.c_gr()),
|
||||||
|
change: CountOrText::Text(content),
|
||||||
|
});
|
||||||
|
column += first.len() as u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn screen_snapshot(&self) -> Screen {
|
||||||
|
Screen{
|
||||||
|
cursor: Some(self.cursor),
|
||||||
|
rows: self.screen.iter()
|
||||||
|
.map(Vec::as_slice).map(Self::format_row)
|
||||||
|
.enumerate()
|
||||||
|
.map(|(row_id, changes)| Row{
|
||||||
|
row: row_id as u8 - 1,
|
||||||
|
changes,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Tracker {
|
||||||
|
fn default() -> Self {
|
||||||
|
let mut ret = Self {
|
||||||
|
screen: vec![],
|
||||||
|
oia: Default::default(),
|
||||||
|
screen_mode: ScreenMode {
|
||||||
|
cols: 80,
|
||||||
|
rows: 43,
|
||||||
|
color: true,
|
||||||
|
model: 4,
|
||||||
|
extended: true,
|
||||||
|
oversize: false,
|
||||||
|
},
|
||||||
|
erase: Erase {
|
||||||
|
logical_rows: None,
|
||||||
|
logical_cols: None,
|
||||||
|
fg: None,
|
||||||
|
bg: None,
|
||||||
|
},
|
||||||
|
thumb: Thumb {
|
||||||
|
top: 0.0,
|
||||||
|
shown: 0.0,
|
||||||
|
saved: 0,
|
||||||
|
screen: 0,
|
||||||
|
back: 0,
|
||||||
|
},
|
||||||
|
settings: Default::default(),
|
||||||
|
cursor: Cursor {
|
||||||
|
enabled: false,
|
||||||
|
row: None,
|
||||||
|
column: None
|
||||||
|
},
|
||||||
|
connection: Connection {
|
||||||
|
state: ConnectionState::NotConnected,
|
||||||
|
host: None,
|
||||||
|
cause: None,
|
||||||
|
},
|
||||||
|
formatted: false,
|
||||||
|
terminal_name: None,
|
||||||
|
trace_file: None,
|
||||||
|
tls: None,
|
||||||
|
static_init: vec![],
|
||||||
|
};
|
||||||
|
ret
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user