use std::cmp::Ordering;
use std::convert::{Infallible, TryFrom};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::str::FromStr;
use std::time::Duration;
use std::vec::IntoIter;
use getset::{CopyGetters, Getters, Setters};
use serde::{Deserialize, Serialize};
use crate::regex;
use crate::Result;
#[derive(Serialize, Deserialize, CopyGetters, Debug, Clone, PartialEq, Eq, Hash)]
pub struct Service {
#[get_copy = "pub"]
id: ServiceKind,
}
impl Service {
pub fn new(id: ServiceKind) -> Self {
Self { id }
}
}
impl Default for Service {
fn default() -> Self {
Self::new(ServiceKind::default())
}
}
#[derive(
Serialize,
Deserialize,
EnumString,
EnumVariantNames,
IntoStaticStr,
Debug,
Copy,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
)]
#[serde(rename_all = "kebab-case")]
#[strum(serialize_all = "kebab-case")]
pub enum ServiceKind {
Atcoder,
}
impl ServiceKind {
pub fn to_user_pass_env_names(self) -> (&'static str, &'static str) {
match self {
Self::Atcoder => ("ACICK_ATCODER_USERNAME", "ACICK_ATCODER_PASSWORD"),
}
}
}
impl Default for ServiceKind {
fn default() -> Self {
Self::Atcoder
}
}
impl fmt::Display for ServiceKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.into())
}
}
#[derive(Serialize, Deserialize, Getters, Debug, Clone, PartialEq, Eq, Hash)]
#[get = "pub"]
pub struct Contest {
id: ContestId,
name: String,
}
impl Contest {
pub fn new(id: impl Into<ContestId>, name: impl Into<String>) -> Self {
Self {
id: id.into(),
name: name.into(),
}
}
}
impl Default for Contest {
fn default() -> Self {
Self::new(DEFAULT_CONTEST_ID_STR, "AtCoder Regular Contest 100")
}
}
pub static DEFAULT_CONTEST_ID_STR: &str = "arc100";
#[derive(Serialize, Deserialize, Debug, Clone, Eq)]
pub struct ContestId(String);
impl ContestId {
pub fn normalize(&self) -> String {
regex!(r"[-_]").replace_all(&self.0, "").to_lowercase()
}
}
impl Default for ContestId {
fn default() -> Self {
Self::from(DEFAULT_CONTEST_ID_STR)
}
}
impl PartialEq<ContestId> for ContestId {
fn eq(&self, other: &ContestId) -> bool {
self.normalize() == other.normalize()
}
}
impl PartialOrd for ContestId {
fn partial_cmp(&self, other: &ContestId) -> Option<Ordering> {
Some(self.normalize().cmp(&other.normalize()))
}
}
impl Ord for ContestId {
fn cmp(&self, other: &Self) -> Ordering {
self.normalize().cmp(&other.normalize())
}
}
impl Hash for ContestId {
fn hash<H: Hasher>(&self, state: &mut H) {
self.normalize().hash(state);
}
}
impl<T: Into<String>> From<T> for ContestId {
fn from(id: T) -> Self {
Self(id.into())
}
}
impl FromStr for ContestId {
type Err = Infallible;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(Self::from(s))
}
}
impl AsRef<str> for ContestId {
fn as_ref(&self) -> &str {
&self.0
}
}
impl fmt::Display for ContestId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&self.0)
}
}
#[derive(
Serialize, Deserialize, Getters, CopyGetters, Setters, Debug, Clone, PartialEq, Eq, Hash,
)]
pub struct Problem {
#[get = "pub"]
id: ProblemId,
#[get = "pub"]
name: String,
#[get = "pub"]
url_name: String,
#[serde(with = "humantime_serde")]
#[get_copy = "pub"]
time_limit: Duration,
#[get_copy = "pub"]
memory_limit: Byte,
#[get_copy = "pub"]
compare: Compare,
#[set = "pub"]
samples: Vec<Sample>,
}
impl Problem {
pub fn new(
id: impl Into<ProblemId>,
name: impl Into<String>,
url_name: impl Into<String>,
time_limit: Duration,
memory_limit: Byte,
compare: Compare,
samples: Vec<Sample>,
) -> Self {
Self {
id: id.into(),
name: name.into(),
url_name: url_name.into(),
time_limit,
memory_limit,
compare,
samples,
}
}
pub fn n_samples(&self) -> usize {
self.samples.len()
}
pub fn max_sample_name_len(&self) -> usize {
self.samples
.iter()
.map(|sample| sample.name.len())
.max()
.unwrap_or(0)
}
pub fn iter_samples<'a>(
self,
sample_name: &'a Option<String>,
) -> Box<dyn Iterator<Item = Result<Sample>> + 'a> {
let iter = self.samples.into_iter();
if let Some(sample_name) = sample_name {
Box::new(
iter.filter(move |sample| sample.name() == sample_name)
.map(Ok),
)
} else {
Box::new(iter.map(Ok))
}
}
pub fn take_samples(self, sample_name: &Option<String>) -> SampleIter {
if let Some(sample_name) = sample_name {
self.samples
.into_iter()
.filter(|sample| &sample.name == sample_name)
.collect::<Vec<_>>()
.into()
} else {
self.samples.into()
}
}
}
impl Default for Problem {
fn default() -> Self {
Self::new(
"C",
"Linear Approximation",
"arc100_a",
Duration::from_secs(2),
"1024 MB".parse().unwrap(),
Compare::Default,
vec![],
)
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Eq)]
pub struct ProblemId(String);
impl ProblemId {
pub fn normalize(&self) -> String {
self.0.to_uppercase()
}
}
impl PartialEq<ProblemId> for ProblemId {
fn eq(&self, other: &ProblemId) -> bool {
self.normalize() == other.normalize()
}
}
impl PartialOrd for ProblemId {
fn partial_cmp(&self, other: &ProblemId) -> Option<Ordering> {
Some(self.normalize().cmp(&other.normalize()))
}
}
impl Ord for ProblemId {
fn cmp(&self, other: &Self) -> Ordering {
self.normalize().cmp(&other.normalize())
}
}
impl Hash for ProblemId {
fn hash<H: Hasher>(&self, state: &mut H) {
self.normalize().hash(state);
}
}
impl<T: Into<String>> From<T> for ProblemId {
fn from(id: T) -> Self {
Self(id.into())
}
}
impl FromStr for ProblemId {
type Err = Infallible;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(Self::from(s))
}
}
impl AsRef<str> for ProblemId {
fn as_ref(&self) -> &str {
&self.0
}
}
impl fmt::Display for ProblemId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&self.normalize())
}
}
#[derive(
Serialize,
Deserialize,
EnumString,
EnumVariantNames,
IntoStaticStr,
Debug,
Copy,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
)]
#[serde(rename_all = "kebab-case")]
#[strum(serialize_all = "kebab-case")]
pub enum Compare {
Default,
}
impl Compare {
pub fn compare(self, a: &str, b: &str) -> bool {
match self {
Self::Default => Self::compare_default(a, b),
}
}
fn compare_default(a: &str, b: &str) -> bool {
a.trim_end() == b.trim_end()
}
}
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[serde(try_from = "String", into = "String")]
pub struct Byte(u64);
impl FromStr for Byte {
type Err = &'static str;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(Self(bytefmt::parse(s)?))
}
}
impl TryFrom<String> for Byte {
type Error = &'static str;
fn try_from(s: String) -> std::result::Result<Self, Self::Error> {
Self::from_str(&s)
}
}
impl From<Byte> for String {
fn from(byte: Byte) -> Self {
bytefmt::format_to(byte.0, bytefmt::Unit::MB)
}
}
impl fmt::Display for Byte {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&String::from(*self))
}
}
#[derive(Serialize, Deserialize, Getters, Debug, Clone, PartialEq, Eq, Hash)]
pub struct Sample {
#[get = "pub"]
name: String,
#[get = "pub"]
input: String,
#[get = "pub"]
output: String,
}
impl Sample {
pub fn new(
name: impl Into<String>,
input: impl Into<String>,
output: impl Into<String>,
) -> Self {
Self {
name: name.into(),
input: input.into(),
output: output.into(),
}
}
pub fn take(self) -> (String, String, String) {
(self.name, self.input, self.output)
}
}
pub trait AsSamples: Iterator<Item = Result<Sample>> {
fn len(&self) -> usize;
fn max_name_len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[derive(Debug, Clone)]
pub struct SampleIter {
len: usize,
max_name_len: usize,
iter: IntoIter<Sample>,
}
impl Iterator for SampleIter {
type Item = Result<Sample>;
fn next(&mut self) -> Option<Result<Sample>> {
self.iter.next().map(Ok)
}
}
impl AsSamples for SampleIter {
fn len(&self) -> usize {
self.len
}
fn max_name_len(&self) -> usize {
self.max_name_len
}
}
impl From<Vec<Sample>> for SampleIter {
fn from(samples: Vec<Sample>) -> Self {
Self {
len: samples.len(),
max_name_len: samples.iter().map(|s| s.name.len()).max().unwrap_or(0),
iter: samples.into_iter(),
}
}
}
pub type LangId = String;
pub type LangIdRef<'a> = &'a str;
pub type LangName = String;
pub type LangNameRef<'a> = &'a str;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn contest_id_eq() {
assert_eq!(ContestId::from("arc100"), ContestId::from("arc100"));
assert_eq!(ContestId::from("ARC100"), ContestId::from("arc100"));
assert_eq!(
ContestId::from("CodeFestival2017QualA"),
ContestId::from("code-festival-2017-quala")
);
}
#[test]
fn problem_id_eq() {
assert_eq!(ProblemId::from("A"), ProblemId::from("A"));
assert_eq!(ProblemId::from("a"), ProblemId::from("A"));
}
}