--- a/src/Pure/Admin/afp.scala Sun Mar 24 20:31:53 2019 +0100
+++ b/src/Pure/Admin/afp.scala Mon Mar 25 14:19:26 2019 +0100
@@ -21,7 +21,66 @@
def init(options: Options, base_dir: Path = Path.explode("$AFP_BASE")): AFP =
new AFP(options, base_dir)
- sealed case class Entry(name: String, sessions: List[String])
+
+ /* metadata */
+
+ object Metadata
+ {
+ private val Section = """^\[(\S+)\]\s*$""".r
+ private val Property = """^(\S+)\s*=(.*)$""".r
+ private val Extra_Line = """^\s+(.*)$""".r
+ private val Blank_Line = """^\s*$""".r
+
+ def read(metadata_file: Path): Map[String, Metadata] =
+ {
+ var result = Map.empty[String, Metadata]
+ var section = ""
+ var props = List.empty[Properties.Entry]
+
+ def flush()
+ {
+ if (section != "") result += (section -> new Metadata(props.reverse))
+ section = ""
+ props = Nil
+ }
+
+ for ((line, i) <- split_lines(File.read(metadata_file)).zipWithIndex)
+ {
+ def err(msg: String): Nothing =
+ error(msg + Position.here(Position.Line_File(i + 1, metadata_file.expand.implode)))
+
+ line match {
+ case Section(name) => flush(); section = name
+ case Property(a, b) =>
+ if (section == "") err("Property without a section")
+ props = (a -> b.trim) :: props
+ case Extra_Line(line) =>
+ props match {
+ case Nil => err("Extra line without a property")
+ case (a, b) :: rest => props = (a, b + "\n" + line.trim) :: rest
+ }
+ case Blank_Line() =>
+ case _ => err("Bad input")
+ }
+ }
+
+ flush()
+ result
+ }
+ }
+
+ class Metadata private[AFP](properties: Properties.T)
+ {
+ override def toString: String =
+ properties.map({ case (a, b) => a + "=" + b }).mkString("Metadata(", ", ", ")")
+
+ def is_empty: Boolean = properties.isEmpty
+ }
+
+
+ /* entries */
+
+ sealed case class Entry(name: String, metadata: Metadata, sessions: List[String])
}
class AFP private(options: Options, val base_dir: Path)
@@ -31,14 +90,28 @@
val main_dir: Path = base_dir + Path.explode("thys")
- /* entries and sessions */
+ /* entries with metadata and sessions */
val entries: List[AFP.Entry] =
- (for (name <- Sessions.parse_roots(main_dir + Sessions.ROOTS)) yield {
- val sessions =
- Sessions.parse_root_entries(main_dir + Path.explode(name) + Sessions.ROOT).map(_.name)
- AFP.Entry(name, sessions)
- }).sortBy(_.name)
+ {
+ val entry_metadata = AFP.Metadata.read(base_dir + Path.explode("metadata/metadata"))
+
+ val entries =
+ (for (name <- Sessions.parse_roots(main_dir + Sessions.ROOTS)) yield {
+ val metadata =
+ entry_metadata.getOrElse(name, error("Entry without metadata: " + quote(name)))
+ val sessions =
+ Sessions.parse_root_entries(main_dir + Path.explode(name) + Sessions.ROOT).map(_.name)
+ AFP.Entry(name, metadata, sessions)
+ }).sortBy(_.name)
+
+ val entry_set = entries.iterator.map(_.name).toSet
+ val extra_metadata =
+ (for ((name, _) <- entry_metadata.iterator if !entry_set(name)) yield name).toList.sorted
+ if (extra_metadata.nonEmpty) error("Meta data without entry: " + commas_quote(extra_metadata))
+
+ entries
+ }
val sessions: List[String] = entries.flatMap(_.sessions)