///|
pub(all) struct Resolution {
x : Int
y : Int
units : Int
} derive(Eq, Debug)
///|
pub fn Resolution::dpi(x : Int, y : Int) -> Resolution raise IppError {
if x < 1 || y < 1 {
raise Invalid("resolution must be positive")
}
{ x, y, units: 3, }
}
///|
pub(all) struct IppDateTime {
year : Int
month : Int
day : Int
hour : Int
minute : Int
second : Int
decisecond : Int
utc_direction : Int
utc_hours : Int
utc_minutes : Int
} derive(Eq, Debug)
///|
pub fn IppDateTime::validate(self : IppDateTime) -> Unit raise IppError {
if self.year < 0 || self.year > 65535 {
raise Invalid("date year out of range")
}
if self.month < 1 || self.month > 12 {
raise Invalid("date month out of range")
}
let days = match self.month {
4 | 6 | 9 | 11 => 30
2 =>
if self.year % 400 == 0 || (self.year % 4 == 0 && self.year % 100 != 0) {
29
} else {
28
}
_ => 31
}
if self.day < 1 || self.day > days {
raise Invalid("date day out of range")
}
if self.hour < 0 || self.hour > 23 || self.minute < 0 || self.minute > 59 {
raise Invalid("date time out of range")
}
if self.second < 0 ||
self.second > 60 ||
self.decisecond < 0 ||
self.decisecond > 9 {
raise Invalid("date seconds out of range")
}
if self.utc_direction != 43 && self.utc_direction != 45 {
raise Invalid("date UTC direction must be + or -")
}
if self.utc_hours < 0 ||
self.utc_hours > 13 ||
self.utc_minutes < 0 ||
self.utc_minutes > 59 {
raise Invalid("date UTC offset out of range")
}
}
///|
pub(all) enum IppValue {
Integer(Int)
Enumeration(Int)
Boolean(Bool)
Range(Int, Int)
ResolutionValue(Resolution)
DateTime(IppDateTime)
Text(String)
Name(String)
Keyword(String)
Uri(String)
UriScheme(String)
Charset(String)
Language(String)
MimeType(String)
Octets(Bytes)
TextWithLanguage(String, String)
NameWithLanguage(String, String)
Collection(Array[Attribute])
OutOfBand(Int)
Unknown(Int, Bytes)
} derive(Eq, Debug)
///|
pub(all) struct Attribute {
name : String
values : Array[IppValue]
} derive(Eq, Debug)
///|
pub fn Attribute::new(name : String, value : IppValue) -> Attribute {
{ name, values: [value], }
}
///|
pub fn Attribute::integers(name : String, values : Array[Int]) -> Attribute {
{ name, values: values.map(fn(n) { IppValue::Integer(n) }), }
}
///|
pub fn Attribute::keywords(name : String, values : Array[String]) -> Attribute {
{ name, values: values.map(fn(s) { IppValue::Keyword(s) }), }
}
///|
pub fn Attribute::first(self : Attribute) -> IppValue? {
self.values.get(0)
}
///|
pub fn Attribute::string_values(self : Attribute) -> Array[String] {
self.values.filter_map(fn(value) { value.as_string() })
}
///|
pub fn IppValue::as_string(self : IppValue) -> String? {
match self {
Text(s)
| Name(s)
| Keyword(s)
| Uri(s)
| UriScheme(s)
| Charset(s)
| Language(s)
| MimeType(s) => Some(s)
TextWithLanguage(_, s) | NameWithLanguage(_, s) => Some(s)
_ => None
}
}
///|
pub fn IppValue::as_int(self : IppValue) -> Int? {
match self {
Integer(n) | Enumeration(n) => Some(n)
_ => None
}
}
///|
pub fn IppValue::as_bool(self : IppValue) -> Bool? {
match self {
Boolean(b) => Some(b)
_ => None
}
}
///|
pub fn IppValue::tag(self : IppValue) -> Int {
match self {
Integer(_) => 0x21
Boolean(_) => 0x22
Enumeration(_) => 0x23
Octets(_) => 0x30
DateTime(_) => 0x31
ResolutionValue(_) => 0x32
Range(_, _) => 0x33
Collection(_) => 0x34
TextWithLanguage(_, _) => 0x35
NameWithLanguage(_, _) => 0x36
Text(_) => 0x41
Name(_) => 0x42
Keyword(_) => 0x44
Uri(_) => 0x45
UriScheme(_) => 0x46
Charset(_) => 0x47
Language(_) => 0x48
MimeType(_) => 0x49
OutOfBand(tag) | Unknown(tag, _) => tag
}
}
///|
pub(all) struct Group {
tag : Int
attributes : Array[Attribute]
} derive(Eq, Debug)
///|
pub fn Group::operation(attributes : Array[Attribute]) -> Group {
{ tag: 0x01, attributes, }
}
///|
pub fn Group::job(attributes : Array[Attribute]) -> Group {
{ tag: 0x02, attributes, }
}
///|
pub fn Group::printer(attributes : Array[Attribute]) -> Group {
{ tag: 0x04, attributes, }
}
///|
pub fn Group::find(self : Group, name : String) -> Attribute? {
for attribute in self.attributes {
if attribute.name == name {
return Some(attribute)
}
}
None
}
///|
pub fn Group::get_string(self : Group, name : String) -> String? {
match self.find(name) {
Some(attribute) =>
match attribute.first() {
Some(value) => value.as_string()
None => None
}
None => None
}
}
///|
pub fn Group::get_int(self : Group, name : String) -> Int? {
match self.find(name) {
Some(attribute) =>
match attribute.first() {
Some(value) => value.as_int()
None => None
}
None => None
}
}
///|
pub fn Group::get_bool(self : Group, name : String) -> Bool? {
match self.find(name) {
Some(attribute) =>
match attribute.first() {
Some(value) => value.as_bool()
None => None
}
None => None
}
}
///|
pub(all) struct Message {
version : Version
code : Int
request_id : Int
groups : Array[Group]
document : Bytes
} derive(Eq, Debug)
///|
pub fn Message::new(
code : Int,
request_id : Int,
groups : Array[Group],
) -> Message {
{ version: Version::v20(), code, request_id, groups, document: b"", }
}
///|
pub fn Message::find_group(self : Message, tag : Int) -> Group? {
for group in self.groups {
if group.tag == tag {
return Some(group)
}
}
None
}
///|
pub fn Message::find(self : Message, tag : Int, name : String) -> Attribute? {
match self.find_group(tag) {
Some(group) => group.find(name)
None => None
}
}
///|
pub fn Message::with_document(self : Message, document : Bytes) -> Message {
{ ..self, document, }
}
///|
pub fn Message::is_success(self : Message) -> Bool {
self.code >= 0 && self.code <= 0x00ff
}