frindlymap/lib/modules/work/src/marker.dart

79 lines
2.7 KiB
Dart

/// 대화 글자에 붙이는 업무 표시. 새 봉투 형식 없이 보통 대화로 오가고, 받는 쪽이 읽어서 업무 목록에 넣는다.
///
/// 📌 업무 [김민수] 결제 API 정리 (마감 10/30)
/// 🚨 긴급 호출: 서버가 멈췄어요
sealed class WorkNote {
const WorkNote();
/// 대화 글자로.
String encode();
/// 대화 글자에서 읽는다. 표시가 없으면 null. [now] 는 마감 연도를 정할 때 쓴다.
static WorkNote? parse(String body, {DateTime? now}) {
final t = body.trim();
if (t.startsWith(UrgentNote.prefix)) {
final text = t.substring(UrgentNote.prefix.length).trim();
return UrgentNote(text);
}
if (t.startsWith(TaskNote.prefix)) return TaskNote._parse(t.substring(TaskNote.prefix.length).trim(), now: now);
return null;
}
}
/// 긴급 호출.
class UrgentNote extends WorkNote {
const UrgentNote(this.text);
static const prefix = '🚨 긴급 호출:';
final String text;
@override
String encode() => text.isEmpty ? prefix : '$prefix $text';
}
/// 업무 맡기기. [assignee] 는 이름(받는 쪽 기기에서 사람을 찾는 데 쓴다), [due] 는 마감 날짜.
class TaskNote extends WorkNote {
const TaskNote(this.title, {this.assignee, this.due});
static const prefix = '📌 업무';
final String title;
final String? assignee;
final DateTime? due;
@override
String encode() {
final b = StringBuffer(prefix);
if (assignee != null && assignee!.trim().isNotEmpty) b.write(' [${assignee!.trim().replaceAll(']', '')}]');
b.write(' ${title.trim()}');
if (due != null) b.write(' (마감 ${due!.month}/${due!.day})');
return b.toString();
}
static final _who = RegExp(r'^\[([^\]]*)\]\s*');
static final _due = RegExp(r'\s*\(마감 (\d{1,2})/(\d{1,2})\)\s*$');
static TaskNote? _parse(String rest, {DateTime? now}) {
var s = rest;
String? who;
final w = _who.firstMatch(s);
if (w != null) {
who = w.group(1)!.trim();
s = s.substring(w.end);
}
DateTime? due;
final d = _due.firstMatch(s);
if (d != null) {
final m = int.parse(d.group(1)!), day = int.parse(d.group(2)!);
if (m >= 1 && m <= 12 && day >= 1 && day <= 31) due = dueFrom(m, day, now ?? DateTime.now());
s = s.substring(0, d.start);
}
s = s.trim();
if (s.isEmpty) return null;
return TaskNote(s, assignee: who == null || who.isEmpty ? null : who, due: due);
}
/// "10/30" 은 [now] 기준 가장 가까운 앞날 (지난 지 두 달이 넘었으면 내년).
static DateTime dueFrom(int month, int day, DateTime now) {
final thisYear = DateTime(now.year, month, day);
return now.difference(thisYear).inDays > 60 ? DateTime(now.year + 1, month, day) : thisYear;
}
}