Skip to content

Commit

Permalink
Merge pull request #8 from jpreprocess/separate-question
Browse files Browse the repository at this point in the history
  • Loading branch information
cm-ayf authored Jan 17, 2024
2 parents 2d32810 + 3ec95e6 commit abe7fed
Show file tree
Hide file tree
Showing 19 changed files with 765 additions and 711 deletions.
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 8 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
[package]
name = "jlabel"
version = "0.1.0"
edition = "2021"
[workspace]
resolver = "2"
members = ["crates/*"]

[workspace.package]
version = "0.1.0"
rust-version = "1.65.0"

[dependencies]
thiserror = "1.0.56"
[workspace.dependencies]
jlabel = { path = "crates/jlabel", version = "0.1.0" }
jlabel-question = { path = "crates/jlabel-question", version = "0.1.0" }
10 changes: 10 additions & 0 deletions crates/jlabel-question/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "jlabel-question"
edition = "2021"

version.workspace = true
rust-version.workspace = true

[dependencies]
thiserror = "1.0.56"
jlabel.workspace = true
148 changes: 148 additions & 0 deletions crates/jlabel-question/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
pub mod position;

use std::num::ParseIntError;

use position::{
position, AllPosition, BooleanPosition, CategoryPosition, PhonePosition, Position,
SignedRangePosition, UndefinedPotision, UnsignedRangePosition,
};

use jlabel::Label;

#[derive(Debug, Clone, thiserror::Error, PartialEq, Eq)]
pub enum ParseError {
#[error("Failed splitting")]
FailSplitting,
#[error("Position mismatch")]
PositionMismatch,
#[error("Invalid position")]
InvalidPosition,
#[error("Empty patterns or range")]
Empty,
#[error("Incontinuous range")]
IncontinuousRange,
#[error("Failed wildcard: {0}")]
FailWildcard(ParseIntError),
#[error("Failed literal: {0}")]
FailLiteral(ParseIntError),
#[error("Invalid boolean: {0}")]
InvalidBoolean(String),
}

fn split_pattern(pattern: &str) -> Option<(&str, &str, &str)> {
let start = if pattern.starts_with("*/") {
4
} else if pattern.starts_with('*') {
2
} else {
0
};
let end = if pattern.ends_with(":*") {
pattern.len().checked_sub(4)?
} else if pattern.ends_with('*') {
pattern.len().checked_sub(2)?
} else {
pattern.len()
};
if start > end {
return None;
}

Some((&pattern[..start], &pattern[start..end], &pattern[end..]))
}

macro_rules! match_position {
($position:expr, $ranges:expr, [$($name:ident),*]) => {
match $position {
$(
AllPosition::$name(position) => Ok(AllQuestion::$name(Question::new(position, $ranges)?)),
)*
}
};
}

pub fn question(patterns: &[&str]) -> Result<AllQuestion, ParseError> {
let [first, rest @ ..] = patterns else {
return Err(ParseError::Empty);
};
let (prefix, range, suffix) = split_pattern(first).ok_or(ParseError::FailSplitting)?;

let mut ranges = Vec::with_capacity(patterns.len());
ranges.push(range);

for pattern in rest {
let (pre, range, suf) = split_pattern(pattern).ok_or(ParseError::FailSplitting)?;
if pre != prefix || suf != suffix {
return Err(ParseError::PositionMismatch);
}
ranges.push(range);
}

match_position!(
position(prefix, suffix).ok_or(ParseError::InvalidPosition)?,
&ranges,
[
Phone,
SignedRange,
UnsignedRange,
Boolean,
Category,
Undefined
]
)
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AllQuestion {
Phone(Question<PhonePosition>),
SignedRange(Question<SignedRangePosition>),
UnsignedRange(Question<UnsignedRangePosition>),
Boolean(Question<BooleanPosition>),
Category(Question<CategoryPosition>),
Undefined(Question<UndefinedPotision>),
}

impl AllQuestion {
pub fn test(&self, label: &Label) -> bool {
match self {
Self::Phone(q) => q.test(label),
Self::SignedRange(q) => q.test(label),
Self::UnsignedRange(q) => q.test(label),
Self::Boolean(q) => q.test(label),
Self::Category(q) => q.test(label),
Self::Undefined(q) => q.test(label),
}
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Question<P: Position> {
pub position: P,
pub range: Option<P::Range>,
}

impl<P: Position> Question<P> {
pub fn new(position: P, ranges: &[&str]) -> Result<Self, ParseError> {
match ranges {
["xx"] => Ok(Self {
range: None,
position,
}),
ranges => Ok(Self {
range: Some(position.range(ranges)?),
position,
}),
}
}

pub fn test(&self, label: &Label) -> bool {
match (&self.range, self.position.get(label)) {
(Some(range), Some(target)) => self.position.test(range, target),
(None, None) => true,
_ => false,
}
}
}

#[cfg(test)]
mod tests;
Loading

0 comments on commit abe7fed

Please sign in to comment.