Coverage for src/somesy/rust/models.py: 85%
62 statements
« prev ^ index » next coverage.py v7.6.0, created at 2024-07-29 07:42 +0000
« prev ^ index » next coverage.py v7.6.0, created at 2024-07-29 07:42 +0000
1"""Pyproject models."""
3import re
4from pathlib import Path
5from typing import Dict, List, Optional, Set, Union
7from packaging.version import parse as parse_version
8from pydantic import BaseModel, Field, field_validator, model_validator
9from typing_extensions import Annotated
11from somesy.core.types import HttpUrlStr
14class RustConfig(BaseModel):
15 """Rust configuration model."""
17 model_config = dict(use_enum_values=True)
19 name: Annotated[
20 str,
21 Field(
22 pattern=r"^[A-Za-z0-9]+([_-][A-Za-z0-9]+)*$",
23 max_length=64,
24 description="Package name",
25 ),
26 ]
27 version: Annotated[
28 str,
29 Field(
30 pattern=r"^\d+(\.\d+)*((a|b|rc)\d+)?(post\d+)?(dev\d+)?$",
31 description="Package version",
32 ),
33 ]
34 description: Annotated[Optional[str], Field(description="Package description")] = (
35 None
36 )
37 license: Annotated[
38 Optional[str],
39 Field(
40 description="A combination SPDX license identifiers with AND, OR and so on."
41 ),
42 ] = None
43 authors: Annotated[Set[str], Field(description="Package authors")]
44 maintainers: Annotated[
45 Optional[Set[str]], Field(description="Package maintainers")
46 ] = None
47 readme: Annotated[
48 Optional[Union[Path, List[Path]]], Field(description="Package readme file(s)")
49 ] = None
50 license_file: Annotated[
51 Optional[Path], Field(description="Package license file")
52 ] = None
53 homepage: Annotated[Optional[HttpUrlStr], Field(description="Package homepage")] = (
54 None
55 )
56 repository: Annotated[
57 Optional[HttpUrlStr], Field(description="Package repository")
58 ] = None
59 documentation: Annotated[
60 Optional[HttpUrlStr], Field(description="Package documentation page")
61 ] = None
62 keywords: Annotated[
63 Optional[Set[str]], Field(description="Keywords that describe the package")
64 ] = None
65 classifiers: Annotated[
66 Optional[List[str]], Field(description="pypi classifiers")
67 ] = None
68 urls: Annotated[
69 Optional[Dict[str, HttpUrlStr]], Field(description="Package URLs")
70 ] = None
72 @model_validator(mode="before")
73 @classmethod
74 def license_or_file(cls, values):
75 """License and license file are mutually exclusive."""
76 if "license" in values and "license_file" in values:
77 raise ValueError("license and license_file are mutually exclusive")
78 return values
80 @field_validator("version")
81 @classmethod
82 def validate_version(cls, v):
83 """Validate version using PEP 440."""
84 try:
85 _ = parse_version(v)
86 except ValueError as err:
87 raise ValueError("Invalid version") from err
88 return v
90 @field_validator("readme", "license_file")
91 @classmethod
92 def validate_readme(cls, v):
93 """Validate readme file(s) by checking whether files exist."""
94 if isinstance(v, list):
95 if any(not e.is_file() for e in v):
96 raise ValueError("Some file(s) do not exist")
97 else:
98 if not v.is_file():
99 raise ValueError("File does not exist")
101 @field_validator("keywords")
102 @classmethod
103 def check_keywords_field(cls, v):
104 """Check the keywords field."""
105 if v is None:
106 return v
108 # Check if number of keywords is at most 5
109 if v is not None and len(v) > 5:
110 raise ValueError("A maximum of 5 keywords is allowed")
112 for keyword in v:
113 check_keyword(keyword)
115 return v
118def check_keyword(keyword: str):
119 """Check if keyword is valid."""
120 # Check if keyword is ASCII and has at most 20 characters
121 if not keyword.isascii() or len(keyword) > 20:
122 raise ValueError(
123 "Each keyword must be ASCII text and have at most 20 characters"
124 )
126 # Check if keyword starts with an alphanumeric character
127 if not re.match(r"^[a-zA-Z0-9]", keyword):
128 raise ValueError("Each keyword must start with an alphanumeric character")
130 # Check if keyword contains only allowed characters
131 if not re.match(r"^[a-zA-Z0-9_\-+]+$", keyword):
132 raise ValueError("Keywords can only contain letters, numbers, _, -, or +")