# SPDX-FileCopyrightText: 2024 Carmen Bianca BAKKER <carmenbianca@fsfe.org>## SPDX-License-Identifier: GPL-3.0-or-later"""Logic to convert a .reuse/dep5 file to a REUSE.toml file."""importreimportsysfromargparseimportArgumentParser,Namespacefromgettextimportgettextas_fromtypingimportIO,Any,Dict,Iterable,List,Optional,TypeVar,Union,castimporttomlkitfromdebian.copyrightimportCopyright,FilesParagraph,Headerfrom.global_licensingimportREUSE_TOML_VERSION,ReuseDep5from.projectimportProject_SINGLE_ASTERISK_PATTERN=re.compile(r"(?<!\*)\*(?!\*)")_T=TypeVar("_T")def_collapse_list_if_one_item(# Technically this should be Sequence[_T], but I can't get that to work.sequence:List[_T],)->Union[List[_T],_T]:"""Return the only item of the list if the length of the list is one, else return the list. """iflen(sequence)==1:returnsequence[0]returnsequencedef_header_from_dep5_header(header:Header,)->Dict[str,Union[str,List[str]]]:result:Dict[str,Union[str,List[str]]]={}ifheader.upstream_name:result["SPDX-PackageName"]=str(header.upstream_name)ifheader.upstream_contact:result["SPDX-PackageSupplier"]=_collapse_list_if_one_item(list(map(str,header.upstream_contact)))ifheader.source:result["SPDX-PackageDownloadLocation"]=str(header.source)ifheader.disclaimer:result["SPDX-PackageComment"]=str(header.disclaimer)returnresultdef_copyrights_from_paragraph(paragraph:FilesParagraph,)->Union[str,List[str]]:return_collapse_list_if_one_item([line.strip()forlineincast(str,paragraph.copyright).splitlines()])def_convert_asterisk(path:str)->str:"""This solves a semantics difference. A singular asterisk is semantically identical to a double asterisk in REUSE.toml. """return_SINGLE_ASTERISK_PATTERN.sub("**",path)def_paths_from_paragraph(paragraph:FilesParagraph)->Union[str,List[str]]:return_collapse_list_if_one_item([_convert_asterisk(path)forpathinlist(paragraph.files)])def_comment_from_paragraph(paragraph:FilesParagraph)->Optional[str]:returncast(Optional[str],paragraph.comment)def_annotations_from_paragraphs(paragraphs:Iterable[FilesParagraph],)->List[Dict[str,Union[str,List[str]]]]:annotations=[]forparagraphinparagraphs:copyrights=_copyrights_from_paragraph(paragraph)paths=_paths_from_paragraph(paragraph)paragraph_result={"path":paths,"precedence":"aggregate","SPDX-FileCopyrightText":copyrights,"SPDX-License-Identifier":paragraph.license.to_str(),}comment=_comment_from_paragraph(paragraph)ifcomment:paragraph_result["SPDX-FileComment"]=commentannotations.append(paragraph_result)returnannotations
[docs]deftoml_from_dep5(dep5:Copyright)->str:"""Given a Copyright object, return an equivalent REUSE.toml string."""header=_header_from_dep5_header(dep5.header)annotations=_annotations_from_paragraphs(dep5.all_files_paragraphs())result:Dict[str,Any]={"version":REUSE_TOML_VERSION}result.update(header)result["annotations"]=annotationsreturntomlkit.dumps(result)
# pylint: disable=unused-argument
[docs]defadd_arguments(parser:ArgumentParser)->None:"""Add arguments to parser."""
# Nothing to do.# pylint: disable=unused-argument
[docs]defrun(args:Namespace,project:Project,out:IO[str]=sys.stdout)->int:"""Convert .reuse/dep5 to REUSE.toml."""ifnot(project.root/".reuse/dep5").exists():args.parser.error(_("no '.reuse/dep5' file"))text=toml_from_dep5(cast(ReuseDep5,project.global_licensing).dep5_copyright)(project.root/"REUSE.toml").write_text(text)(project.root/".reuse/dep5").unlink()return0