use crate::{utils::*, Error, Result, Time, MAX_BITS};
use std::{fmt, str};
pub type LineId = u32;
pub type BitId = u8;
#[derive(Debug, Clone)]
pub struct LineMap {
map: Vec<BitId>,
}
impl LineMap {
const NOT_LINE: BitId = MAX_BITS;
pub fn new(lines: &[LineId]) -> Self {
let mut map: Vec<BitId> = (0..=lines.iter().max().copied().unwrap_or(0))
.map(|_| Self::NOT_LINE)
.collect();
for i in 0..lines.len() {
map[lines[i] as usize] = i as _;
}
Self { map }
}
pub fn get(&self, line: LineId) -> Result<BitId> {
let line = line as usize;
if line < self.map.len() {
let val = self.map[line];
if val != Self::NOT_LINE {
return Ok(val as _);
}
}
Err(invalid_data("Unknown line offset"))
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LineInfo {
pub direction: Direction,
pub active: Active,
pub edge: EdgeDetect,
pub used: bool,
pub bias: Bias,
pub drive: Drive,
pub name: String,
pub consumer: String,
}
impl fmt::Display for LineInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.name.is_empty() {
write!(f, "\t unnamed")?;
} else {
write!(f, "\t {:?}", self.name)?;
}
if self.consumer.is_empty() {
write!(f, "\t unused")?;
} else {
write!(f, "\t {:?}", self.consumer)?;
}
write!(f, "\t {}", self.direction)?;
write!(f, "\t active-{}", self.active)?;
if !matches!(self.edge, EdgeDetect::Disable) {
write!(f, "\t {}-edge", self.edge)?;
}
if !matches!(self.bias, Bias::Disable) {
write!(f, "\t {}", self.edge)?;
}
if !matches!(self.drive, Drive::PushPull) {
write!(f, "\t {}", self.drive)?;
}
if self.used {
write!(f, "\t [used]")?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
#[repr(u8)]
pub enum Direction {
#[cfg_attr(feature = "clap", clap(aliases = ["i", "in"]))]
Input,
#[cfg_attr(feature = "clap", clap(aliases = ["o", "out"]))]
Output,
}
impl Default for Direction {
fn default() -> Self {
Self::Input
}
}
impl AsRef<str> for Direction {
fn as_ref(&self) -> &str {
match self {
Self::Input => "input",
Self::Output => "output",
}
}
}
impl fmt::Display for Direction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_ref().fmt(f)
}
}
impl str::FromStr for Direction {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Ok(match s {
"i" | "in" | "input" => Self::Input,
"o" | "out" | "output" => Self::Output,
_ => return Err(invalid_input("Not recognized direction")),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
#[repr(u8)]
pub enum Active {
#[cfg_attr(feature = "clap", clap(aliases = ["l", "lo"]))]
Low,
#[cfg_attr(feature = "clap", clap(aliases = ["h", "hi"]))]
High,
}
impl Default for Active {
fn default() -> Self {
Self::High
}
}
impl AsRef<str> for Active {
fn as_ref(&self) -> &str {
match self {
Self::Low => "low",
Self::High => "high",
}
}
}
impl fmt::Display for Active {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_ref().fmt(f)
}
}
impl str::FromStr for Active {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Ok(match s {
"l" | "lo" | "low" | "active-low" => Self::Low,
"h" | "hi" | "high" | "active-high" => Self::High,
_ => return Err(invalid_input("Not recognized active state")),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
#[repr(u8)]
pub enum Edge {
#[cfg_attr(feature = "clap", clap(aliases = ["r", "rise"]))]
Rising,
#[cfg_attr(feature = "clap", clap(aliases = ["f", "fall"]))]
Falling,
}
impl AsRef<str> for Edge {
fn as_ref(&self) -> &str {
match self {
Self::Rising => "rising",
Self::Falling => "falling",
}
}
}
impl fmt::Display for Edge {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_ref().fmt(f)
}
}
impl str::FromStr for Edge {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Ok(match s {
"r" | "rise" | "rising" => Self::Rising,
"f" | "fall" | "falling" => Self::Falling,
_ => return Err(invalid_input("Not recognized edge")),
})
}
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Event {
pub line: BitId,
pub edge: Edge,
pub time: Time,
}
impl fmt::Display for Event {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
'#'.fmt(f)?;
self.line.fmt(f)?;
' '.fmt(f)?;
self.edge.fmt(f)?;
' '.fmt(f)?;
self.time.as_nanos().fmt(f)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
#[repr(u8)]
pub enum EdgeDetect {
#[cfg_attr(feature = "clap", clap(aliases = ["d", "dis"]))]
Disable,
#[cfg_attr(feature = "clap", clap(aliases = ["r", "rise"]))]
Rising,
#[cfg_attr(feature = "clap", clap(aliases = ["f", "fall"]))]
Falling,
#[cfg_attr(feature = "clap", clap(aliases = ["b"]))]
Both,
}
impl Default for EdgeDetect {
fn default() -> Self {
Self::Disable
}
}
impl AsRef<str> for EdgeDetect {
fn as_ref(&self) -> &str {
match self {
Self::Disable => "disable",
Self::Rising => "rising",
Self::Falling => "falling",
Self::Both => "both",
}
}
}
impl fmt::Display for EdgeDetect {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_ref().fmt(f)
}
}
impl str::FromStr for EdgeDetect {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Ok(match s {
"d" | "dis" | "disable" => Self::Disable,
"r" | "rise" | "rising" => Self::Rising,
"f" | "fall" | "falling" => Self::Falling,
"b" | "both" | "rise-fall" | "rising-falling" => Self::Both,
_ => return Err(invalid_input("Not recognized edge-detect")),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
#[repr(u8)]
pub enum Bias {
#[cfg_attr(feature = "clap", clap(aliases = ["d", "dis"]))]
Disable,
#[cfg_attr(feature = "clap", clap(aliases = ["pu"]))]
PullUp,
#[cfg_attr(feature = "clap", clap(aliases = ["pd"]))]
PullDown,
}
impl Default for Bias {
fn default() -> Self {
Self::Disable
}
}
impl AsRef<str> for Bias {
fn as_ref(&self) -> &str {
match self {
Self::Disable => "disable",
Self::PullUp => "pull-up",
Self::PullDown => "pull-down",
}
}
}
impl fmt::Display for Bias {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_ref().fmt(f)
}
}
impl str::FromStr for Bias {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Ok(match s {
"d" | "dis" | "disable" => Self::Disable,
"pu" | "pull-up" => Self::PullUp,
"pd" | "pull-down" => Self::PullUp,
_ => return Err(invalid_input("Not recognized input bias")),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
#[repr(u8)]
pub enum Drive {
#[cfg_attr(feature = "clap", clap(aliases = ["pp"]))]
PushPull,
#[cfg_attr(feature = "clap", clap(aliases = ["od"]))]
OpenDrain,
#[cfg_attr(feature = "clap", clap(aliases = ["os"]))]
OpenSource,
}
impl Default for Drive {
fn default() -> Self {
Self::PushPull
}
}
impl AsRef<str> for Drive {
fn as_ref(&self) -> &str {
match self {
Self::PushPull => "push-pull",
Self::OpenDrain => "open-drain",
Self::OpenSource => "open-source",
}
}
}
impl fmt::Display for Drive {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_ref().fmt(f)
}
}
impl str::FromStr for Drive {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Ok(match s {
"pp" | "push-pull" => Self::PushPull,
"od" | "open-drain" => Self::OpenDrain,
"os" | "open-source" => Self::OpenSource,
_ => return Err(invalid_input("Not recognized output drive")),
})
}
}