123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166(********************************************************************************)(* crs - A tool for managing code review comments embedded in source code *)(* Copyright (C) 2024-2025 Mathieu Barbin <mathieu.barbin@gmail.com> *)(* *)(* This file is part of crs. *)(* *)(* crs is free software; you can redistribute it and/or modify it under the *)(* terms of the GNU Lesser General Public License as published by the Free *)(* Software Foundation either version 3 of the License, or any later version, *)(* with the LGPL-3.0 Linking Exception. *)(* *)(* crs is distributed in the hope that it will be useful, but WITHOUT ANY *)(* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS *)(* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License and *)(* the file `NOTICE.md` at the root of this repository for more details. *)(* *)(* You should have received a copy of the GNU Lesser General Public License *)(* and the LGPL-3.0 Linking Exception along with this library. If not, see *)(* <http://www.gnu.org/licenses/> and <https://spdx.org>, respectively. *)(********************************************************************************)(* This module is derived from Iron (v0.9.114.44+47), file
* [./hg/cr_comment.ml], which is released under Apache 2.0:
*
* Copyright (c) 2016-2017 Jane Street Group, LLC <opensource-contacts@janestreet.com>
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* See the file `NOTICE.md` at the root of this repository for more details.
*
* Changes:
*
* - Migrate to this file only the part that relates to the parsing of the 1st line.
* - Remove dependency to [Core] - make small adjustments to use [Base] instead.
* - Replace [Unresolved_name] by [Vcs.User_handle].
* - Remove alternate names and aliases resolution.
* - Remove support for in-file `Properties.
* - Remove support for extra headers.
* - Remove support for attributes.
* - Remove assignee computation (left as external work).
* - Replace [is_xcr] by a variant type [Status.t].
* - Make [reporter] mandatory.
* - Rename [Processed] to [Header].
* - Remove support for 'v' separator in CR comment
* - Include the leading '-' char in due's [Loc.t].
* - Migrate from [Re2] to [ocaml-re].
*)(* : and @ have other meanings in CR comments *)letword_t=Re.compl[Re.char' ';Re.char'\t';Re.char'\n';Re.char':';Re.char'@'];;letwhitespace=Re.alt[Re.char' ';Re.char'\n';Re.char'\t']letcomment_regex=lazy(Re.(whole_string(seq[repwhitespace;group~name:"status"(seq[opt(char'X');str"CR"]);opt(seq[char'-';group~name:"qualifier"(alt[repndigit6(Some6);str"soon";str"someday"])]);rep1whitespace;group~name:"reporter"(rep1word_t);opt(seq[rep1whitespace;str"for";rep1whitespace;group~name:"recipient"(rep1word_t)]);repwhitespace;char':';repany]))|>Re.compile);;letparse~file_cache~content_start_offset~content=let(let*)af=Or_error.binda~fintryletcomment_regex=Lazy.forcecomment_regexinletgroup_names=Re.group_namescomment_regexinlet*m=matchRe.exec_optcomment_regexcontentwith|None->Or_error.error"Invalid CR comment"contentString.sexp_of_t|Somem->Or_error.returnminletgetfield_name=letindex=matchList.findgroup_names~f:(fun(name,_)->String.equalfield_namename)with|Some(_,index)->index|None->failwith(Printf.sprintf"Invalid regexp group name %S"field_name)[@coverageoff]inmatchRe.Group.get_optmindexwith|None->None|Somev->letstart,stop=Re.Group.offsetmindexinletloc=Loc.of_file_range~file_cache~range:{start=content_start_offset+start;stop=content_start_offset+stop}inSome(v,loc)inletreporter=matchget"reporter"with|None->assertfalse(* Mandatory in the [comment_regexp]. *)|Some(reporter,loc)->{Loc.Txt.txt=Vcs.User_handle.vreporter;loc}inletstatus=matchget"status"with|None->assertfalse(* Mandatory in the [comment_regexp]. *)|Some(status,loc)->lettxt:Cr_comment.Status.t=matchstatuswith|"CR"->CR|"XCR"->XCR|_->assertfalse(* Cannot be parsed according to the regexp. *)in{Loc.Txt.txt;loc}inletrecipient=Option.map(get"recipient")~f:(fun(user,loc)->{Loc.Txt.txt=Vcs.User_handle.vuser;loc})inletqualifier=matchget"qualifier"with|None->{Loc.Txt.txt=Cr_comment.Qualifier.None;loc=status.loc}|Some("soon",loc)->{Loc.Txt.txt=Cr_comment.Qualifier.Soon;loc}|Some("someday",loc)->{Loc.Txt.txt=Cr_comment.Qualifier.Someday;loc}|Some(_,loc)->(* dated CR -> CR-someday *){Loc.Txt.txt=Cr_comment.Qualifier.Someday;loc}inOr_error.return(Cr_comment.Private.Header.create~status~qualifier~reporter~recipient)with|exn->Or_error.error"could not process CR"(content,exn)[%sexp_of:string*exn][@coverageoff];;