Added more error handling and necessary functions

This commit is contained in:
Jason Streifling 2024-10-19 12:28:09 +02:00
parent d4e7bce5e2
commit f4dfd6d060
22 changed files with 317 additions and 122 deletions

View File

@ -14,9 +14,9 @@ go get git.streifling.com/jason/atom@latest
## Usage ## Usage
This library provides easy to use functions to safely create and extend elements This library provides convenient functions to safely create and extend elements
and attributes of an Atom feed. The intended way of using it entails using these and attributes of an Atom feed. This is because it can be hard to know all
functions. pitfalls of RFC4287. The intended way of using atom is with these functions.
```go ```go
package main package main

31
atom.go
View File

@ -10,18 +10,11 @@ import (
"golang.org/x/text/language" "golang.org/x/text/language"
) )
type (
EmailAddress string
LanguageTag string
MediaType string
IRI string
)
// isValidIRI checks whether an IRI is valid or not. It returns a bool. // isValidIRI checks whether an IRI is valid or not. It returns a bool.
// https://www.w3.org/2011/04/XMLSchema/TypeLibrary-IRI-RFC3987.xsd // https://www.w3.org/2011/04/XMLSchema/TypeLibrary-IRI-RFC3987.xsd
func isValidIRI(iri IRI) bool { func isValidIRI(iri string) bool {
pattern := `((([A-Za-z])[A-Za-z0-9+\-\.]*):((//(((([A-Za-z0-9\-\._~ -퟿豈-﷏ﷰ-￯𐀀-🿽𠀀-𯿽𰀀-𿿽񀀀-񏿽񐀀-񟿽񠀀-񯿽񰀀-񿿽򀀀-򏿽򐀀-򟿽򠀀-򯿽򰀀-򿿽󀀀-󏿽󐀀-󟿽󡀀-󯿽!$&'()*+,;=:]|(%[0-9A-Fa-f][0-9A-Fa-f]))*@))?((\[((((([0-9A-Fa-f]{0,4}:)){6}(([0-9A-Fa-f]{0,4}:[0-9A-Fa-f]{0,4})|(([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5])))))|(::(([0-9A-Fa-f]{0,4}:)){5}(([0-9A-Fa-f]{0,4}:[0-9A-Fa-f]{0,4})|(([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5])))))|(([0-9A-Fa-f]{0,4})?::(([0-9A-Fa-f]{0,4}:)){4}(([0-9A-Fa-f]{0,4}:[0-9A-Fa-f]{0,4})|(([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5])))))|((((([0-9A-Fa-f]{0,4}:))?[0-9A-Fa-f]{0,4}))?::(([0-9A-Fa-f]{0,4}:)){3}(([0-9A-Fa-f]{0,4}:[0-9A-Fa-f]{0,4})|(([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5])))))|((((([0-9A-Fa-f]{0,4}:)){0,2}[0-9A-Fa-f]{0,4}))?::(([0-9A-Fa-f]{0,4}:)){2}(([0-9A-Fa-f]{0,4}:[0-9A-Fa-f]{0,4})|(([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5])))))|((((([0-9A-Fa-f]{0,4}:)){0,3}[0-9A-Fa-f]{0,4}))?::[0-9A-Fa-f]{0,4}:(([0-9A-Fa-f]{0,4}:[0-9A-Fa-f]{0,4})|(([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5])))))|((((([0-9A-Fa-f]{0,4}:)){0,4}[0-9A-Fa-f]{0,4}))?::(([0-9A-Fa-f]{0,4}:[0-9A-Fa-f]{0,4})|(([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5])))))|((((([0-9A-Fa-f]{0,4}:)){0,5}[0-9A-Fa-f]{0,4}))?::[0-9A-Fa-f]{0,4})|((((([0-9A-Fa-f]{0,4}:)){0,6}[0-9A-Fa-f]{0,4}))?::))|(v[0-9A-Fa-f]+\.[A-Za-z0-9\-\._~!$&'()*+,;=:]+))\])|(([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5])))|(([A-Za-z0-9\-\._~ -퟿豈-﷏ﷰ-￯𐀀-🿽𠀀-𯿽𰀀-𿿽񀀀-񏿽񐀀-񟿽񠀀-񯿽񰀀-񿿽򀀀-򏿽򐀀-򟿽򠀀-򯿽򰀀-򿿽󀀀-󏿽󐀀-󟿽󡀀-󯿽]|(%[0-9A-Fa-f][0-9A-Fa-f])|[!$&'()*+,;=]))*)((:[0-9]*))?)((/(([A-Za-z0-9\-\._~ -퟿豈-﷏ﷰ-￯𐀀-🿽𠀀-𯿽𰀀-𿿽񀀀-񏿽񐀀-񟿽񠀀-񯿽񰀀-񿿽򀀀-򏿽򐀀-򟿽򠀀-򯿽򰀀-򿿽󀀀-󏿽󐀀-󟿽󡀀-󯿽]|(%[0-9A-Fa-f][0-9A-Fa-f])|[!$&'()*+,;=:@]))*))*)|(/(((([A-Za-z0-9\-\._~ -퟿豈-﷏ﷰ-￯𐀀-🿽𠀀-𯿽𰀀-𿿽񀀀-񏿽񐀀-񟿽񠀀-񯿽񰀀-񿿽򀀀-򏿽򐀀-򟿽򠀀-򯿽򰀀-򿿽󀀀-󏿽󐀀-󟿽󡀀-󯿽]|(%[0-9A-Fa-f][0-9A-Fa-f])|[!$&'()*+,;=:@]))+((/(([A-Za-z0-9\-\._~ -퟿豈-﷏ﷰ-￯𐀀-🿽𠀀-𯿽𰀀-𿿽񀀀-񏿽񐀀-񟿽񠀀-񯿽񰀀-񿿽򀀀-򏿽򐀀-򟿽򠀀-򯿽򰀀-򿿽󀀀-󏿽󐀀-󟿽󡀀-󯿽]|(%[0-9A-Fa-f][0-9A-Fa-f])|[!$&'()*+,;=:@]))*))*))?)|((([A-Za-z0-9\-\._~ -퟿豈-﷏ﷰ-￯𐀀-🿽𠀀-𯿽𰀀-𿿽񀀀-񏿽񐀀-񟿽񠀀-񯿽񰀀-񿿽򀀀-򏿽򐀀-򟿽򠀀-򯿽򰀀-򿿽󀀀-󏿽󐀀-󟿽󡀀-󯿽]|(%[0-9A-Fa-f][0-9A-Fa-f])|[!$&'()*+,;=:@]))+((/(([A-Za-z0-9\-\._~ -퟿豈-﷏ﷰ-￯𐀀-🿽𠀀-𯿽𰀀-𿿽񀀀-񏿽񐀀-񟿽񠀀-񯿽񰀀-񿿽򀀀-򏿽򐀀-򟿽򠀀-򯿽򰀀-򿿽󀀀-󏿽󐀀-󟿽󡀀-󯿽]|(%[0-9A-Fa-f][0-9A-Fa-f])|[!$&'()*+,;=:@]))*))*)|)((\?(([A-Za-z0-9\-\._~ -퟿豈-﷏ﷰ-￯𐀀-🿽𠀀-𯿽𰀀-𿿽񀀀-񏿽񐀀-񟿽񠀀-񯿽񰀀-񿿽򀀀-򏿽򐀀-򟿽򠀀-򯿽򰀀-򿿽󀀀-󏿽󐀀-󟿽󡀀-󯿽]|(%[0-9A-Fa-f][0-9A-Fa-f])|[!$&'()*+,;=:@])|[-󰀀-󿿽􀀀-􏿽/?])*))?((#((([A-Za-z0-9\-\._~ -퟿豈-﷏ﷰ-￯𐀀-🿽𠀀-𯿽𰀀-𿿽񀀀-񏿽񐀀-񟿽񠀀-񯿽񰀀-񿿽򀀀-򏿽򐀀-򟿽򠀀-򯿽򰀀-򿿽󀀀-󏿽󐀀-󟿽󡀀-󯿽]|(%[0-9A-Fa-f][0-9A-Fa-f])|[!$&'()*+,;=:@])|/|\?))*))?)` pattern := `((([A-Za-z])[A-Za-z0-9+\-\.]*):((//(((([A-Za-z0-9\-\._~ -퟿豈-﷏ﷰ-￯𐀀-🿽𠀀-𯿽𰀀-𿿽񀀀-񏿽񐀀-񟿽񠀀-񯿽񰀀-񿿽򀀀-򏿽򐀀-򟿽򠀀-򯿽򰀀-򿿽󀀀-󏿽󐀀-󟿽󡀀-󯿽!$&'()*+,;=:]|(%[0-9A-Fa-f][0-9A-Fa-f]))*@))?((\[((((([0-9A-Fa-f]{0,4}:)){6}(([0-9A-Fa-f]{0,4}:[0-9A-Fa-f]{0,4})|(([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5])))))|(::(([0-9A-Fa-f]{0,4}:)){5}(([0-9A-Fa-f]{0,4}:[0-9A-Fa-f]{0,4})|(([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5])))))|(([0-9A-Fa-f]{0,4})?::(([0-9A-Fa-f]{0,4}:)){4}(([0-9A-Fa-f]{0,4}:[0-9A-Fa-f]{0,4})|(([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5])))))|((((([0-9A-Fa-f]{0,4}:))?[0-9A-Fa-f]{0,4}))?::(([0-9A-Fa-f]{0,4}:)){3}(([0-9A-Fa-f]{0,4}:[0-9A-Fa-f]{0,4})|(([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5])))))|((((([0-9A-Fa-f]{0,4}:)){0,2}[0-9A-Fa-f]{0,4}))?::(([0-9A-Fa-f]{0,4}:)){2}(([0-9A-Fa-f]{0,4}:[0-9A-Fa-f]{0,4})|(([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5])))))|((((([0-9A-Fa-f]{0,4}:)){0,3}[0-9A-Fa-f]{0,4}))?::[0-9A-Fa-f]{0,4}:(([0-9A-Fa-f]{0,4}:[0-9A-Fa-f]{0,4})|(([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5])))))|((((([0-9A-Fa-f]{0,4}:)){0,4}[0-9A-Fa-f]{0,4}))?::(([0-9A-Fa-f]{0,4}:[0-9A-Fa-f]{0,4})|(([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5])))))|((((([0-9A-Fa-f]{0,4}:)){0,5}[0-9A-Fa-f]{0,4}))?::[0-9A-Fa-f]{0,4})|((((([0-9A-Fa-f]{0,4}:)){0,6}[0-9A-Fa-f]{0,4}))?::))|(v[0-9A-Fa-f]+\.[A-Za-z0-9\-\._~!$&'()*+,;=:]+))\])|(([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))\.([0-9]|([1-9][0-9])|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5])))|(([A-Za-z0-9\-\._~ -퟿豈-﷏ﷰ-￯𐀀-🿽𠀀-𯿽𰀀-𿿽񀀀-񏿽񐀀-񟿽񠀀-񯿽񰀀-񿿽򀀀-򏿽򐀀-򟿽򠀀-򯿽򰀀-򿿽󀀀-󏿽󐀀-󟿽󡀀-󯿽]|(%[0-9A-Fa-f][0-9A-Fa-f])|[!$&'()*+,;=]))*)((:[0-9]*))?)((/(([A-Za-z0-9\-\._~ -퟿豈-﷏ﷰ-￯𐀀-🿽𠀀-𯿽𰀀-𿿽񀀀-񏿽񐀀-񟿽񠀀-񯿽񰀀-񿿽򀀀-򏿽򐀀-򟿽򠀀-򯿽򰀀-򿿽󀀀-󏿽󐀀-󟿽󡀀-󯿽]|(%[0-9A-Fa-f][0-9A-Fa-f])|[!$&'()*+,;=:@]))*))*)|(/(((([A-Za-z0-9\-\._~ -퟿豈-﷏ﷰ-￯𐀀-🿽𠀀-𯿽𰀀-𿿽񀀀-񏿽񐀀-񟿽񠀀-񯿽񰀀-񿿽򀀀-򏿽򐀀-򟿽򠀀-򯿽򰀀-򿿽󀀀-󏿽󐀀-󟿽󡀀-󯿽]|(%[0-9A-Fa-f][0-9A-Fa-f])|[!$&'()*+,;=:@]))+((/(([A-Za-z0-9\-\._~ -퟿豈-﷏ﷰ-￯𐀀-🿽𠀀-𯿽𰀀-𿿽񀀀-񏿽񐀀-񟿽񠀀-񯿽񰀀-񿿽򀀀-򏿽򐀀-򟿽򠀀-򯿽򰀀-򿿽󀀀-󏿽󐀀-󟿽󡀀-󯿽]|(%[0-9A-Fa-f][0-9A-Fa-f])|[!$&'()*+,;=:@]))*))*))?)|((([A-Za-z0-9\-\._~ -퟿豈-﷏ﷰ-￯𐀀-🿽𠀀-𯿽𰀀-𿿽񀀀-񏿽񐀀-񟿽񠀀-񯿽񰀀-񿿽򀀀-򏿽򐀀-򟿽򠀀-򯿽򰀀-򿿽󀀀-󏿽󐀀-󟿽󡀀-󯿽]|(%[0-9A-Fa-f][0-9A-Fa-f])|[!$&'()*+,;=:@]))+((/(([A-Za-z0-9\-\._~ -퟿豈-﷏ﷰ-￯𐀀-🿽𠀀-𯿽𰀀-𿿽񀀀-񏿽񐀀-񟿽񠀀-񯿽񰀀-񿿽򀀀-򏿽򐀀-򟿽򠀀-򯿽򰀀-򿿽󀀀-󏿽󐀀-󟿽󡀀-󯿽]|(%[0-9A-Fa-f][0-9A-Fa-f])|[!$&'()*+,;=:@]))*))*)|)((\?(([A-Za-z0-9\-\._~ -퟿豈-﷏ﷰ-￯𐀀-🿽𠀀-𯿽𰀀-𿿽񀀀-񏿽񐀀-񟿽񠀀-񯿽񰀀-񿿽򀀀-򏿽򐀀-򟿽򠀀-򯿽򰀀-򿿽󀀀-󏿽󐀀-󟿽󡀀-󯿽]|(%[0-9A-Fa-f][0-9A-Fa-f])|[!$&'()*+,;=:@])|[-󰀀-󿿽􀀀-􏿽/?])*))?((#((([A-Za-z0-9\-\._~ -퟿豈-﷏ﷰ-￯𐀀-🿽𠀀-𯿽𰀀-𿿽񀀀-񏿽񐀀-񟿽񠀀-񯿽񰀀-񿿽򀀀-򏿽򐀀-򟿽򠀀-򯿽򰀀-򿿽󀀀-󏿽󐀀-󟿽󡀀-󯿽]|(%[0-9A-Fa-f][0-9A-Fa-f])|[!$&'()*+,;=:@])|/|\?))*))?)`
return regexp.MustCompile(pattern).MatchString(string(iri)) return regexp.MustCompile(pattern).MatchString(iri)
} }
// isCorrectlyEscaped checks whether a string is correctly escaped as per // isCorrectlyEscaped checks whether a string is correctly escaped as per
@ -40,8 +33,8 @@ func isCorrectlyEscaped(text string) bool {
// isCompositeMediaType checks whether a string is a composite media type. It // isCompositeMediaType checks whether a string is a composite media type. It
// returns a bool. // returns a bool.
func isCompositeMediaType(mediaType string) bool { func isCompositeMediaType(m string) bool {
mediaType, _, err := mime.ParseMediaType(mediaType) mediaType, _, err := mime.ParseMediaType(m)
if err != nil { if err != nil {
return false return false
} }
@ -51,8 +44,8 @@ func isCompositeMediaType(mediaType string) bool {
// isXMLMediaType checks whether a string is an xml media type. It returns a // isXMLMediaType checks whether a string is an xml media type. It returns a
// bool. // bool.
func isXMLMediaType(mediaType string) bool { func isXMLMediaType(m string) bool {
mediaType, _, err := mime.ParseMediaType(mediaType) mediaType, _, err := mime.ParseMediaType(m)
if err != nil { if err != nil {
return false return false
} }
@ -62,8 +55,8 @@ func isXMLMediaType(mediaType string) bool {
// isValidMediaType checks whether a string is a valid media type. It returns a // isValidMediaType checks whether a string is a valid media type. It returns a
// bool. // bool.
func isValidMediaType(mediaType string) bool { func isValidMediaType(m string) bool {
mediaType, _, err := mime.ParseMediaType(mediaType) mediaType, _, err := mime.ParseMediaType(m)
if err != nil { if err != nil {
return false return false
} }
@ -77,8 +70,8 @@ func isValidMediaType(mediaType string) bool {
} }
// isValidLanguageTag checks whether a LanguageTag is valid. It returns a bool. // isValidLanguageTag checks whether a LanguageTag is valid. It returns a bool.
func isValidLanguageTag(tag LanguageTag) bool { func isValidLanguageTag(languageTag string) bool {
_, err := language.Parse(string(tag)) _, err := language.Parse(languageTag)
return err == nil return err == nil
} }
@ -89,6 +82,6 @@ func isValidAttribute(attribute string) bool {
} }
// NewURN generates an new valid IRI based on a UUID. It returns an IRI. // NewURN generates an new valid IRI based on a UUID. It returns an IRI.
func NewURN() IRI { func NewURN() string {
return IRI(fmt.Sprint("urn:uuid:", uuid.New())) return fmt.Sprint("urn:uuid:", uuid.New())
} }

View File

@ -2,6 +2,7 @@ package atom
import ( import (
"encoding/xml" "encoding/xml"
"errors"
"fmt" "fmt"
"html" "html"
) )
@ -10,18 +11,47 @@ type Category struct {
XMLName xml.Name `xml:"category"` XMLName xml.Name `xml:"category"`
*CommonAttributes *CommonAttributes
Term string `xml:"term,attr"` Term string `xml:"term,attr"`
Scheme IRI `xml:"scheme,attr,omitempty"` Scheme string `xml:"scheme,attr,omitempty"` // IRI
Label string `xml:"label,attr,omitempty"` Label string `xml:"label,attr,omitempty"`
} }
// NewCategory creates a new Category. It returns a *Category. // NewCategory creates a new Category. It returns a *Category and an error.
func NewCategory(term string) *Category { func NewCategory(term string) (*Category, error) {
return &Category{Term: term} if term == "" {
return nil, errors.New("error creating new category: term string empty")
}
return &Category{Term: term}, nil
} }
// SetLabel sets the label of the Category. // SetTerm sets the Term attribute of the Category. It returns an error.
func (c *Category) SetLabel(label string) { func (c *Category) SetTerm(t string) error {
if t == "" {
return errors.New("error setting term of category: t string empty")
}
c.Term = t
return nil
}
// SetScheme sets the Scheme attribute of the Category. It returns an error.
func (c *Category) SetScheme(s string) error {
if !isValidIRI(s) {
return fmt.Errorf("scheme %v not correctly formatted", s)
}
c.Scheme = s
return nil
}
// SetLabel sets the Label attribute of the Category. It returns an error.
func (c *Category) SetLabel(label string) error {
if label == "" {
return errors.New("error setting label of category: label string empty")
}
c.Label = html.UnescapeString(label) c.Label = html.UnescapeString(label)
return nil
} }
// Check checks the Category for incompatibilities with RFC4287. It returns an // Check checks the Category for incompatibilities with RFC4287. It returns an

View File

@ -1,10 +1,13 @@
package atom package atom
import "encoding/xml" import (
"encoding/xml"
"errors"
)
type CommonAttributes struct { type CommonAttributes struct {
Base IRI `xml:"base,attr,omitempty"` Base string `xml:"base,attr,omitempty"` // IRI
Lang LanguageTag `xml:"lang,attr,omitempty"` Lang string `xml:"lang,attr,omitempty"` // LanguageTag
UndefinedAttributes []*xml.Attr `xml:",attr,omitempty"` UndefinedAttributes []*xml.Attr `xml:",attr,omitempty"`
} }
@ -14,12 +17,21 @@ func NewCommonAttributes() *CommonAttributes {
return new(CommonAttributes) return new(CommonAttributes)
} }
// AddExtensionAttribute adds the ExtensionAttribute to the CommonAttributes. // AddAttribute adds the Attribute to the CommonAttributes. It returns an error.
func (c *CommonAttributes) AddExtensionAttribute(name, value string) { func (c *CommonAttributes) AddAttribute(name, value string) error {
if name == "" {
return errors.New("error adding attribute: name string empty")
}
if value == "" {
return errors.New("error adding attribute: value string empty")
}
if c.UndefinedAttributes == nil { if c.UndefinedAttributes == nil {
c.UndefinedAttributes = make([]*xml.Attr, 1) c.UndefinedAttributes = make([]*xml.Attr, 1)
c.UndefinedAttributes[0] = &xml.Attr{Name: xml.Name{Local: name}, Value: value} c.UndefinedAttributes[0] = &xml.Attr{Name: xml.Name{Local: name}, Value: value}
} else { } else {
c.UndefinedAttributes = append(c.UndefinedAttributes, &xml.Attr{Name: xml.Name{Local: name}, Value: value}) c.UndefinedAttributes = append(c.UndefinedAttributes, &xml.Attr{Name: xml.Name{Local: name}, Value: value})
} }
return nil
} }

View File

@ -28,6 +28,6 @@ func NewContent(contentType int, mediaType string, content any) (Content, error)
case 3: case 3:
return newOutOfLineContent(mediaType, content) return newOutOfLineContent(mediaType, content)
default: default:
return nil, fmt.Errorf("%v is not a valid text type", contentType) return nil, fmt.Errorf("error creating new content: %v is not a valid text type", contentType)
} }
} }

View File

@ -13,7 +13,7 @@ type Date struct {
// DateTime formats a time.Time to string formated as defined by RFC3339. It // DateTime formats a time.Time to string formated as defined by RFC3339. It
// returns a string. // returns a string.
func DateTime(t time.Time) string { func DateTime(t time.Time) string {
return string(t.Format(time.RFC3339)) return t.Format(time.RFC3339)
} }
// NewDate creates a new Date. It returns a *Date. // NewDate creates a new Date. It returns a *Date.

View File

@ -2,6 +2,7 @@ package atom
import ( import (
"encoding/xml" "encoding/xml"
"errors"
"fmt" "fmt"
"strings" "strings"
"time" "time"
@ -75,8 +76,12 @@ func NewEntry(title string) (*Entry, error) {
}, nil }, nil
} }
// AddAuthor adds the Person as an author to the Entry. // AddAuthor adds the Person as an author to the Entry. It returns an error.
func (e *Entry) AddAuthor(p *Person) { func (e *Entry) AddAuthor(p *Person) error {
if p == nil {
return errors.New("error adding author element to entry: *Person is nil")
}
if e.Authors == nil { if e.Authors == nil {
e.Authors = make([]*Person, 1) e.Authors = make([]*Person, 1)
e.Authors[0] = p e.Authors[0] = p
@ -85,10 +90,15 @@ func (e *Entry) AddAuthor(p *Person) {
} }
e.Updated.DateTime = DateTime(time.Now()) e.Updated.DateTime = DateTime(time.Now())
return nil
} }
// AddCategory adds the Category to the Entry. // AddCategory adds the Category to the Entry. It returns an error.
func (e *Entry) AddCategory(c *Category) { func (e *Entry) AddCategory(c *Category) error {
if c == nil {
return errors.New("error adding category element to entry: *Category is nil")
}
if e.Categories == nil { if e.Categories == nil {
e.Categories = make([]*Category, 1) e.Categories = make([]*Category, 1)
e.Categories[0] = c e.Categories[0] = c
@ -97,10 +107,16 @@ func (e *Entry) AddCategory(c *Category) {
} }
e.Updated.DateTime = DateTime(time.Now()) e.Updated.DateTime = DateTime(time.Now())
return nil
} }
// AddContributor adds the Person as a contributor to the Entry. // AddContributor adds the Person as a contributor to the Entry. It returns an
func (e *Entry) AddContributor(c *Person) { // error.
func (e *Entry) AddContributor(c *Person) error {
if c == nil {
return errors.New("error adding contributor element to entry: *Person is nil")
}
if e.Contributors == nil { if e.Contributors == nil {
e.Contributors = make([]*Person, 1) e.Contributors = make([]*Person, 1)
e.Contributors[0] = c e.Contributors[0] = c
@ -109,10 +125,15 @@ func (e *Entry) AddContributor(c *Person) {
} }
e.Updated.DateTime = DateTime(time.Now()) e.Updated.DateTime = DateTime(time.Now())
return nil
} }
// AddLink adds the Link to the Entry. // AddLink adds the Link to the Entry. It returns an error.
func (e *Entry) AddLink(l *Link) { func (e *Entry) AddLink(l *Link) error {
if l == nil {
return errors.New("error adding link element to entry: *Link is nil")
}
if e.Links == nil { if e.Links == nil {
e.Links = make([]*Link, 1) e.Links = make([]*Link, 1)
e.Links[0] = l e.Links[0] = l
@ -121,10 +142,15 @@ func (e *Entry) AddLink(l *Link) {
} }
e.Updated.DateTime = DateTime(time.Now()) e.Updated.DateTime = DateTime(time.Now())
return nil
} }
// AddExtension adds the ExtensionElement to the Entry. // AddExtension adds the ExtensionElement to the Entry. It returns an error.
func (e *Entry) AddExtension(x *ExtensionElement) { func (e *Entry) AddExtension(x *ExtensionElement) error {
if x == nil {
return errors.New("error adding extension element to entry: *ExtensionElement is nil")
}
if e.Extensions == nil { if e.Extensions == nil {
e.Extensions = make([]*ExtensionElement, 1) e.Extensions = make([]*ExtensionElement, 1)
e.Extensions[0] = x e.Extensions[0] = x
@ -133,6 +159,7 @@ func (e *Entry) AddExtension(x *ExtensionElement) {
} }
e.Updated.DateTime = DateTime(time.Now()) e.Updated.DateTime = DateTime(time.Now())
return nil
} }
// Check checks the Entry for incompatibilities with RFC4287. It returns an // Check checks the Entry for incompatibilities with RFC4287. It returns an

View File

@ -11,14 +11,25 @@ type ExtensionElement struct {
} }
// NewExtensionElement creates a new ExtensionElement. It returns a // NewExtensionElement creates a new ExtensionElement. It returns a
// *ExtensionElement. // *ExtensionElement and an error.
func NewExtensionElement(name string, value any) *ExtensionElement { func NewExtensionElement(name string, value any) (*ExtensionElement, error) {
return &ExtensionElement{XMLName: xml.Name{Local: name}, Value: value} if name == "" {
return nil, errors.New("error adding extension attribute: name string empty")
}
if value == "" {
return nil, errors.New("error adding extension attribute: value string empty")
}
return &ExtensionElement{XMLName: xml.Name{Local: name}, Value: value}, nil
} }
// Check checks the ExtensionElement for incompatibilities with RFC4287. It // Check checks the ExtensionElement for incompatibilities with RFC4287. It
// returns an error. // returns an error.
func (e *ExtensionElement) Check() error { func (e *ExtensionElement) Check() error {
if e.XMLName.Local == "" {
return errors.New("xml name of extension empty")
}
if e.Value == nil { if e.Value == nil {
return errors.New("value element of extension empty") return errors.New("value element of extension empty")
} }

57
feed.go
View File

@ -2,6 +2,7 @@ package atom
import ( import (
"encoding/xml" "encoding/xml"
"errors"
"fmt" "fmt"
"time" "time"
) )
@ -44,8 +45,12 @@ func NewFeed(title string) (*Feed, error) {
}, nil }, nil
} }
// AddAuthor adds the Person as an author to the Feed. // AddAuthor adds the Person as an author to the Feed. It returns an error.
func (f *Feed) AddAuthor(p *Person) { func (f *Feed) AddAuthor(p *Person) error {
if p == nil {
return errors.New("error adding author element to feed: *Person is nil")
}
if f.Authors == nil { if f.Authors == nil {
f.Authors = make([]*Person, 1) f.Authors = make([]*Person, 1)
f.Authors[0] = p f.Authors[0] = p
@ -54,10 +59,15 @@ func (f *Feed) AddAuthor(p *Person) {
} }
f.Updated.DateTime = DateTime(time.Now()) f.Updated.DateTime = DateTime(time.Now())
return nil
} }
// AddCategory adds the Category to the Feed. // AddCategory adds the Category to the Feed. It returns an error.
func (f *Feed) AddCategory(c *Category) { func (f *Feed) AddCategory(c *Category) error {
if c == nil {
return errors.New("error adding category element to feed: *Category is nil")
}
if f.Categories == nil { if f.Categories == nil {
f.Categories = make([]*Category, 1) f.Categories = make([]*Category, 1)
f.Categories[0] = c f.Categories[0] = c
@ -66,10 +76,16 @@ func (f *Feed) AddCategory(c *Category) {
} }
f.Updated.DateTime = DateTime(time.Now()) f.Updated.DateTime = DateTime(time.Now())
return nil
} }
// AddContributor adds the Person as a contributor to the Feed. // AddContributor adds the Person as a contributor to the Feed. It returns an
func (f *Feed) AddContributor(c *Person) { // error.
func (f *Feed) AddContributor(c *Person) error {
if c == nil {
return errors.New("error adding contributor element to feed: *Person is nil")
}
if f.Contributors == nil { if f.Contributors == nil {
f.Contributors = make([]*Person, 1) f.Contributors = make([]*Person, 1)
f.Contributors[0] = c f.Contributors[0] = c
@ -78,10 +94,16 @@ func (f *Feed) AddContributor(c *Person) {
} }
f.Updated.DateTime = DateTime(time.Now()) f.Updated.DateTime = DateTime(time.Now())
return nil
} }
// AddLink adds the Link to the Feed. There should be one Link with Rel "self". // AddLink adds the Link to the Feed. It returns an error. There should be one
func (f *Feed) AddLink(l *Link) { // Link with Rel "self".
func (f *Feed) AddLink(l *Link) error {
if l == nil {
return errors.New("error adding link element to feed: *Link is nil")
}
if f.Links == nil { if f.Links == nil {
f.Links = make([]*Link, 1) f.Links = make([]*Link, 1)
f.Links[0] = l f.Links[0] = l
@ -90,10 +112,15 @@ func (f *Feed) AddLink(l *Link) {
} }
f.Updated.DateTime = DateTime(time.Now()) f.Updated.DateTime = DateTime(time.Now())
return nil
} }
// AddExtension adds the Extension to the Feed. // AddExtension adds the Extension to the Feed. It returns an error.
func (f *Feed) AddExtension(e *ExtensionElement) { func (f *Feed) AddExtension(e *ExtensionElement) error {
if e == nil {
return errors.New("error adding extension element to feed: *ExtensionElement is nil")
}
if f.Extensions == nil { if f.Extensions == nil {
f.Extensions = make([]*ExtensionElement, 1) f.Extensions = make([]*ExtensionElement, 1)
f.Extensions[0] = e f.Extensions[0] = e
@ -102,10 +129,15 @@ func (f *Feed) AddExtension(e *ExtensionElement) {
} }
f.Updated.DateTime = DateTime(time.Now()) f.Updated.DateTime = DateTime(time.Now())
return nil
} }
// AddEntry adds the Entry to the Feed. // AddEntry adds the Entry to the Feed. It returns an error.
func (f *Feed) AddEntry(e *Entry) { func (f *Feed) AddEntry(e *Entry) error {
if e == nil {
return errors.New("error adding entry element to feed: *Entry is nil")
}
if f.Entries == nil { if f.Entries == nil {
f.Entries = make([]*Entry, 1) f.Entries = make([]*Entry, 1)
f.Entries[0] = e f.Entries[0] = e
@ -114,6 +146,7 @@ func (f *Feed) AddEntry(e *Entry) {
} }
f.Updated.DateTime = DateTime(time.Now()) f.Updated.DateTime = DateTime(time.Now())
return nil
} }
// Check checks the Feed for incompatibilities with RFC4287. It returns an // Check checks the Feed for incompatibilities with RFC4287. It returns an

View File

@ -2,6 +2,7 @@ package atom
import ( import (
"encoding/xml" "encoding/xml"
"errors"
"fmt" "fmt"
"html" "html"
) )
@ -9,14 +10,27 @@ import (
type Generator struct { type Generator struct {
XMLName xml.Name `xml:"generator"` XMLName xml.Name `xml:"generator"`
*CommonAttributes *CommonAttributes
URI IRI `xml:"uri,attr,omitempty"` URI string `xml:"uri,attr,omitempty"` // IRI
Version string `xml:"version,attr,omitempty"` Version string `xml:"version,attr,omitempty"`
Text string `xml:",chardata"` Text string `xml:",chardata"`
} }
// NewGenerator creates a new Generator. It returns a *Generator. // NewGenerator creates a new Generator. It returns a *Generator and an error.
func NewGenerator(text string) *Generator { func NewGenerator(text string) (*Generator, error) {
return &Generator{Text: html.UnescapeString(text)} if text == "" {
return nil, errors.New("error creating new generator: text string empty")
}
return &Generator{Text: html.UnescapeString(text)}, nil
}
// SetURI sets the URI attribute of the Generator. It returns an error.
func (g *Generator) SetURI(uri string) error {
if !isValidIRI(uri) {
return fmt.Errorf("uri %v not correctly formatted", g)
}
return nil
} }
// Check checks the Generator for incompatibilities with RFC4287. It returns an // Check checks the Generator for incompatibilities with RFC4287. It returns an

View File

@ -9,11 +9,11 @@ import (
type Icon struct { type Icon struct {
XMLName xml.Name `xml:"icon"` XMLName xml.Name `xml:"icon"`
*CommonAttributes *CommonAttributes
URI IRI `xml:",chardata"` URI string `xml:",chardata"` // IRI
} }
// NewIcon creates a new Icon. It returns a *Icon and an error. // NewIcon creates a new Icon. It returns a *Icon and an error.
func NewIcon(uri IRI) (*Icon, error) { func NewIcon(uri string) (*Icon, error) {
if !isValidIRI(uri) { if !isValidIRI(uri) {
return nil, fmt.Errorf("uri %v not correctly formatted", uri) return nil, fmt.Errorf("uri %v not correctly formatted", uri)
} }

6
id.go
View File

@ -9,16 +9,16 @@ import (
type ID struct { type ID struct {
XMLName xml.Name `xml:"id"` XMLName xml.Name `xml:"id"`
*CommonAttributes *CommonAttributes
URI IRI `xml:",chardata"` URI string `xml:",chardata"` // IRI
} }
// NewID creates a new ID. It returns a *ID and an error. // NewID creates a new ID. It returns a *ID and an error.
func NewID(uri IRI) (*ID, error) { func NewID(uri string) (*ID, error) {
if !isValidIRI(uri) { if !isValidIRI(uri) {
return nil, fmt.Errorf("uri %v not correctly formatted", uri) return nil, fmt.Errorf("uri %v not correctly formatted", uri)
} }
return &ID{URI: IRI(uri)}, nil return &ID{URI: uri}, nil
} }
// Check checks the ID for incompatibilities with RFC4287. It returns an error. // Check checks the ID for incompatibilities with RFC4287. It returns an error.

View File

@ -9,18 +9,19 @@ import (
type InlineOtherContent struct { type InlineOtherContent struct {
XMLName xml.Name `xml:"content"` XMLName xml.Name `xml:"content"`
*CommonAttributes *CommonAttributes
AnyElement any `xml:",chardata"` AnyElement any `xml:",chardata"`
Type MediaType `xml:"type,attr,omitempty"` Type string `xml:"type,attr,omitempty"` // MediaType
} }
// newInlineOtherContent creates a new InlineOtherContent. It returns a // newInlineOtherContent creates a new InlineOtherContent. It returns a
// *InlineOtherContent and an error. // *InlineOtherContent and an error.
func newInlineOtherContent(mediaType string, content any) (*InlineOtherContent, error) { func newInlineOtherContent(mediaType string, content any) (*InlineOtherContent, error) {
if mediaType, _, err := mime.ParseMediaType(mediaType); err != nil { if !isValidMediaType(mediaType) {
return nil, fmt.Errorf("media type %v incompatible with inline other content", mediaType) return nil, fmt.Errorf("error creating new inline other content: media type %v invalid", mediaType)
} }
mediaType, _, _ = mime.ParseMediaType(mediaType)
return &InlineOtherContent{Type: MediaType(mediaType), AnyElement: content}, nil return &InlineOtherContent{Type: mediaType, AnyElement: content}, nil
} }
// isContent checks whether the InlineOtherContent is a Content. It returns a // isContent checks whether the InlineOtherContent is a Content. It returns a
@ -32,15 +33,15 @@ func (i *InlineOtherContent) isContent() bool { return true }
func (i *InlineOtherContent) hasSRC() bool { return false } func (i *InlineOtherContent) hasSRC() bool { return false }
// getType returns the Type of the InlineOtherContent as a string. // getType returns the Type of the InlineOtherContent as a string.
func (i *InlineOtherContent) getType() string { return string(i.Type) } func (i *InlineOtherContent) getType() string { return i.Type }
// Check checks the InlineOtherContent for incompatibilities with RFC4287. It // Check checks the InlineOtherContent for incompatibilities with RFC4287. It
// returns an error. // returns an error.
func (i *InlineOtherContent) Check() error { func (i *InlineOtherContent) Check() error {
mediaType := i.getType() mediaType := i.getType()
if mediaType, _, err := mime.ParseMediaType(mediaType); err != nil { if !isValidMediaType(mediaType) {
return fmt.Errorf("type attribute %v incompatible with inline other content", mediaType) return fmt.Errorf("type attribute of inline other content %v invalid media type", i)
} }
if isCompositeMediaType(mediaType) { if isCompositeMediaType(mediaType) {

44
link.go
View File

@ -9,17 +9,39 @@ import (
type Link struct { type Link struct {
XMLName xml.Name `xml:"link"` XMLName xml.Name `xml:"link"`
*CommonAttributes *CommonAttributes
Title string `xml:"title,attr,omitempty"` Title string `xml:"title,attr,omitempty"`
Href IRI `xml:"href,attr"` Href string `xml:"href,attr"` // IRI
Rel string `xml:"rel,attr,omitempty"` Rel string `xml:"rel,attr,omitempty"`
Type MediaType `xml:"type,attr,omitempty"` Type string `xml:"type,attr,omitempty"` // MediaType
HrefLang LanguageTag `xml:"hreflang,attr,omitempty"` HrefLang string `xml:"hreflang,attr,omitempty"` // LanguageTag
Length uint `xml:"length,attr,omitempty"` Length uint `xml:"length,attr,omitempty"`
} }
// NewLink creates a new Link. It returns a *Link. // NewLink creates a new Link. It returns a *Link and an error.
func NewLink(href string) *Link { func NewLink(href string) (*Link, error) {
return &Link{Href: IRI(href)} if !isValidIRI(href) {
return nil, fmt.Errorf("href %v not correctly formatted", href)
}
return &Link{Href: href}, nil
}
// SetType sets the Type attribute of the Link. It returns an error.
func (l *Link) SetType(t string) error {
if !isValidMediaType(t) {
return fmt.Errorf("type %v invalid media type", l)
}
return nil
}
// SetHrefLang sets the HrefLang attribute of the Link. It returns an error.
func (l *Link) SetHrefLang(h string) error {
if !isValidLanguageTag(h) {
return fmt.Errorf("hreflang %v invalid language tag", l)
}
return nil
} }
// Check checks the Link for incompatibilities with RFC4287. It returns an // Check checks the Link for incompatibilities with RFC4287. It returns an
@ -34,13 +56,13 @@ func (l *Link) Check() error {
} }
if l.Rel != "" { if l.Rel != "" {
if strings.Contains(l.Rel, ":") && !isValidIRI(IRI(l.Rel)) { if strings.Contains(l.Rel, ":") && !isValidIRI(l.Rel) {
return fmt.Errorf("rel attribute of link %v not correctly formatted", l) return fmt.Errorf("rel attribute of link %v not correctly formatted", l)
} }
} }
if l.Type != "" { if l.Type != "" {
if !isValidMediaType(string(l.Type)) { if !isValidMediaType(l.Type) {
return fmt.Errorf("type attribute of link %v invalid media type", l) return fmt.Errorf("type attribute of link %v invalid media type", l)
} }
} }

View File

@ -8,11 +8,11 @@ import (
type Logo struct { type Logo struct {
XMLName xml.Name `xml:"logo"` XMLName xml.Name `xml:"logo"`
*CommonAttributes *CommonAttributes
URI IRI `xml:",chardata"` URI string `xml:",chardata"` // IRI
} }
// NewLogo creates a new Logo. It returns a *Logo. // NewLogo creates a new Logo. It returns a *Logo and an error.
func NewLogo(uri IRI) (*Logo, error) { func NewLogo(uri string) (*Logo, error) {
if !isValidIRI(uri) { if !isValidIRI(uri) {
return nil, fmt.Errorf("uri %v not correctly formatted", uri) return nil, fmt.Errorf("uri %v not correctly formatted", uri)
} }

View File

@ -9,18 +9,19 @@ import (
type OutOfLineContent struct { type OutOfLineContent struct {
XMLName xml.Name `xml:"content"` XMLName xml.Name `xml:"content"`
*CommonAttributes *CommonAttributes
Type MediaType `xml:"type,attr,omitempty"` Type string `xml:"type,attr,omitempty"` // MediaType
SRC IRI `xml:"src,attr"` SRC string `xml:"src,attr"` // IRI
} }
// newOutOfLineContent creates a new OutOfLineContent. It returns a // newOutOfLineContent creates a new OutOfLineContent. It returns a
// *OutOfLineContent and an error. // *OutOfLineContent and an error.
func newOutOfLineContent(mediaType string, content any) (*OutOfLineContent, error) { func newOutOfLineContent(mediaType string, content any) (*OutOfLineContent, error) {
if mediaType, _, err := mime.ParseMediaType(mediaType); err != nil { if !isValidMediaType(mediaType) {
return nil, fmt.Errorf("media type %v incompatible with out of line content", mediaType) return nil, fmt.Errorf("error creating new out of line content: media type %v invalid", mediaType)
} }
mediaType, _, _ = mime.ParseMediaType(mediaType)
iri, ok := content.(IRI) iri, ok := content.(string)
if !ok { if !ok {
return nil, fmt.Errorf("content type %T incompatible with out of line content", content) return nil, fmt.Errorf("content type %T incompatible with out of line content", content)
} }
@ -29,7 +30,7 @@ func newOutOfLineContent(mediaType string, content any) (*OutOfLineContent, erro
return nil, fmt.Errorf("content %v not a valid uri", iri) return nil, fmt.Errorf("content %v not a valid uri", iri)
} }
return &OutOfLineContent{Type: MediaType(mediaType), SRC: iri}, nil return &OutOfLineContent{Type: mediaType, SRC: iri}, nil
} }
// isContent checks whether the OutOfLineContent is a Content. It returns a // isContent checks whether the OutOfLineContent is a Content. It returns a
@ -41,15 +42,15 @@ func (o *OutOfLineContent) isContent() bool { return true }
func (o *OutOfLineContent) hasSRC() bool { return true } func (o *OutOfLineContent) hasSRC() bool { return true }
// getType returns the Type of the OutOfLineContent as a string. // getType returns the Type of the OutOfLineContent as a string.
func (o *OutOfLineContent) getType() string { return string(o.Type) } func (o *OutOfLineContent) getType() string { return o.Type }
// Check checks the OutOfLineContent for incompatibilities with RFC4287. It // Check checks the OutOfLineContent for incompatibilities with RFC4287. It
// returns an error. // returns an error.
func (o *OutOfLineContent) Check() error { func (o *OutOfLineContent) Check() error {
mediaType := o.getType() mediaType := o.getType()
if mediaType, _, err := mime.ParseMediaType(mediaType); err != nil { if !isValidMediaType(mediaType) {
return fmt.Errorf("type attribute %v incompatible with out of line content", mediaType) return fmt.Errorf("type attribute of out of line content %v invalid media type", o)
} }
if isCompositeMediaType(mediaType) { if isCompositeMediaType(mediaType) {

View File

@ -1,6 +1,7 @@
package atom package atom
import ( import (
"errors"
"fmt" "fmt"
"net/mail" "net/mail"
) )
@ -8,24 +9,43 @@ import (
type Person struct { type Person struct {
*CommonAttributes *CommonAttributes
Name string `xml:"name"` Name string `xml:"name"`
URI IRI `xml:"uri,omitempty"` URI string `xml:"uri,omitempty"` // IRI
Email EmailAddress `xml:"email,omitempty"` Email string `xml:"email,omitempty"` // EmailAddress
Extensions []*ExtensionElement `xml:",any,omitempty"` Extensions []*ExtensionElement `xml:",any,omitempty"`
} }
// NewPerson creates a new Person. It returns a *Person. // NewPerson creates a new Person. It returns a *Person and an error.
func NewPerson(name string) *Person { func NewPerson(name string) (*Person, error) {
return &Person{Name: name} if name == "" {
return nil, errors.New("error creating new person: name string empty")
}
return &Person{Name: name}, nil
} }
// AddExtension adds the Extension to the Person. // SetURI sets the URI element of the Person. It returns an error.
func (p *Person) AddExtension(e *ExtensionElement) { func (l *Link) SetURI(uri string) error {
if !isValidIRI(uri) {
return fmt.Errorf("uri %v not correctly formatted", uri)
}
return nil
}
// AddExtension adds the Extension to the Person. It returns an error.
func (p *Person) AddExtension(e *ExtensionElement) error {
if e == nil {
return errors.New("error adding extension element to person: *ExtensionElement is nil")
}
if p.Extensions == nil { if p.Extensions == nil {
p.Extensions = make([]*ExtensionElement, 1) p.Extensions = make([]*ExtensionElement, 1)
p.Extensions[0] = e p.Extensions[0] = e
} else { } else {
p.Extensions = append(p.Extensions, e) p.Extensions = append(p.Extensions, e)
} }
return nil
} }
// Check checks the Person for incompatibilities with RFC4287. It returns an // Check checks the Person for incompatibilities with RFC4287. It returns an
@ -42,7 +62,7 @@ func (p *Person) Check() error {
} }
if p.Email != "" { if p.Email != "" {
if _, err := mail.ParseAddress(string(p.Email)); err != nil { if _, err := mail.ParseAddress(p.Email); err != nil {
return fmt.Errorf("email element of person %v not correctly formatted", p) return fmt.Errorf("email element of person %v not correctly formatted", p)
} }
} }

View File

@ -1,6 +1,9 @@
package atom package atom
import "fmt" import (
"errors"
"fmt"
)
type PlainText struct { type PlainText struct {
*CommonAttributes *CommonAttributes
@ -11,6 +14,15 @@ type PlainText struct {
// isText checks whether the PlainText is a Text. It returns a bool. // isText checks whether the PlainText is a Text. It returns a bool.
func (p *PlainText) isText() bool { return true } func (p *PlainText) isText() bool { return true }
// newPlainText creates a new PlainText. It returns a *PlainText and an error.
func newPlainText(textType, content string) (*PlainText, error) {
if content == "" {
return nil, errors.New("error creating new plain text: content string empty")
}
return &PlainText{Type: textType, Text: content}, nil
}
// Check checks the PlainText for incompatibilities with RFC4287. It returns an // Check checks the PlainText for incompatibilities with RFC4287. It returns an
// error. // error.
func (p *PlainText) Check() error { func (p *PlainText) Check() error {

View File

@ -23,6 +23,11 @@ type Source struct {
Extensions []*ExtensionElement `xml:",any,omitempty"` Extensions []*ExtensionElement `xml:",any,omitempty"`
} }
// NewSource creates a new Source. It returns a *Source.
func NewSource() *Source {
return new(Source)
}
// Check checks the Source for incompatibilities with RFC4287. It returns an // Check checks the Source for incompatibilities with RFC4287. It returns an
// error. // error.
func (s *Source) Check() error { func (s *Source) Check() error {

12
text.go
View File

@ -14,17 +14,11 @@ type Text interface {
func NewText(textType, content string) (Text, error) { func NewText(textType, content string) (Text, error) {
switch textType { switch textType {
case "text", "": case "text", "":
return &PlainText{Type: textType, Text: content}, nil return newPlainText(textType, content)
case "html": case "html":
return &PlainText{Type: textType, Text: html.UnescapeString(content)}, nil return newPlainText(textType, html.UnescapeString(content))
case "xhtml": case "xhtml":
return &XHTMLText{ return newXHTMLText(textType, content)
Type: textType,
XHTMLDiv: &XHTMLDiv{
XMLNS: "http://www.w3.org/1999/xhtml",
Content: content,
},
}, nil
default: default:
return nil, fmt.Errorf("%v is not a valid text type", textType) return nil, fmt.Errorf("%v is not a valid text type", textType)
} }

View File

@ -2,6 +2,7 @@ package atom
import ( import (
"encoding/xml" "encoding/xml"
"errors"
"fmt" "fmt"
) )
@ -11,12 +12,16 @@ type XHTMLDiv struct {
Content string `xml:",innerxml"` Content string `xml:",innerxml"`
} }
// NewXHTMLDiv creates a new XHTMLDiv. It returns a *XHTMLDiv. // NewXHTMLDiv creates a new XHTMLDiv. It returns a *XHTMLDiv and an error.
func NewXHTMLDiv(content string) *XHTMLDiv { func NewXHTMLDiv(content string) (*XHTMLDiv, error) {
if content == "" {
return nil, errors.New("error creating new xhtml div: content string empty")
}
return &XHTMLDiv{ return &XHTMLDiv{
XMLNS: "http://www.w3.org/1999/xhtml", XMLNS: "http://www.w3.org/1999/xhtml",
Content: content, Content: content,
} }, nil
} }
// Check checks the XHTMLDiv for incompatibilities with RFC4287. It returns an // Check checks the XHTMLDiv for incompatibilities with RFC4287. It returns an

View File

@ -1,6 +1,8 @@
package atom package atom
import "fmt" import (
"fmt"
)
type XHTMLText struct { type XHTMLText struct {
*CommonAttributes *CommonAttributes
@ -11,6 +13,19 @@ type XHTMLText struct {
// isText checks whether the XHTMLText is a Text. It returns a bool. // isText checks whether the XHTMLText is a Text. It returns a bool.
func (x *XHTMLText) isText() bool { return true } func (x *XHTMLText) isText() bool { return true }
// newPlainText creates a new PlainText. It returns a *PlainText and an error.
func newXHTMLText(textType, content string) (*XHTMLText, error) {
xhtmlDiv, err := NewXHTMLDiv(content)
if err != nil {
return nil, fmt.Errorf("error creating new xhtml text: %v", err)
}
return &XHTMLText{
Type: textType,
XHTMLDiv: xhtmlDiv,
}, nil
}
// Check checks the XHTMLText for incompatibilities with RFC4287. It returns an // Check checks the XHTMLText for incompatibilities with RFC4287. It returns an
// error. // error.
func (x *XHTMLText) Check() error { func (x *XHTMLText) Check() error {