#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(test)]
extern crate alloc;
mod rules;
use core::fmt;
use kinavis::relative_motion::{Contact, Vessel};
use kinavis_kernel::angle::{Side, TrueCourse};
use kinavis_kernel::units::Angle;
pub use rules::rule_of_the_road;
#[cfg(doctest)]
#[doc = include_str!("../README.md")]
pub struct ReadmeExamples;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Tack {
Port,
Starboard,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum VesselCategory {
PowerDriven,
Sailing {
tack: Option<Tack>,
},
Fishing,
ConstrainedByDraught,
RestrictedInAbilityToManoeuvre,
NotUnderCommand,
}
impl VesselCategory {
#[must_use]
pub const fn precedence(self) -> u8 {
match self {
Self::PowerDriven => 0,
Self::Sailing { .. } => 1,
Self::Fishing => 2,
Self::ConstrainedByDraught => 3,
Self::RestrictedInAbilityToManoeuvre => 4,
Self::NotUnderCommand => 5,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Party {
motion: Vessel,
heading: Option<TrueCourse>,
category: VesselCategory,
}
impl Party {
#[must_use]
pub const fn new(motion: Vessel, category: VesselCategory) -> Self {
Self {
motion,
heading: None,
category,
}
}
#[must_use]
pub const fn with_heading(mut self, heading: TrueCourse) -> Self {
self.heading = Some(heading);
self
}
#[must_use]
pub const fn motion(&self) -> Vessel {
self.motion
}
#[must_use]
pub fn heading(&self) -> TrueCourse {
self.heading.unwrap_or(self.motion.course)
}
#[must_use]
pub const fn category(&self) -> VesselCategory {
self.category
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Visibility {
InSight,
Restricted,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Situation {
own: Party,
target: Party,
contact: Contact,
visibility: Visibility,
wind_from: Option<TrueCourse>,
}
impl Situation {
#[must_use]
pub const fn new(own: Party, target: Party, contact: Contact, visibility: Visibility) -> Self {
Self {
own,
target,
contact,
visibility,
wind_from: None,
}
}
#[must_use]
pub const fn with_wind_from(mut self, from: TrueCourse) -> Self {
self.wind_from = Some(from);
self
}
#[must_use]
pub const fn own(&self) -> Party {
self.own
}
#[must_use]
pub const fn target(&self) -> Party {
self.target
}
#[must_use]
pub const fn contact(&self) -> Contact {
self.contact
}
#[must_use]
pub const fn visibility(&self) -> Visibility {
self.visibility
}
#[must_use]
pub const fn wind_from(&self) -> Option<TrueCourse> {
self.wind_from
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ColregsConfig {
pub overtaking_abaft_beam: Angle,
pub head_on_half_width: Angle,
}
impl ColregsConfig {
pub const STANDARD: Self = Self {
overtaking_abaft_beam: Angle::from_degrees_unchecked(22.5),
head_on_half_width: Angle::from_degrees_unchecked(6.0),
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Encounter {
Overtaking,
BeingOvertaken,
HeadOn,
Crossing {
target_on: Side,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Responsibility {
GiveWay,
StandOn,
Both,
Undetermined,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Rule {
Rule12,
Rule13,
Rule14,
Rule15,
Rule17,
Rule18,
Rule19,
}
impl fmt::Display for Rule {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let number = match self {
Self::Rule12 => 12,
Self::Rule13 => 13,
Self::Rule14 => 14,
Self::Rule15 => 15,
Self::Rule17 => 17,
Self::Rule18 => 18,
Self::Rule19 => 19,
};
write!(f, "Rule {number}")
}
}
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PermittedManoeuvre {
pub starboard: bool,
pub port: bool,
pub slow_down: bool,
pub hold: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Ruling {
encounter: Encounter,
responsibility: Responsibility,
rule: Rule,
manoeuvre: PermittedManoeuvre,
}
impl Ruling {
#[must_use]
pub const fn encounter(&self) -> Encounter {
self.encounter
}
#[must_use]
pub const fn responsibility(&self) -> Responsibility {
self.responsibility
}
#[must_use]
pub const fn rule(&self) -> Rule {
self.rule
}
#[must_use]
pub const fn manoeuvre(&self) -> PermittedManoeuvre {
self.manoeuvre
}
}