mirror of
https://github.com/kristoferssolo/customs-rs.git
synced 2025-10-21 19:20:33 +00:00
Initial commit
add officer
This commit is contained in:
commit
a8311c5e0f
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
72
Cargo.lock
generated
Normal file
72
Cargo.lock
generated
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.81"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "customs-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.78"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.52"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.57"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.57"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "customs-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
thiserror = "1"
|
||||||
|
anyhow = "1"
|
||||||
|
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
2
data/input/customs.i1
Normal file
2
data/input/customs.i1
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
1 1 10 10
|
||||||
|
X
|
||||||
4
data/input/customs.i2
Normal file
4
data/input/customs.i2
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
1 1 10 10
|
||||||
|
P 1
|
||||||
|
N 2
|
||||||
|
X
|
||||||
4
data/input/customs.i3
Normal file
4
data/input/customs.i3
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
1 1 10 10
|
||||||
|
P 1
|
||||||
|
P 2
|
||||||
|
X
|
||||||
13
data/input/customs.i4
Normal file
13
data/input/customs.i4
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
2 1 10 10
|
||||||
|
T P 2 21
|
||||||
|
P 1
|
||||||
|
P 2
|
||||||
|
P 3
|
||||||
|
P 4
|
||||||
|
P 5
|
||||||
|
P 6
|
||||||
|
P 7
|
||||||
|
P 8
|
||||||
|
P 9
|
||||||
|
P 10
|
||||||
|
X
|
||||||
13
data/input/customs.i5
Normal file
13
data/input/customs.i5
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
1 5 10 10
|
||||||
|
T N 1 5
|
||||||
|
T N 3 3
|
||||||
|
T N 5 1
|
||||||
|
T N 4 2
|
||||||
|
T N 2 5
|
||||||
|
N 997
|
||||||
|
N 1001
|
||||||
|
N 1002
|
||||||
|
N 1003
|
||||||
|
N 1004
|
||||||
|
N 1005
|
||||||
|
X
|
||||||
14
data/input/customs.i6
Normal file
14
data/input/customs.i6
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
2 3 10 50
|
||||||
|
T P 1 7
|
||||||
|
T N 2 80
|
||||||
|
P 1
|
||||||
|
N 2
|
||||||
|
N 10
|
||||||
|
N 20
|
||||||
|
N 30
|
||||||
|
N 40
|
||||||
|
P 45
|
||||||
|
P 50
|
||||||
|
P 53
|
||||||
|
N 60
|
||||||
|
X
|
||||||
2004
data/input/customs.i7
Normal file
2004
data/input/customs.i7
Normal file
File diff suppressed because it is too large
Load Diff
1
data/output/customs.o1
Normal file
1
data/output/customs.o1
Normal file
@ -0,0 +1 @@
|
|||||||
|
nothing
|
||||||
2
data/output/customs.o2
Normal file
2
data/output/customs.o2
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
1 11
|
||||||
|
2 12
|
||||||
2
data/output/customs.o3
Normal file
2
data/output/customs.o3
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
1 11
|
||||||
|
2 21
|
||||||
10
data/output/customs.o4
Normal file
10
data/output/customs.o4
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
1 11
|
||||||
|
3 21
|
||||||
|
2 23
|
||||||
|
4 31
|
||||||
|
6 41
|
||||||
|
5 44
|
||||||
|
7 51
|
||||||
|
9 61
|
||||||
|
8 65
|
||||||
|
10 71
|
||||||
6
data/output/customs.o5
Normal file
6
data/output/customs.o5
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
997 1002
|
||||||
|
1001 1006
|
||||||
|
1003 1006
|
||||||
|
1004 1006
|
||||||
|
1005 1006
|
||||||
|
1002 1007
|
||||||
10
data/output/customs.o6
Normal file
10
data/output/customs.o6
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
1 8
|
||||||
|
45 52
|
||||||
|
2 52
|
||||||
|
53 60
|
||||||
|
50 60
|
||||||
|
20 70
|
||||||
|
10 90
|
||||||
|
30 102
|
||||||
|
40 120
|
||||||
|
60 170
|
||||||
2000
data/output/customs.o7
Normal file
2000
data/output/customs.o7
Normal file
File diff suppressed because it is too large
Load Diff
48
src/citizenship.rs
Normal file
48
src/citizenship.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use crate::{error::InputError, utils::Time};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum CitizenshipType {
|
||||||
|
Citizen,
|
||||||
|
NonCitizen,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for CitizenshipType {
|
||||||
|
type Err = InputError;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s.trim() {
|
||||||
|
"P" => Ok(Self::Citizen),
|
||||||
|
"N" => Ok(Self::NonCitizen),
|
||||||
|
_ => Err(InputError::InvalidType("citizenship type".into())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Citizenship {
|
||||||
|
pub id: Time,
|
||||||
|
pub type_: CitizenshipType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Citizenship {
|
||||||
|
pub fn new(id: Time, type_: CitizenshipType) -> Self {
|
||||||
|
Self { id, type_ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Citizenship {
|
||||||
|
type Err = InputError;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let mut iter = s.split_whitespace();
|
||||||
|
|
||||||
|
match (iter.next(), iter.next()) {
|
||||||
|
(Some(citizenship), Some(id)) => {
|
||||||
|
let id = id.parse::<Time>().map_err(|_| InputError::InvalidFormat)?;
|
||||||
|
let type_ = CitizenshipType::from_str(citizenship)?;
|
||||||
|
Ok(Self { id, type_ })
|
||||||
|
}
|
||||||
|
_ => Err(InputError::InvalidFormat),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/customs.rs
Normal file
30
src/customs.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use crate::{citizenship::CitizenshipType, officer::Officer};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Customs {
|
||||||
|
pub citizens: Vec<Officer>,
|
||||||
|
pub noncitizens: Vec<Officer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Customs {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
citizens: Vec::new(),
|
||||||
|
noncitizens: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn push(&mut self, officer: Officer) {
|
||||||
|
match officer.citizenship {
|
||||||
|
CitizenshipType::Citizen => self.citizens.push(officer),
|
||||||
|
CitizenshipType::NonCitizen => self.noncitizens.push(officer),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.citizens.is_empty() && self.noncitizens.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sort(&mut self) {
|
||||||
|
self.citizens.sort();
|
||||||
|
self.noncitizens.sort();
|
||||||
|
}
|
||||||
|
}
|
||||||
66
src/customs_info.rs
Normal file
66
src/customs_info.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
citizenship::CitizenshipType,
|
||||||
|
error::InputError,
|
||||||
|
utils::{CustomsCount, Time},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DefaultOfficer {
|
||||||
|
pub count: CustomsCount,
|
||||||
|
pub time: Time,
|
||||||
|
pub citizenship: CitizenshipType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DefaultOfficer {
|
||||||
|
fn new(count: CustomsCount, time: Time, citizenship: CitizenshipType) -> Self {
|
||||||
|
Self {
|
||||||
|
count,
|
||||||
|
time,
|
||||||
|
citizenship,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CustomsInfo {
|
||||||
|
pub citizen: DefaultOfficer,
|
||||||
|
pub noncitizen: DefaultOfficer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CustomsInfo {
|
||||||
|
pub fn count(&self) -> u8 {
|
||||||
|
self.citizen.count + self.noncitizen.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for CustomsInfo {
|
||||||
|
type Err = InputError;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let nums = s
|
||||||
|
.split_whitespace()
|
||||||
|
.map(|x| x.parse::<Time>().map_err(|_| InputError::InvalidFormat))
|
||||||
|
.collect::<Result<Vec<_>, _>>();
|
||||||
|
|
||||||
|
let nums = nums?;
|
||||||
|
|
||||||
|
let (citizen_customs, noncitizen_customs, citizen_time, noncitizen_time) = (
|
||||||
|
nums[0] as CustomsCount,
|
||||||
|
nums[1] as CustomsCount,
|
||||||
|
nums[2],
|
||||||
|
nums[3],
|
||||||
|
);
|
||||||
|
let citizen = DefaultOfficer::new(citizen_customs, citizen_time, CitizenshipType::Citizen);
|
||||||
|
let noncitizen = DefaultOfficer::new(
|
||||||
|
noncitizen_customs,
|
||||||
|
noncitizen_time,
|
||||||
|
CitizenshipType::NonCitizen,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
citizen,
|
||||||
|
noncitizen,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/error.rs
Normal file
24
src/error.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum InputError {
|
||||||
|
#[error("invalid input format")]
|
||||||
|
InvalidFormat,
|
||||||
|
#[error("invalid {0}")]
|
||||||
|
InvalidType(String),
|
||||||
|
#[error("unknown input error")]
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum ParseError {
|
||||||
|
#[error("no first line found")]
|
||||||
|
NoFirstLine,
|
||||||
|
#[error("newcomer error")]
|
||||||
|
Newcomer,
|
||||||
|
#[error("citizenship error")]
|
||||||
|
Citizenship,
|
||||||
|
#[error("no citizens found")]
|
||||||
|
NoCitizens,
|
||||||
|
#[error("no customs found")]
|
||||||
|
NoCustoms,
|
||||||
|
}
|
||||||
8
src/lib.rs
Normal file
8
src/lib.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
pub mod citizenship;
|
||||||
|
pub mod customs;
|
||||||
|
pub mod customs_info;
|
||||||
|
pub mod error;
|
||||||
|
pub mod officer;
|
||||||
|
pub mod output;
|
||||||
|
pub mod process;
|
||||||
|
pub mod utils;
|
||||||
15
src/main.rs
Normal file
15
src/main.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
use std::{fs::File, io::Read};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use customs_rs::process::process;
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let filename = "customs.i4";
|
||||||
|
println!("\nFilename: {}\n", &filename);
|
||||||
|
let mut file = File::open(format!("data/input/{}", &filename))?;
|
||||||
|
let mut contents = String::new();
|
||||||
|
file.read_to_string(&mut contents)?;
|
||||||
|
let result = process(&contents);
|
||||||
|
print!("{result}");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
61
src/officer.rs
Normal file
61
src/officer.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
citizenship::CitizenshipType,
|
||||||
|
error::InputError,
|
||||||
|
utils::{CustomsCount, Time},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct Officer {
|
||||||
|
pub id: CustomsCount,
|
||||||
|
pub time: Time,
|
||||||
|
pub citizenship: CitizenshipType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Officer {
|
||||||
|
pub fn new(id: CustomsCount, time: Time, citizenship: CitizenshipType) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
time,
|
||||||
|
citizenship,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Officer {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
self.id.partial_cmp(&other.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Officer {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.id.cmp(&other.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Officer {
|
||||||
|
type Err = InputError;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let mut iter = s.split_whitespace();
|
||||||
|
|
||||||
|
match (iter.next(), iter.next(), iter.next(), iter.next()) {
|
||||||
|
(Some("T"), Some(citizenship_type), Some(customs_num), Some(time)) => {
|
||||||
|
let citizenship = CitizenshipType::from_str(&citizenship_type)?;
|
||||||
|
let time = time
|
||||||
|
.parse::<Time>()
|
||||||
|
.map_err(|_| InputError::InvalidFormat)?;
|
||||||
|
let id = customs_num
|
||||||
|
.parse::<CustomsCount>()
|
||||||
|
.map_err(|_| InputError::InvalidFormat)?;
|
||||||
|
Ok(Self {
|
||||||
|
id,
|
||||||
|
citizenship,
|
||||||
|
time,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Err(InputError::InvalidFormat),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
82
src/output.rs
Normal file
82
src/output.rs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
use std::{cmp, fmt::Display};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
citizenship::{Citizenship, CitizenshipType},
|
||||||
|
customs::Customs,
|
||||||
|
officer::Officer,
|
||||||
|
utils::Time,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Output(Vec<OutputTime>);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct OutputTime {
|
||||||
|
input: Time,
|
||||||
|
output: Time,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Output {
|
||||||
|
pub fn new(mut customs: Customs, all_citizens: Vec<Citizenship>) -> Self {
|
||||||
|
customs.sort();
|
||||||
|
dbg!(&customs);
|
||||||
|
let citizens = Self::from((&customs.citizens, &all_citizens, CitizenshipType::Citizen));
|
||||||
|
let noncitizens = Self::from((
|
||||||
|
&customs.noncitizens,
|
||||||
|
&all_citizens,
|
||||||
|
CitizenshipType::NonCitizen,
|
||||||
|
));
|
||||||
|
let mut output = Vec::new();
|
||||||
|
output.extend(citizens.0);
|
||||||
|
output.extend(noncitizens.0);
|
||||||
|
Self(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(&Vec<Officer>, &Vec<Citizenship>, CitizenshipType)> for Output {
|
||||||
|
fn from(value: (&Vec<Officer>, &Vec<Citizenship>, CitizenshipType)) -> Self {
|
||||||
|
let (customs, citizens, type_) = value;
|
||||||
|
let mut outputs = Vec::new();
|
||||||
|
for officer in customs {
|
||||||
|
let mut prev = 0;
|
||||||
|
for citizen in citizens {
|
||||||
|
if citizen.type_ == officer.citizenship && citizen.type_ == type_ {
|
||||||
|
let output_ = officer.time + cmp::max(citizen.id, prev);
|
||||||
|
let output = OutputTime::new(citizen.id, output_);
|
||||||
|
outputs.push(output);
|
||||||
|
prev = output_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self(outputs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Output {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let mut output_string = String::new();
|
||||||
|
for time in &self.0 {
|
||||||
|
let output_str = format!("{}\n", time);
|
||||||
|
output_string.push_str(&output_str);
|
||||||
|
}
|
||||||
|
write!(f, "{}", output_string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<String> for Output {
|
||||||
|
fn into(self) -> String {
|
||||||
|
self.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputTime {
|
||||||
|
pub fn new(input: Time, output: Time) -> Self {
|
||||||
|
Self { input, output }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for OutputTime {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{} {}", self.input, self.output)
|
||||||
|
}
|
||||||
|
}
|
||||||
86
src/process.rs
Normal file
86
src/process.rs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
citizenship::{Citizenship, CitizenshipType},
|
||||||
|
customs::Customs,
|
||||||
|
customs_info::CustomsInfo,
|
||||||
|
error::ParseError,
|
||||||
|
officer::Officer,
|
||||||
|
output::Output,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn process(input: &str) -> String {
|
||||||
|
match parse_input(input) {
|
||||||
|
Ok(data) => data.into(),
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error: {e}");
|
||||||
|
"nothing\n".into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_input(input: &str) -> Result<Output, ParseError> {
|
||||||
|
let mut lines = input.lines();
|
||||||
|
let first = match CustomsInfo::from_str(lines.next().unwrap()) {
|
||||||
|
Ok(info) => info,
|
||||||
|
Err(_) => return Err(ParseError::NoFirstLine),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut customs = Customs::new();
|
||||||
|
let mut citizens = Vec::new();
|
||||||
|
customs.citizens.reserve_exact(first.citizen.count as usize);
|
||||||
|
customs
|
||||||
|
.noncitizens
|
||||||
|
.reserve_exact(first.noncitizen.count as usize);
|
||||||
|
|
||||||
|
while let Some(line) = lines.next() {
|
||||||
|
if line.contains("X") {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
if let Ok(officer) = Officer::from_str(line) {
|
||||||
|
customs.push(officer);
|
||||||
|
}
|
||||||
|
if let Ok(citizen) = Citizenship::from_str(line) {
|
||||||
|
citizens.push(citizen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if citizens.is_empty() {
|
||||||
|
return Err(ParseError::NoCitizens);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut citizen_customs_new = Vec::new();
|
||||||
|
for customs_id in 1..=first.citizen.count {
|
||||||
|
if customs
|
||||||
|
.citizens
|
||||||
|
.iter()
|
||||||
|
.any(|x| x.id == customs_id && x.citizenship == CitizenshipType::Citizen)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let new = Officer::new(customs_id, first.citizen.time, CitizenshipType::Citizen);
|
||||||
|
citizen_customs_new.push(new);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut noncitizen_customs_new = Vec::new();
|
||||||
|
for customs_id in 1..=first.noncitizen.count {
|
||||||
|
if customs
|
||||||
|
.noncitizens
|
||||||
|
.iter()
|
||||||
|
.any(|x| x.id == customs_id && x.citizenship == CitizenshipType::NonCitizen)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let new = Officer::new(
|
||||||
|
customs_id,
|
||||||
|
first.noncitizen.time,
|
||||||
|
CitizenshipType::NonCitizen,
|
||||||
|
);
|
||||||
|
noncitizen_customs_new.push(new);
|
||||||
|
}
|
||||||
|
|
||||||
|
customs.citizens.extend(citizen_customs_new);
|
||||||
|
customs.noncitizens.extend(noncitizen_customs_new);
|
||||||
|
|
||||||
|
Ok(Output::new(customs, citizens))
|
||||||
|
}
|
||||||
2
src/utils.rs
Normal file
2
src/utils.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub type Time = u32;
|
||||||
|
pub type CustomsCount = u8;
|
||||||
47
tests/test.rs
Normal file
47
tests/test.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use customs_rs::process::process;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn customs_1() {
|
||||||
|
let input = include_str!("../data/input/customs.i1");
|
||||||
|
let output = include_str!("../data/output/customs.o1");
|
||||||
|
assert_eq!(process(input), output);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn customs_2() {
|
||||||
|
let input = include_str!("../data/input/customs.i2");
|
||||||
|
let output = include_str!("../data/output/customs.o2");
|
||||||
|
assert_eq!(process(input), output);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn customs_3() {
|
||||||
|
let input = include_str!("../data/input/customs.i3");
|
||||||
|
let output = include_str!("../data/output/customs.o3");
|
||||||
|
assert_eq!(process(input), output);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn customs_4() {
|
||||||
|
let input = include_str!("../data/input/customs.i4");
|
||||||
|
let output = include_str!("../data/output/customs.o4");
|
||||||
|
assert_eq!(process(input), output);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn customs_5() {
|
||||||
|
let input = include_str!("../data/input/customs.i5");
|
||||||
|
let output = include_str!("../data/output/customs.o5");
|
||||||
|
assert_eq!(process(input), output);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn customs_6() {
|
||||||
|
let input = include_str!("../data/input/customs.i6");
|
||||||
|
let output = include_str!("../data/output/customs.o6");
|
||||||
|
assert_eq!(process(input), output);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn customs_7() {
|
||||||
|
let input = include_str!("../data/input/customs.i7");
|
||||||
|
let output = include_str!("../data/output/customs.o7");
|
||||||
|
assert_eq!(process(input), output);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user