/// Core value model for RFC 8866 Session Description Protocol documents.
///|
/// The library version. Keep this value aligned with `moon.mod`.
pub fn library_version() -> String {
"0.1.0"
}
///|
/// A source line retained for diagnostics and preserve-mode writing.
pub struct SdpLine {
kind : String
value : String
line : Int
}
///|
/// The `o=` origin field.
pub struct Origin {
username : String
session_id : String
session_version : String
network_type : String
address_type : String
unicast_address : String
}
///|
/// A `c=` connection field.
pub struct Connection {
network_type : String
address_type : String
connection_address : String
ttl : Int?
address_count : Int?
}
///|
/// A `b=` bandwidth field.
pub struct Bandwidth {
modifier : String
value : Int
}
///|
/// A `t=` time field and its associated `r=` repeat fields.
pub struct Timing {
start : String
stop : String
repeats : Array[String]
}
///|
/// A generic `a=` attribute. The value is absent for property attributes.
pub struct Attribute {
name : String
value : String?
line : Int
}
///|
/// A typed RTP payload mapping from `a=rtpmap`.
pub struct RtpMap {
payload_type : Int
encoding_name : String
clock_rate : Int
encoding_parameters : String?
}
///|
/// A typed `a=fmtp` mapping.
pub struct FormatParameters {
format : String
parameters : String
}
///|
/// A typed direction attribute.
pub enum Direction {
SendRecv
SendOnly
RecvOnly
Inactive
Unspecified
}
///|
/// A single RFC 8866 media description beginning with `m=`.
pub struct MediaDescription {
media : String
port : Int
port_count : Int
protocol : String
formats : Array[String]
mut information : String?
connections : Array[Connection]
bandwidths : Array[Bandwidth]
attributes : Array[Attribute]
source_lines : Array[SdpLine]
}
///|
/// A complete SDP document.
pub struct SessionDescription {
mut version : Int
mut origin : Origin?
mut session_name : String
mut information : String?
mut uri : String?
emails : Array[String]
phones : Array[String]
mut connection : Connection?
bandwidths : Array[Bandwidth]
timings : Array[Timing]
mut timezone_adjustments : String?
mut encryption_key : String?
attributes : Array[Attribute]
media_descriptions : Array[MediaDescription]
source_lines : Array[SdpLine]
raw_source : String
}
///|
/// Constructs a retained source line.
pub fn sdp_line(kind : String, value : String, line : Int) -> SdpLine {
{ kind, value, line }
}
///|
/// Constructs an origin value.
pub fn origin(
username : String,
session_id : String,
session_version : String,
network_type : String,
address_type : String,
unicast_address : String,
) -> Origin {
{
username,
session_id,
session_version,
network_type,
address_type,
unicast_address,
}
}
///|
/// Constructs a connection value.
pub fn connection(
network_type : String,
address_type : String,
connection_address : String,
ttl : Int?,
address_count : Int?,
) -> Connection {
{ network_type, address_type, connection_address, ttl, address_count }
}
///|
/// Constructs a bandwidth value.
pub fn bandwidth(modifier : String, value : Int) -> Bandwidth {
{ modifier, value }
}
///|
/// Constructs a timing value.
pub fn timing(start : String, stop : String) -> Timing {
{ start, stop, repeats: Array::new() }
}
///|
/// Constructs an attribute value.
pub fn attribute(name : String, value : String?, line : Int) -> Attribute {
{ name, value, line }
}
///|
/// Constructs a typed RTP mapping.
pub fn rtp_map(
payload_type : Int,
encoding_name : String,
clock_rate : Int,
encoding_parameters : String?,
) -> RtpMap {
{ payload_type, encoding_name, clock_rate, encoding_parameters }
}
///|
/// Constructs a typed format-parameter mapping.
pub fn format_parameters(
format : String,
parameters : String,
) -> FormatParameters {
{ format, parameters }
}
///|
/// Constructs an empty media description with its `m=` tuple.
pub fn media_description(
media : String,
port : Int,
port_count : Int,
protocol : String,
formats : Array[String],
) -> MediaDescription {
{
media,
port,
port_count,
protocol,
formats,
information: None,
connections: Array::new(),
bandwidths: Array::new(),
attributes: Array::new(),
source_lines: Array::new(),
}
}
///|
/// Constructs an empty session for generators and the parser.
pub fn empty_session(raw_source : String) -> SessionDescription {
{
version: 0,
origin: None,
session_name: "-",
information: None,
uri: None,
emails: Array::new(),
phones: Array::new(),
connection: None,
bandwidths: Array::new(),
timings: Array::new(),
timezone_adjustments: None,
encryption_key: None,
attributes: Array::new(),
media_descriptions: Array::new(),
source_lines: Array::new(),
raw_source,
}
}
///|
pub fn SdpLine::kind(self : SdpLine) -> String {
self.kind
}
///|
pub fn SdpLine::value(self : SdpLine) -> String {
self.value
}
///|
pub fn SdpLine::line(self : SdpLine) -> Int {
self.line
}
///|
pub fn Origin::username(self : Origin) -> String {
self.username
}
///|
pub fn Origin::session_id(self : Origin) -> String {
self.session_id
}
///|
pub fn Origin::session_version(self : Origin) -> String {
self.session_version
}
///|
pub fn Origin::network_type(self : Origin) -> String {
self.network_type
}
///|
pub fn Origin::address_type(self : Origin) -> String {
self.address_type
}
///|
pub fn Origin::unicast_address(self : Origin) -> String {
self.unicast_address
}
///|
pub fn Connection::network_type(self : Connection) -> String {
self.network_type
}
///|
pub fn Connection::address_type(self : Connection) -> String {
self.address_type
}
///|
pub fn Connection::address(self : Connection) -> String {
self.connection_address
}
///|
pub fn Connection::ttl(self : Connection) -> Int? {
self.ttl
}
///|
pub fn Connection::address_count(self : Connection) -> Int? {
self.address_count
}
///|
pub fn Bandwidth::modifier(self : Bandwidth) -> String {
self.modifier
}
///|
pub fn Bandwidth::value(self : Bandwidth) -> Int {
self.value
}
///|
pub fn Timing::start(self : Timing) -> String {
self.start
}
///|
pub fn Timing::stop(self : Timing) -> String {
self.stop
}
///|
pub fn Timing::repeats(self : Timing) -> Array[String] {
self.repeats
}
///|
pub fn Attribute::name(self : Attribute) -> String {
self.name
}
///|
pub fn Attribute::value(self : Attribute) -> String? {
self.value
}
///|
pub fn Attribute::line(self : Attribute) -> Int {
self.line
}
///|
pub fn Attribute::is_property(self : Attribute) -> Bool {
self.value is None
}
///|
pub fn RtpMap::payload_type(self : RtpMap) -> Int {
self.payload_type
}
///|
pub fn RtpMap::encoding_name(self : RtpMap) -> String {
self.encoding_name
}
///|
pub fn RtpMap::clock_rate(self : RtpMap) -> Int {
self.clock_rate
}
///|
pub fn RtpMap::encoding_parameters(self : RtpMap) -> String? {
self.encoding_parameters
}
///|
pub fn FormatParameters::format(self : FormatParameters) -> String {
self.format
}
///|
pub fn FormatParameters::parameters(self : FormatParameters) -> String {
self.parameters
}
///|
pub fn Direction::to_string(self : Direction) -> String {
match self {
SendRecv => "sendrecv"
SendOnly => "sendonly"
RecvOnly => "recvonly"
Inactive => "inactive"
Unspecified => "unspecified"
}
}
///|
pub fn MediaDescription::media(self : MediaDescription) -> String {
self.media
}
///|
pub fn MediaDescription::port(self : MediaDescription) -> Int {
self.port
}
///|
pub fn MediaDescription::port_count(self : MediaDescription) -> Int {
self.port_count
}
///|
pub fn MediaDescription::protocol(self : MediaDescription) -> String {
self.protocol
}
///|
pub fn MediaDescription::formats(self : MediaDescription) -> Array[String] {
self.formats
}
///|
pub fn MediaDescription::information(self : MediaDescription) -> String? {
self.information
}
///|
pub fn MediaDescription::connections(
self : MediaDescription,
) -> Array[Connection] {
self.connections
}
///|
pub fn MediaDescription::bandwidths(
self : MediaDescription,
) -> Array[Bandwidth] {
self.bandwidths
}
///|
pub fn MediaDescription::attributes(
self : MediaDescription,
) -> Array[Attribute] {
self.attributes
}
///|
pub fn MediaDescription::source_lines(
self : MediaDescription,
) -> Array[SdpLine] {
self.source_lines
}
///|
pub fn MediaDescription::is_rejected(self : MediaDescription) -> Bool {
self.port == 0
}
///|
pub fn SessionDescription::version(self : SessionDescription) -> Int {
self.version
}
///|
pub fn SessionDescription::origin(self : SessionDescription) -> Origin? {
self.origin
}
///|
pub fn SessionDescription::session_name(self : SessionDescription) -> String {
self.session_name
}
///|
pub fn SessionDescription::information(self : SessionDescription) -> String? {
self.information
}
///|
pub fn SessionDescription::uri(self : SessionDescription) -> String? {
self.uri
}
///|
pub fn SessionDescription::emails(self : SessionDescription) -> Array[String] {
self.emails
}
///|
pub fn SessionDescription::phones(self : SessionDescription) -> Array[String] {
self.phones
}
///|
pub fn SessionDescription::connection(self : SessionDescription) -> Connection? {
self.connection
}
///|
pub fn SessionDescription::bandwidths(
self : SessionDescription,
) -> Array[Bandwidth] {
self.bandwidths
}
///|
pub fn SessionDescription::timings(self : SessionDescription) -> Array[Timing] {
self.timings
}
///|
pub fn SessionDescription::timezone_adjustments(
self : SessionDescription,
) -> String? {
self.timezone_adjustments
}
///|
pub fn SessionDescription::encryption_key(self : SessionDescription) -> String? {
self.encryption_key
}
///|
pub fn SessionDescription::attributes(
self : SessionDescription,
) -> Array[Attribute] {
self.attributes
}
///|
pub fn SessionDescription::media_descriptions(
self : SessionDescription,
) -> Array[MediaDescription] {
self.media_descriptions
}
///|
pub fn SessionDescription::source_lines(
self : SessionDescription,
) -> Array[SdpLine] {
self.source_lines
}
///|
pub fn SessionDescription::raw_source(self : SessionDescription) -> String {
self.raw_source
}
///|
pub fn SessionDescription::media_count(self : SessionDescription) -> Int {
self.media_descriptions.length()
}
// Mutation helpers are deliberately package-private. They keep incremental
// parser state out of the public API while the returned document remains a
// normal value from a caller's perspective.
///|
fn SessionDescription::set_version(
self : SessionDescription,
value : Int,
) -> Unit {
self.version = value
}
///|
fn SessionDescription::set_origin(
self : SessionDescription,
value : Origin,
) -> Unit {
self.origin = Some(value)
}
///|
fn SessionDescription::set_session_name(
self : SessionDescription,
value : String,
) -> Unit {
self.session_name = value
}
///|
fn SessionDescription::set_information(
self : SessionDescription,
value : String,
) -> Unit {
self.information = Some(value)
}
///|
fn SessionDescription::set_uri(
self : SessionDescription,
value : String,
) -> Unit {
self.uri = Some(value)
}
///|
fn SessionDescription::set_connection(
self : SessionDescription,
value : Connection,
) -> Unit {
self.connection = Some(value)
}
///|
fn SessionDescription::set_timezone_adjustments(
self : SessionDescription,
value : String,
) -> Unit {
self.timezone_adjustments = Some(value)
}
///|
fn SessionDescription::set_encryption_key(
self : SessionDescription,
value : String,
) -> Unit {
self.encryption_key = Some(value)
}
///|
fn MediaDescription::set_information(
self : MediaDescription,
value : String,
) -> Unit {
self.information = Some(value)
}
///|
/// Returns all session-level attributes with the requested case-insensitive name.
pub fn SessionDescription::attributes_named(
self : SessionDescription,
name : String,
) -> Array[Attribute] {
let out : Array[Attribute] = Array::new()
for item in self.attributes {
if item.name.equal_ignore_ascii_case(name) {
out.push(item)
}
}
out
}
///|
/// Returns all media-level attributes with the requested case-insensitive name.
pub fn MediaDescription::attributes_named(
self : MediaDescription,
name : String,
) -> Array[Attribute] {
let out : Array[Attribute] = Array::new()
for item in self.attributes {
if item.name.equal_ignore_ascii_case(name) {
out.push(item)
}
}
out
}
///|
/// Returns the first attribute value with the requested name.
pub fn MediaDescription::attribute_value(
self : MediaDescription,
name : String,
) -> String? {
for item in self.attributes {
if item.name.equal_ignore_ascii_case(name) {
return item.value
}
}
None
}
///|
/// Computes the effective RFC 3264 direction, inheriting from the session.
pub fn MediaDescription::direction(
self : MediaDescription,
session_attributes : Array[Attribute],
) -> Direction {
for item in self.attributes {
match direction_from_name(item.name) {
Unspecified => ()
other => return other
}
}
for item in session_attributes {
match direction_from_name(item.name) {
Unspecified => ()
other => return other
}
}
SendRecv
}
///|
fn direction_from_name(name : String) -> Direction {
if name.equal_ignore_ascii_case("sendrecv") {
SendRecv
} else if name.equal_ignore_ascii_case("sendonly") {
SendOnly
} else if name.equal_ignore_ascii_case("recvonly") {
RecvOnly
} else if name.equal_ignore_ascii_case("inactive") {
Inactive
} else {
Unspecified
}
}
///|
/// Returns a compact deterministic diagnostic representation.
pub fn SessionDescription::to_debug_string(self : SessionDescription) -> String {
let sb = StringBuilder()
sb.write_string("Session(version=")
sb.write_string(self.version.to_string())
sb.write_string(", name=")
sb.write_string(self.session_name)
sb.write_string(", timings=")
sb.write_string(self.timings.length().to_string())
sb.write_string(", media=")
sb.write_string(self.media_descriptions.length().to_string())
sb.write_string(", attributes=")
sb.write_string(self.attributes.length().to_string())
sb.write_char(')')
sb.to_string()
}