///|
fn contained_or(reason : String, condition : Bool) -> Containment {
  if condition {
    Containment::Contained
  } else {
    Containment::NotContained(reason~)
  }
}

///|
pub fn effect_contains(
  grant : EffectScope,
  requested : EffectScope,
) -> Containment {
  match (grant, requested) {
    (FileRead(grant), FileRead(requested)) =>
      contained_or(
        "file read path is outside the grant",
        path_contains(grant, requested),
      )
    (FileWrite(grant), FileWrite(requested)) =>
      contained_or(
        "file write path is outside the grant",
        path_contains(grant, requested),
      )
    (FileDelete(grant), FileDelete(requested)) =>
      contained_or(
        "file delete path is outside the grant",
        path_contains(grant, requested),
      )
    (ProcessExec(grant), ProcessExec(requested)) =>
      contained_or(
        "process executable or arguments exceed the grant",
        command_contains(grant, requested),
      )
    (NetworkSend(grant), NetworkSend(requested)) =>
      contained_or(
        "network host, method, or data class exceeds the grant",
        network_contains(grant, requested),
      )
    (SecretRead(grant), SecretRead(requested)) =>
      contained_or(
        "secret identifier is not granted",
        grant != "" && requested != "" && grant == requested,
      )
    _ => Containment::NotContained(reason="effect kinds differ")
  }
}

///|
pub fn EffectScope::canonical(self : EffectScope) -> String {
  match self {
    FileRead(path) => "fs.read:" + path.canonical()
    FileWrite(path) => "fs.write:" + path.canonical()
    FileDelete(path) => "fs.delete:" + path.canonical()
    ProcessExec(command) => "process.exec:" + command.canonical()
    NetworkSend(network) => "network.send:" + network.canonical()
    SecretRead(name) =>
      if name is "" {
        "secret.read:"
      } else {
        "secret.read:" + name
      }
  }
}