Skip to content

Commit aac4c60

Browse files
committed
fragment-selectors: split xpathOf (bare XPath) from xpointerOf (#xpointer)
xpathOf now returns the bare absolute XPath (`/PROJECT/NAME`); the new xpointerOf wraps the same path as a W3C `#xpointer(...)` fragment. Added xpointerOf alongside xpathOf on all eight IO entity modules, and updated the tests and the Phase 2 plan section to match.
1 parent 2f03ca2 commit aac4c60

11 files changed

Lines changed: 85 additions & 36 deletions

File tree

‎plans/xml-fragment-selectors.md‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,14 @@ model. Phase 2 adds the runtime counterpart — given a value returned by `*.rea
138138
```fsharp
139139
let project = BioProject.read "PRJDB5192.xml" |> Seq.head
140140
project |> BioProject.xpathOf <@ fun b -> b.Name @>
141-
// -> "#xpointer(/PROJECT/NAME)"
142-
project |> BioProject.xpathOf <@ fun b -> b.Collaborators.[1].Name @>
141+
// -> "/PROJECT/NAME"
142+
project |> BioProject.xpointerOf <@ fun b -> b.Collaborators.[1].Name @>
143143
// -> "#xpointer(/PROJECT/COLLABORATORS/COLLABORATOR[2]/NAME)"
144144
```
145145

146+
Each entity exposes two functions: **`xpathOf`** returns the bare absolute XPath; **`xpointerOf`**
147+
wraps the same path as a W3C `#xpointer(...)` fragment.
148+
146149
Where the Phase-1 maps describe where a property lives *structurally* (a `COLLABORATOR` node-set),
147150
Phase 2 returns the XPath of *this value in this document*: real array indices, only the nodes
148151
actually present, and xscgen choice groups resolved to the populated branch.
@@ -184,10 +187,10 @@ obsolete.
184187
Reflects over the FileFormats model types (the IO project already references FileFormats). It
185188
re-implements the attribute→step logic from `FragmentSelectorTasks.fs` (which is build-only); the
186189
duplication is factorable later but not worth blocking on.
187-
5. **Per-entity `xpathOf` — the `BioProject`/`Study`/`BioSample`/`Experiment`/`Run`/`Analysis`/
188-
`Submission`/`Receipt` modules.** One-line wrappers over the generic core so `BioProject.xpathOf`
189-
reads as in the example. `read`/`readString`/`write` are untouched — the IO read/write surface is
190-
unchanged.
190+
5. **Per-entity `xpathOf` / `xpointerOf` — the `BioProject`/`Study`/`BioSample`/`Experiment`/`Run`/
191+
`Analysis`/`Submission`/`Receipt` modules.** One-line wrappers over the generic core so
192+
`BioProject.xpathOf` / `BioProject.xpointerOf` read as in the example. `read`/`readString`/`write`
193+
are untouched — the IO read/write surface is unchanged.
191194

192195
> **Scope note.** Phase 2 adds files to the **IO layer**, which Phase 1 excluded ("No IO-layer
193196
> changes"). It is the promotion of the Phase-1 "evaluate selector" follow-up into scope — but it stays

‎src/BioFSharp.IO.INSDC/Analysis.fs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@ module Analysis =
2222
let writeString (analysis: Analysis) : string =
2323
XmlSerializer.writeString analysis
2424

25-
/// Resolve the position-qualified W3C XPointer fragment selector for a property of a parsed
26-
/// `analysis`. Name the property with a quotation, addressing array positions with `.[i]`:
25+
/// Resolve the absolute XPath of a property of a parsed `analysis`. Name the property with a
26+
/// quotation, addressing array positions with `.[i]`:
2727
/// `analysis |> Analysis.xpathOf <@ fun a -> a.Files.[0].Filename @>`.
2828
let xpathOf selector (analysis: Analysis) : string =
2929
XPathTracking.xpathOf selector analysis
30+
31+
/// As `xpathOf`, but wrapped as a W3C XPointer fragment selector (`#xpointer(...)`).
32+
let xpointerOf selector (analysis: Analysis) : string =
33+
XPathTracking.xpointerOf selector analysis

‎src/BioFSharp.IO.INSDC/BioProject.fs‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,13 @@ module BioProject =
2424
let writeString (project: BioProject) : string =
2525
XmlSerializer.writeString project
2626

27-
/// Resolve the position-qualified W3C XPointer fragment selector for a property of a parsed
28-
/// `project`. Name the property with a quotation, addressing array positions with `.[i]`:
29-
/// `project |> BioProject.xpathOf <@ fun p -> p.Name @>` -> `#xpointer(/PROJECT/NAME)`.
27+
/// Resolve the absolute XPath of a property of a parsed `project`. Name the property with a
28+
/// quotation, addressing array positions with `.[i]`:
29+
/// `project |> BioProject.xpathOf <@ fun p -> p.Name @>` -> `/PROJECT/NAME`.
3030
let xpathOf selector (project: BioProject) : string =
3131
XPathTracking.xpathOf selector project
32+
33+
/// As `xpathOf`, but wrapped as a W3C XPointer fragment selector:
34+
/// `project |> BioProject.xpointerOf <@ fun p -> p.Name @>` -> `#xpointer(/PROJECT/NAME)`.
35+
let xpointerOf selector (project: BioProject) : string =
36+
XPathTracking.xpointerOf selector project

‎src/BioFSharp.IO.INSDC/BioSample.fs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,12 @@ module BioSample =
2424
let writeString (sample: BioSample) : string =
2525
XmlSerializer.writeString sample
2626

27-
/// Resolve the position-qualified W3C XPointer fragment selector for a property of a parsed
28-
/// `sample`. Name the property with a quotation, addressing array positions with `.[i]`:
27+
/// Resolve the absolute XPath of a property of a parsed `sample`. Name the property with a
28+
/// quotation, addressing array positions with `.[i]`:
2929
/// `sample |> BioSample.xpathOf <@ fun s -> s.SampleName.ScientificName @>`.
3030
let xpathOf selector (sample: BioSample) : string =
3131
XPathTracking.xpathOf selector sample
32+
33+
/// As `xpathOf`, but wrapped as a W3C XPointer fragment selector (`#xpointer(...)`).
34+
let xpointerOf selector (sample: BioSample) : string =
35+
XPathTracking.xpointerOf selector sample

‎src/BioFSharp.IO.INSDC/Experiment.fs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@ module Experiment =
2222
let writeString (experiment: Experiment) : string =
2323
XmlSerializer.writeString experiment
2424

25-
/// Resolve the position-qualified W3C XPointer fragment selector for a property of a parsed
26-
/// `experiment`. Name the property with a quotation, addressing array positions with `.[i]`:
25+
/// Resolve the absolute XPath of a property of a parsed `experiment`. Name the property with a
26+
/// quotation, addressing array positions with `.[i]`:
2727
/// `experiment |> Experiment.xpathOf <@ fun e -> e.StudyRef.Accession @>`.
2828
let xpathOf selector (experiment: Experiment) : string =
2929
XPathTracking.xpathOf selector experiment
30+
31+
/// As `xpathOf`, but wrapped as a W3C XPointer fragment selector (`#xpointer(...)`).
32+
let xpointerOf selector (experiment: Experiment) : string =
33+
XPathTracking.xpointerOf selector experiment

‎src/BioFSharp.IO.INSDC/Internal/XPathTracking.fs‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ open System.Xml.Serialization
99

1010
open Microsoft.FSharp.Quotations
1111

12-
/// Resolves the concrete, position-qualified W3C XPointer of a property on a parsed INSDC value.
13-
/// Public entity modules expose this as `<Entity>.xpathOf`. The property is named with a quotation
12+
/// Resolves the concrete, position-qualified XPath of a property on a parsed INSDC value. Public
13+
/// entity modules expose this as `<Entity>.xpathOf` (the bare XPath) and `<Entity>.xpointerOf` (the
14+
/// same path wrapped as a W3C `#xpointer(...)` fragment). The property is named with a quotation
1415
/// (`<@ fun b -> b.Name @>`) rather than piped by value, because a scalar value carries no
1516
/// back-reference to its property, owner, or array position. The XPath is derived from the same
1617
/// `System.Xml.Serialization` attributes the serializer uses, with array positions taken from the
@@ -119,11 +120,15 @@ module XPathTracking =
119120
current <- value
120121
sb.ToString()
121122

122-
/// Resolve the `#xpointer(...)` fragment selector for the property named by `selector` on the
123-
/// parsed `root` value, with real array positions taken from the indices in the quotation.
123+
/// The bare absolute XPath for the property named by `selector` on the parsed `root` value
124+
/// (`/PROJECT/NAME`), with real array positions taken from the indices in the quotation.
124125
let xpathOf (selector: Expr<'Root -> 'P>) (root: 'Root) : string =
125126
let steps =
126127
match (selector :> Expr) with
127128
| Patterns.Lambda(_, body) -> parseChain body
128129
| _ -> failwith "Expected a selector lambda, e.g. <@ fun x -> x.Prop @>."
129-
"#xpointer(" + build typeof<'Root> steps (box root) + ")"
130+
build typeof<'Root> steps (box root)
131+
132+
/// The same selector as `xpathOf`, wrapped as a W3C XPointer fragment (`#xpointer(/PROJECT/NAME)`).
133+
let xpointerOf (selector: Expr<'Root -> 'P>) (root: 'Root) : string =
134+
"#xpointer(" + xpathOf selector root + ")"

‎src/BioFSharp.IO.INSDC/Receipt.fs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@ module Receipt =
2323
let writeString (receipt: Receipt) : string =
2424
XmlSerializer.writeString receipt
2525

26-
/// Resolve the position-qualified W3C XPointer fragment selector for a property of a parsed
27-
/// `receipt`. Name the property with a quotation, addressing array positions with `.[i]`:
26+
/// Resolve the absolute XPath of a property of a parsed `receipt`. Name the property with a
27+
/// quotation, addressing array positions with `.[i]`:
2828
/// `receipt |> Receipt.xpathOf <@ fun r -> r.Submission.Accession @>`.
2929
let xpathOf selector (receipt: Receipt) : string =
3030
XPathTracking.xpathOf selector receipt
31+
32+
/// As `xpathOf`, but wrapped as a W3C XPointer fragment selector (`#xpointer(...)`).
33+
let xpointerOf selector (receipt: Receipt) : string =
34+
XPathTracking.xpointerOf selector receipt

‎src/BioFSharp.IO.INSDC/Run.fs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@ module Run =
2222
let writeString (run: Run) : string =
2323
XmlSerializer.writeString run
2424

25-
/// Resolve the position-qualified W3C XPointer fragment selector for a property of a parsed
26-
/// `run`. Name the property with a quotation, addressing array positions with `.[i]`:
25+
/// Resolve the absolute XPath of a property of a parsed `run`. Name the property with a
26+
/// quotation, addressing array positions with `.[i]`:
2727
/// `run |> Run.xpathOf <@ fun r -> r.ExperimentRef.Accession @>`.
2828
let xpathOf selector (run: Run) : string =
2929
XPathTracking.xpathOf selector run
30+
31+
/// As `xpathOf`, but wrapped as a W3C XPointer fragment selector (`#xpointer(...)`).
32+
let xpointerOf selector (run: Run) : string =
33+
XPathTracking.xpointerOf selector run

‎src/BioFSharp.IO.INSDC/Study.fs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@ module Study =
2222
let writeString (study: Study) : string =
2323
XmlSerializer.writeString study
2424

25-
/// Resolve the position-qualified W3C XPointer fragment selector for a property of a parsed
26-
/// `study`. Name the property with a quotation, addressing array positions with `.[i]`:
25+
/// Resolve the absolute XPath of a property of a parsed `study`. Name the property with a
26+
/// quotation, addressing array positions with `.[i]`:
2727
/// `study |> Study.xpathOf <@ fun s -> s.Descriptor.StudyTitle @>`.
2828
let xpathOf selector (study: Study) : string =
2929
XPathTracking.xpathOf selector study
30+
31+
/// As `xpathOf`, but wrapped as a W3C XPointer fragment selector (`#xpointer(...)`).
32+
let xpointerOf selector (study: Study) : string =
33+
XPathTracking.xpointerOf selector study

‎src/BioFSharp.IO.INSDC/Submission.fs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@ module Submission =
2222
let writeString (submission: Submission) : string =
2323
XmlSerializer.writeString submission
2424

25-
/// Resolve the position-qualified W3C XPointer fragment selector for a property of a parsed
26-
/// `submission`. Name the property with a quotation, addressing array positions with `.[i]`:
25+
/// Resolve the absolute XPath of a property of a parsed `submission`. Name the property with a
26+
/// quotation, addressing array positions with `.[i]`:
2727
/// `submission |> Submission.xpathOf <@ fun s -> s.SubmissionLinks.[0].XrefLink.Db @>`.
2828
let xpathOf selector (submission: Submission) : string =
2929
XPathTracking.xpathOf selector submission
30+
31+
/// As `xpathOf`, but wrapped as a W3C XPointer fragment selector (`#xpointer(...)`).
32+
let xpointerOf selector (submission: Submission) : string =
33+
XPathTracking.xpointerOf selector submission

0 commit comments

Comments
 (0)