///|
pub(all) enum ChangeKind {
Add
Modify
Delete
Rename
Copy
} derive(Eq, @debug.Debug)
///|
pub fn ChangeKind::to_text(self : ChangeKind) -> String {
match self {
Add => "add"
Modify => "modify"
Delete => "delete"
Rename => "rename"
Copy => "copy"
}
}
///|
pub fn ChangeKind::parse(value : String) -> ChangeKind? {
match value {
"add" => Some(Add)
"modify" => Some(Modify)
"delete" => Some(Delete)
"rename" => Some(Rename)
"copy" => Some(Copy)
_ => None
}
}
///|
pub struct Change {
kind : ChangeKind
old_path : String?
new_path : String?
additions : Int
deletions : Int
binary : Bool
} derive(Eq, @debug.Debug)
///|
pub fn Change::new(
kind : ChangeKind,
old_path : String?,
new_path : String?,
additions : Int,
deletions : Int,
binary? : Bool = false,
) -> Result[Change, Diagnostic] {
if additions < 0 || deletions < 0 {
return Err(
Diagnostic::new(
"change.lines.negative",
"change",
"line counts cannot be negative",
"non-negative additions and deletions",
additions.to_string() + "/" + deletions.to_string(),
),
)
}
if additions > 10000000 || deletions > 10000000 {
return Err(
Diagnostic::new(
"change.lines.limit",
"change",
"line count exceeds the safety limit",
"at most 10000000 per side",
additions.to_string() + "/" + deletions.to_string(),
),
)
}
let old_required = kind != Add
let new_required = kind != Delete
if old_required && old_path is None {
return Err(
Diagnostic::new(
"change.old_path.required", "change.old_path", "operation needs an old path",
"safe relative path", "missing",
),
)
}
if !old_required && old_path is Some(_) {
return Err(
Diagnostic::new(
"change.old_path.unexpected",
"change.old_path",
"add operation cannot have an old path",
"missing old path",
old_path.unwrap(),
),
)
}
if new_required && new_path is None {
return Err(
Diagnostic::new(
"change.new_path.required", "change.new_path", "operation needs a new path",
"safe relative path", "missing",
),
)
}
if !new_required && new_path is Some(_) {
return Err(
Diagnostic::new(
"change.new_path.unexpected",
"change.new_path",
"delete operation cannot have a new path",
"missing new path",
new_path.unwrap(),
),
)
}
if (kind == Modify || kind == Copy) && old_path != new_path && kind == Modify {
return Err(
Diagnostic::new(
"change.modify.path",
"change",
"modify operation must keep the same path",
old_path.unwrap_or("missing"),
new_path.unwrap_or("missing"),
),
)
}
if kind == Rename && old_path == new_path {
return Err(
Diagnostic::new(
"change.rename.same_path",
"change",
"rename operation must change path",
"different old and new paths",
old_path.unwrap_or("missing"),
),
)
}
match old_path {
Some(value) =>
if !is_safe_repo_path(value) {
return Err(
Diagnostic::new(
"change.path.unsafe", "change.old_path", "path is not a normalized repository-relative path",
"relative forward-slash path without dot segments", value,
),
)
}
None => ()
}
match new_path {
Some(value) =>
if !is_safe_repo_path(value) {
return Err(
Diagnostic::new(
"change.path.unsafe", "change.new_path", "path is not a normalized repository-relative path",
"relative forward-slash path without dot segments", value,
),
)
}
None => ()
}
Ok({ kind, old_path, new_path, additions, deletions, binary, })
}
///|
pub fn Change::kind(self : Change) -> ChangeKind {
self.kind
}
///|
pub fn Change::old_path(self : Change) -> String? {
self.old_path
}
///|
pub fn Change::new_path(self : Change) -> String? {
self.new_path
}
///|
pub fn Change::additions(self : Change) -> Int {
self.additions
}
///|
pub fn Change::deletions(self : Change) -> Int {
self.deletions
}
///|
pub fn Change::is_binary(self : Change) -> Bool {
self.binary
}
///|
pub fn Change::changed_lines(self : Change) -> Int {
self.additions + self.deletions
}
///|
pub fn Change::policy_path(self : Change) -> String {
match self.new_path {
Some(path) => path
None => self.old_path.unwrap()
}
}
///|
pub(all) enum CheckState {
Passed
Failed
Pending
} derive(Eq, @debug.Debug)
///|
pub fn CheckState::to_text(self : CheckState) -> String {
match self {
Passed => "passed"
Failed => "failed"
Pending => "pending"
}
}
///|
pub fn CheckState::parse(value : String) -> CheckState? {
match value {
"passed" => Some(Passed)
"failed" => Some(Failed)
"pending" => Some(Pending)
_ => None
}
}
///|
pub struct CheckResult {
name : String
state : CheckState
} derive(Eq, @debug.Debug)
///|
pub fn CheckResult::new(name : String, state : CheckState) -> CheckResult {
{ name, state, }
}
///|
pub fn CheckResult::name(self : CheckResult) -> String {
self.name
}
///|
pub fn CheckResult::state(self : CheckResult) -> CheckState {
self.state
}
///|
pub struct Evidence {
actor : String
approvals : Array[String]
checks : Array[CheckResult]
labels : Array[String]
release_note : Bool
} derive(Eq, @debug.Debug)
///|
pub fn Evidence::new(
actor : String,
approvals : Array[String],
checks : Array[CheckResult],
labels : Array[String],
release_note? : Bool = false,
) -> Evidence {
{
actor,
approvals: approvals.copy(),
checks: checks.copy(),
labels: labels.copy(),
release_note,
}
}
///|
pub fn Evidence::actor(self : Evidence) -> String {
self.actor
}
///|
pub fn Evidence::approvals(self : Evidence) -> Array[String] {
self.approvals.copy()
}
///|
pub fn Evidence::checks(self : Evidence) -> Array[CheckResult] {
self.checks.copy()
}
///|
pub fn Evidence::labels(self : Evidence) -> Array[String] {
self.labels.copy()
}
///|
pub fn Evidence::has_release_note(self : Evidence) -> Bool {
self.release_note
}
///|
pub struct ChangeSet {
id : String
changes : Array[Change]
evidence : Evidence
} derive(Eq, @debug.Debug)
///|
pub fn ChangeSet::new(
id : String,
changes : Array[Change],
evidence : Evidence,
) -> Result[ChangeSet, Diagnostic] {
if !is_identifier(id) {
return Err(
Diagnostic::new(
"changeset.id.invalid", "changeset.id", "change-set identifier is invalid",
"1-64 letters, digits, dot, underscore, or hyphen", id,
),
)
}
if changes.is_empty() {
return Err(
Diagnostic::new(
"changeset.empty", "changeset.changes", "change set contains no changes",
"at least one change", "none",
),
)
}
if changes.length() > 10000 {
return Err(
Diagnostic::new(
"changeset.limit",
"changeset.changes",
"change set exceeds the safety limit",
"at most 10000 changes",
changes.length().to_string(),
),
)
}
Ok({ id, changes: changes.copy(), evidence, })
}
///|
pub fn ChangeSet::id(self : ChangeSet) -> String {
self.id
}
///|
pub fn ChangeSet::changes(self : ChangeSet) -> Array[Change] {
self.changes.copy()
}
///|
pub fn ChangeSet::evidence(self : ChangeSet) -> Evidence {
self.evidence
}